Nim wrapper refactoring

- no more copies of original EVMC files
- automated C API wrapper generation using c2nim, sed and gawk
- removed changes to nim_host_create_context() that depended on custom
  changes to the example C++ host, since the test suite is the same for
  both
This commit is contained in:
Ștefan Talpalaru 2020-01-22 02:00:59 +01:00
parent 2aa39b9f61
commit ad51a35021
No known key found for this signature in database
GPG Key ID: CBF7934204F1B6F9
16 changed files with 301 additions and 3228 deletions

View File

@ -1,39 +0,0 @@
version: '{build}'
image: Visual Studio 2015
cache:
- NimBinaries
matrix:
# We always want 32 and 64-bit compilation
fast_finish: false
platform:
- x86
- x64
# when multiple CI builds are queued, the tested commit needs to be in the last X commits cloned with "--depth X"
clone_depth: 10
install:
# use the newest versions documented here: https://www.appveyor.com/docs/windows-images-software/#mingw-msys-cygwin
- IF "%PLATFORM%" == "x86" SET PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH%
- IF "%PLATFORM%" == "x64" SET PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%PATH%
# build nim from our own branch - this to avoid the day-to-day churn and
# regressions of the fast-paced Nim development while maintaining the
# flexibility to apply patches
- curl -O -L -s -S https://raw.githubusercontent.com/status-im/nimbus-build-system/master/scripts/build_nim.sh
- env MAKE="mingw32-make -j2" ARCH_OVERRIDE=%PLATFORM% bash build_nim.sh Nim csources dist/nimble NimBinaries
- SET PATH=%CD%\Nim\bin;%PATH%
build_script:
- cd C:\projects\%APPVEYOR_PROJECT_SLUG%
- nimble install -y
test_script:
- nimble test
deploy: off

6
bindings/nim/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# Nim
nimcache/
# Executables shall be put in an ignored build/ directory
build/

20
bindings/nim/README.md Normal file
View File

@ -0,0 +1,20 @@
# Nim EVMC wrapper
[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
# Introduction
This is a Nim wrapper for EVMC, the [Ethereum Client-VM Connector API](https://github.com/ethereum/evmc).
# Installation
`nimble install evmc`
# Testing
`nimble test`
# License
Licensed and distributed under the Apache License, Version 2.0, ([LICENSE](../../LICENSE) or http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -1,3 +1,8 @@
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed under the Apache License, Version 2.0.
# This file may not be copied, modified, or distributed except according to
# those terms.
mode = ScriptMode.Verbose
packageName = "evmc"
@ -14,6 +19,9 @@ proc test(name: string, lang: string = "cpp") =
if not dirExists "build":
mkDir "build"
--run
--forceBuild
--verbosity:0
--hints:off
switch("out", ("./build/" & name))
setCommand lang, "tests/" & name & ".nim"

127
bindings/nim/evmc/evmc.nim Normal file
View File

@ -0,0 +1,127 @@
type
evmc_bytes32* {.importc: "evmc_bytes32", header: "evmc/evmc.h", bycopy.} = object
bytes* {.importc: "bytes".}: array[32, uint8]
evmc_uint256be* = evmc_bytes32
evmc_address* {.importc: "evmc_address", header: "evmc/evmc.h", bycopy.} = object
bytes* {.importc: "bytes".}: array[20, uint8]
evmc_call_kind* {.size: sizeof(cint).} = enum
EVMC_CALL = 0, EVMC_DELEGATECALL = 1, EVMC_CALLCODE = 2, EVMC_CREATE = 3,
EVMC_CREATE2 = 4
evmc_flags* {.size: sizeof(cint).} = enum
EVMC_STATIC = 1
evmc_message* {.importc: "struct evmc_message", header: "evmc/evmc.h", bycopy.} = object
kind* {.importc: "kind".}: evmc_call_kind
flags* {.importc: "flags".}: uint32
depth* {.importc: "depth".}: int32
gas* {.importc: "gas".}: int64
destination* {.importc: "destination".}: evmc_address
sender* {.importc: "sender".}: evmc_address
input_data* {.importc: "input_data".}: ptr uint8
input_size* {.importc: "input_size".}: uint
value* {.importc: "value".}: evmc_uint256be
create2_salt* {.importc: "create2_salt".}: evmc_bytes32
evmc_tx_context* {.importc: "struct evmc_tx_context", header: "evmc/evmc.h", bycopy.} = object
tx_gas_price* {.importc: "tx_gas_price".}: evmc_uint256be
tx_origin* {.importc: "tx_origin".}: evmc_address
block_coinbase* {.importc: "block_coinbase".}: evmc_address
block_number* {.importc: "block_number".}: int64
block_timestamp* {.importc: "block_timestamp".}: int64
block_gas_limit* {.importc: "block_gas_limit".}: int64
block_difficulty* {.importc: "block_difficulty".}: evmc_uint256be
chain_id* {.importc: "chain_id".}: evmc_uint256be
evmc_host_context* {.importc: "struct evmc_host_context", header: "evmc/evmc.h", bycopy.} = object
evmc_get_tx_context_fn* = proc (context: ptr evmc_host_context): evmc_tx_context {.
cdecl.}
evmc_get_block_hash_fn* = proc (context: ptr evmc_host_context; number: int64): evmc_bytes32 {.
cdecl.}
evmc_status_code* {.size: sizeof(cint).} = enum
EVMC_OUT_OF_MEMORY = -3, EVMC_REJECTED = -2, EVMC_INTERNAL_ERROR = -1,
EVMC_SUCCESS = 0, EVMC_FAILURE = 1, EVMC_REVERT = 2, EVMC_OUT_OF_GAS = 3,
EVMC_INVALID_INSTRUCTION = 4, EVMC_UNDEFINED_INSTRUCTION = 5,
EVMC_STACK_OVERFLOW = 6, EVMC_STACK_UNDERFLOW = 7, EVMC_BAD_JUMP_DESTINATION = 8,
EVMC_INVALID_MEMORY_ACCESS = 9, EVMC_CALL_DEPTH_EXCEEDED = 10,
EVMC_STATIC_MODE_VIOLATION = 11, EVMC_PRECOMPILE_FAILURE = 12,
EVMC_CONTRACT_VALIDATION_FAILURE = 13, EVMC_ARGUMENT_OUT_OF_RANGE = 14,
EVMC_WASM_UNREACHABLE_INSTRUCTION = 15, EVMC_WASM_TRAP = 16
evmc_release_result_fn* = proc (result: ptr evmc_result) {.cdecl.}
evmc_result* {.importc: "struct evmc_result", header: "evmc/evmc.h", bycopy.} = object
status_code* {.importc: "status_code".}: evmc_status_code
gas_left* {.importc: "gas_left".}: int64
output_data* {.importc: "output_data".}: ptr uint8
output_size* {.importc: "output_size".}: uint
release* {.importc: "release".}: evmc_release_result_fn
create_address* {.importc: "create_address".}: evmc_address
padding* {.importc: "padding".}: array[4, uint8]
evmc_account_exists_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address): bool {.cdecl.}
evmc_get_storage_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address; key: ptr evmc_bytes32): evmc_bytes32 {.
cdecl.}
evmc_storage_status* {.size: sizeof(cint).} = enum
EVMC_STORAGE_UNCHANGED = 0, EVMC_STORAGE_MODIFIED = 1,
EVMC_STORAGE_MODIFIED_AGAIN = 2, EVMC_STORAGE_ADDED = 3, EVMC_STORAGE_DELETED = 4
evmc_set_storage_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address; key: ptr evmc_bytes32;
value: ptr evmc_bytes32): evmc_storage_status {.cdecl.}
evmc_get_balance_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address): evmc_uint256be {.cdecl.}
evmc_get_code_size_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address): uint {.cdecl.}
evmc_get_code_hash_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address): evmc_bytes32 {.cdecl.}
evmc_copy_code_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address; code_offset: uint;
buffer_data: ptr uint8; buffer_size: uint): uint {.cdecl.}
evmc_selfdestruct_fn* = proc (context: ptr evmc_host_context;
address: ptr evmc_address;
beneficiary: ptr evmc_address) {.cdecl.}
evmc_emit_log_fn* = proc (context: ptr evmc_host_context; address: ptr evmc_address;
data: ptr uint8; data_size: uint;
topics: ptr evmc_bytes32; topics_count: uint) {.cdecl.}
evmc_call_fn* = proc (context: ptr evmc_host_context; msg: ptr evmc_message): evmc_result {.
cdecl.}
evmc_host_interface* {.importc: "struct evmc_host_interface", header: "evmc/evmc.h",
bycopy.} = object
account_exists* {.importc: "account_exists".}: evmc_account_exists_fn
get_storage* {.importc: "get_storage".}: evmc_get_storage_fn
set_storage* {.importc: "set_storage".}: evmc_set_storage_fn
get_balance* {.importc: "get_balance".}: evmc_get_balance_fn
get_code_size* {.importc: "get_code_size".}: evmc_get_code_size_fn
get_code_hash* {.importc: "get_code_hash".}: evmc_get_code_hash_fn
copy_code* {.importc: "copy_code".}: evmc_copy_code_fn
selfdestruct* {.importc: "selfdestruct".}: evmc_selfdestruct_fn
call* {.importc: "call".}: evmc_call_fn
get_tx_context* {.importc: "get_tx_context".}: evmc_get_tx_context_fn
get_block_hash* {.importc: "get_block_hash".}: evmc_get_block_hash_fn
emit_log* {.importc: "emit_log".}: evmc_emit_log_fn
evmc_destroy_fn* = proc (vm: ptr evmc_vm) {.cdecl.}
evmc_set_option_result* {.size: sizeof(cint).} = enum
EVMC_SET_OPTION_SUCCESS = 0, EVMC_SET_OPTION_INVALID_NAME = 1,
EVMC_SET_OPTION_INVALID_VALUE = 2
evmc_set_option_fn* = proc (vm: ptr evmc_vm; name: cstring; value: cstring): evmc_set_option_result {.
cdecl.}
evmc_revision* {.size: sizeof(cint).} = enum
EVMC_FRONTIER = 0, EVMC_HOMESTEAD = 1, EVMC_TANGERINE_WHISTLE = 2,
EVMC_SPURIOUS_DRAGON = 3, EVMC_BYZANTIUM = 4, EVMC_CONSTANTINOPLE = 5,
EVMC_PETERSBURG = 6, EVMC_ISTANBUL = 7, EVMC_BERLIN = 8
evmc_execute_fn* = proc (vm: ptr evmc_vm; host: ptr evmc_host_interface;
context: ptr evmc_host_context; rev: evmc_revision;
msg: ptr evmc_message; code: ptr uint8; code_size: uint): evmc_result {.
cdecl.}
evmc_capabilities* {.size: sizeof(cint).} = enum
EVMC_CAPABILITY_EVM1 = (1 shl 0), EVMC_CAPABILITY_EWASM = (1 shl 1),
EVMC_CAPABILITY_PRECOMPILES = (1 shl 2)
evmc_capabilities_flagset* = uint32
evmc_get_capabilities_fn* = proc (vm: ptr evmc_vm): evmc_capabilities_flagset {.cdecl.}
evmc_vm* {.importc: "struct evmc_vm", header: "evmc/evmc.h", bycopy.} = object
abi_version* {.importc: "abi_version".}: cint
name* {.importc: "name".}: cstring
version* {.importc: "version".}: cstring
destroy* {.importc: "destroy".}: evmc_destroy_fn
execute* {.importc: "execute".}: evmc_execute_fn
get_capabilities* {.importc: "get_capabilities".}: evmc_get_capabilities_fn
set_option* {.importc: "set_option".}: evmc_set_option_fn
const
EVMC_ABI_VERSION* = 7
EVMC_MAX_REVISION* = EVMC_BERLIN

View File

@ -1,19 +1,24 @@
import evmc
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed under the Apache License, Version 2.0.
# This file may not be copied, modified, or distributed except according to
# those terms.
import ./evmc
type
HostContext* = object
host: ptr evmc_host_interface
context: evmc_host_context
context: ptr evmc_host_context
EvmcVM* = object
vm: ptr evmc_vm
hc: HostContext
proc init*(x: var HostContext, host: ptr evmc_host_interface, context: evmc_host_context) =
proc init*(x: var HostContext, host: ptr evmc_host_interface, context: ptr evmc_host_context) =
x.host = host
x.context = context
proc init*(x: typedesc[HostContext], host: ptr evmc_host_interface, context: evmc_host_context): HostContext =
proc init*(x: typedesc[HostContext], host: ptr evmc_host_interface, context: ptr evmc_host_context): HostContext =
result.init(host, context)
proc getTxContext*(ctx: HostContext): evmc_tx_context =
@ -79,7 +84,7 @@ proc name*(vm: EvmcVM): string =
proc version*(vm: EvmcVM): string =
$vm.vm.version
proc getCapabilities*(vm: EvmcVM): evmc_capabilities =
proc getCapabilities*(vm: EvmcVM): evmc_capabilities_flagset =
vm.vm.get_capabilities(vm.vm)
proc setOption*(vm: EvmcVM, name, value: string): evmc_set_option_result =
@ -88,7 +93,7 @@ proc setOption*(vm: EvmcVM, name, value: string): evmc_set_option_result =
result = EVMC_SET_OPTION_INVALID_NAME
proc execute*(vm: EvmcVM, rev: evmc_revision, msg: evmc_message, code: openArray[byte]): evmc_result =
proc execute*(vm: EvmcVM, rev: evmc_revision, msg: ptr evmc_message, code: openArray[byte]): evmc_result =
vm.vm.execute(vm.vm, vm.hc.host, vm.hc.context, rev, msg, code[0].unsafeAddr, code.len.uint)
proc destroy*(vm: EvmcVM) =

View File

@ -0,0 +1,57 @@
#!/bin/bash
# Copyright (c) 2020 Status Research & Development GmbH
# Licensed under the Apache License, Version 2.0.
# This file may not be copied, modified, or distributed except according to
# those terms.
set -e
cd "$(dirname "${BASH_SOURCE[0]}")"
sed -e '/^#include/d' \
-e '/^struct evmc_result;$/d' \
-e '/^struct evmc_vm;$/d' \
../../../include/evmc/evmc.h | cpp | sed '/^#/d' > tmp.h
c2nim --header:'"evmc/evmc.h"' --cdecl tmp.h
rm tmp.h
sed \
-e 's/uint8_t/uint8/g' \
-e 's/uint32_t/uint32/g' \
-e 's/int32_t/int32/g' \
-e 's/int64_t/int64/g' \
-e 's/csize/uint/g' \
-e 's/EVMC_MAX_REVISION/EVMC_MAX_REVISION*/' \
-e 's/"evmc_message"/"struct evmc_message"/' \
-e 's/"evmc_tx_context"/"struct evmc_tx_context"/' \
-e 's/"evmc_host_context"/"struct evmc_host_context"/' \
-e 's/"evmc_result"/"struct evmc_result"/' \
-e 's/"evmc_host_interface"/"struct evmc_host_interface"/' \
-e 's/"evmc_vm"/"struct evmc_vm"/' \
tmp.nim | gawk '
/^const$/ {
target = "const"
}
/^type$/ {
target = "type"
}
/^ / {
if(target == "const")
consts[const_index++] = $0
else if(target == "type")
types[type_index++] = $0
}
END {
print "type"
for(i in types)
print types[i]
print "\nconst"
for(i in consts)
print consts[i]
}
' > evmc.nim
rm tmp.nim

View File

@ -1,6 +1,11 @@
import tables, hashes, strutils
import ../../evmc/evmc
import stew/byteutils
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed under the Apache License, Version 2.0.
# This file may not be copied, modified, or distributed except according to
# those terms.
import tables, hashes, strutils,
stew/byteutils,
../evmc/evmc
type
Account = ref object
@ -12,6 +17,24 @@ type
tx_context: evmc_tx_context
accounts: Table[evmc_address, Account]
const
EVMC_HOST_NAME = "example_vm"
EVMC_VM_VERSION = "0.0.0"
# {.nodecl.} only works in the global scope
var globalVM {.importc, nodecl.}: evmc_vm
# Nim doesn't support initialising a struct with const fields, so we do it in C
{.emit: [evmc_vm, " ", globalVM, " = {.abi_version = ", EVMC_ABI_VERSION, ", .name = \"", EVMC_HOST_NAME, "\", .version = \"", EVMC_VM_VERSION, "\"};"].}
proc incl*(a: var evmc_capabilities_flagset, b: evmc_capabilities) {.inline.} =
a = evmc_capabilities_flagset(a.uint32 or b.uint32)
proc excl*(a: var evmc_capabilities_flagset, b: evmc_capabilities) {.inline.} =
a = evmc_capabilities_flagset(a.uint32 and (not b.uint32))
proc contains*(a: evmc_capabilities_flagset, b: evmc_capabilities): bool {.inline.} =
(a.uint32 and b.uint32) != 0
proc hash*(x: evmc_bytes32): Hash =
result = hash(x.bytes)
@ -24,9 +47,12 @@ proc codeHash(acc: Account): evmc_bytes32 =
let idx = v.int mod sizeof(result.bytes)
result.bytes[idx] = result.bytes[idx] xor v
proc evmcReleaseResultImpl(result: var evmc_result) {.cdecl.} =
proc evmcReleaseResultImpl(result: ptr evmc_result) {.cdecl.} =
discard
converter toEVMCHostContext*(ctx: HostContext): ptr evmc_host_context =
cast[ptr evmc_host_context](ctx)
proc evmcGetTxContextImpl(ctx: HostContext): evmc_tx_context {.cdecl.} =
ctx.tx_context
@ -36,7 +62,7 @@ proc evmcGetBlockHashImpl(ctx: HostContext, number: int64): evmc_bytes32 {.cdecl
if number < current_block_number and number >= current_block_number - 256:
result.bytes = hash
proc evmcAccountExistsImpl(ctx: HostContext, address: var evmc_address): c99bool {.cdecl.} =
proc evmcAccountExistsImpl(ctx: HostContext, address: var evmc_address): bool {.cdecl.} =
address in ctx.accounts
proc evmcGetStorageImpl(ctx: HostContext, address: var evmc_address, key: var evmc_bytes32): evmc_bytes32 {.cdecl.} =
@ -112,7 +138,7 @@ proc evmcSetOptionImpl(vm: ptr evmc_vm, name, value: cstring): evmc_set_option_r
proc evmcExecuteImpl(vm: ptr evmc_vm, host: ptr evmc_host_interface,
ctx: HostContext, rev: evmc_revision,
msg: evmc_message, code: ptr byte, code_size: uint): evmc_result {.cdecl.} =
msg: ptr evmc_message, code: ptr byte, code_size: uint): evmc_result {.cdecl.} =
var the_code = "\x43\x60\x00\x55\x43\x60\x00\x52\x59\x60\x00\xf3"
if (code_size.int == the_code.len) and equalMem(code, the_code[0].addr, code_size):
@ -135,7 +161,7 @@ proc evmcExecuteImpl(vm: ptr evmc_vm, host: ptr evmc_host_interface,
result.status_code = EVMC_FAILURE
result.gas_left = 0
proc evmcGetCapabilitiesImpl(vm: ptr evmc_vm): evmc_capabilities {.cdecl.} =
proc evmcGetCapabilitiesImpl(vm: ptr evmc_vm): evmc_capabilities_flagset {.cdecl.} =
result.incl(EVMC_CAPABILITY_EVM1)
result.incl(EVMC_CAPABILITY_EWASM)
@ -143,67 +169,38 @@ proc evmcDestroyImpl(vm: ptr evmc_vm) {.cdecl.} =
dealloc(vm)
proc init_host_interface(): evmc_host_interface =
# workaround for nim cpp codegen bug
{.emit: [result.account_exists, " = (", evmc_account_exists_fn, ")", evmcAccountExistsImpl, ";" ].}
{.emit: [result.get_storage, " = (", evmc_get_storage_fn, ")", evmcGetStorageImpl, ";" ].}
{.emit: [result.set_storage, " = (", evmc_set_storage_fn, ")", evmcSetStorageImpl, ";" ].}
{.emit: [result.get_balance, " = (", evmc_get_balance_fn, ")", evmcGetBalanceImpl, ";" ].}
{.emit: [result.get_code_size, " = (", evmc_get_code_size_fn, ")", evmcGetCodeSizeImpl, ";" ].}
{.emit: [result.get_code_hash, " = (", evmc_get_code_hash_fn, ")", evmcGetCodeHashImpl, ";" ].}
{.emit: [result.copy_code, " = (", evmc_copy_code_fn, ")", evmcCopyCodeImpl, ";" ].}
{.emit: [result.selfdestruct, " = (", evmc_selfdestruct_fn, ")", evmcSelfdestructImpl, ";" ].}
{.emit: [result.call, " = (", evmc_call_fn, ")", evmcCallImpl, ";" ].}
{.emit: [result.get_tx_context, " = (", evmc_get_tx_context_fn, ")", evmcGetTxContextImpl, ";" ].}
{.emit: [result.get_block_hash, " = (", evmc_get_block_hash_fn, ")", evmcGetBlockHashImpl, ";" ].}
{.emit: [result.emit_log, " = (", evmc_emit_log_fn, ")", evmcEmitLogImpl, ";" ].}
result.account_exists = cast[evmc_account_exists_fn](evmcAccountExistsImpl)
result.get_storage = cast[evmc_get_storage_fn](evmcGetStorageImpl)
result.set_storage = cast[evmc_set_storage_fn](evmcSetStorageImpl)
result.get_balance = cast[evmc_get_balance_fn](evmcGetBalanceImpl)
result.get_code_size = cast[evmc_get_code_size_fn](evmcGetCodeSizeImpl)
result.get_code_hash = cast[evmc_get_code_hash_fn](evmcGetCodeHashImpl)
result.copy_code = cast[evmc_copy_code_fn](evmcCopyCodeImpl)
result.selfdestruct = cast[evmc_selfdestruct_fn](evmcSelfdestructImpl)
result.call = cast[evmc_call_fn](evmcCallImpl)
result.get_tx_context = cast[evmc_get_tx_context_fn](evmcGetTxContextImpl)
result.get_block_hash = cast[evmc_get_block_hash_fn](evmcGetBlockHashImpl)
result.emit_log = cast[evmc_emit_log_fn](evmcEmitLogImpl)
#result.account_exists = cast[evmc_account_exists_fn](evmcAccountExistsImpl)
#result.get_storage = cast[evmc_get_storage_fn](evmcGetStorageImpl)
#result.set_storage = cast[evmc_set_storage_fn](evmcSetStorageImpl)
#result.get_balance = cast[evmc_get_balance_fn](evmcGetBalanceImpl)
#result.get_code_size = cast[evmc_get_code_size_fn](evmcGetCodeSizeImpl)
#result.get_code_hash = cast[evmc_get_code_hash_fn](evmcGetCodeHashImpl)
#result.copy_code = cast[evmc_copy_code_fn](evmcCopyCodeImpl)
#result.selfdestruct = cast[evmc_selfdestruct_fn](evmcSelfdestructImpl)
#result.call = cast[evmc_call_fn](evmcCallImpl)
#result.get_tx_context = cast[evmc_get_tx_context_fn](evmcGetTxContextImpl)
#result.get_block_hash = cast[evmc_get_block_hash_fn](evmcGetBlockHashImpl)
#result.emit_log = cast[evmc_emit_log_fn](evmcEmitLogImpl)
const
EVMC_HOST_NAME = "example_vm"
EVMC_VM_VERSION = "0.0.0"
proc init(vm: var evmc_vm) {.exportc, cdecl.} =
vm.abi_version = EVMC_ABI_VERSION
vm.name = EVMC_HOST_NAME
vm.version = EVMC_VM_VERSION
proc init(vm: ptr evmc_vm) {.exportc, cdecl.} =
vm.destroy = evmcDestroyImpl
{.emit: [vm.execute, " = (", evmc_execute_fn, ")", evmcExecuteImpl, ";" ].}
#vm.execute = cast[evmc_execute_fn](evmcExecuteImpl)
vm.execute = cast[evmc_execute_fn](evmcExecuteImpl)
vm.get_capabilities = evmcGetCapabilitiesImpl
vm.set_option = evmcSetOptionImpl
let gHost = init_host_interface()
proc nim_host_get_interface(): ptr evmc_host_interface {.exportc, cdecl.} =
proc nim_host_get_interface*(): ptr evmc_host_interface {.exportc, cdecl.} =
result = gHost.unsafeAddr
proc nim_host_create_context(tx_context: evmc_tx_context): HostContext {.exportc, cdecl.} =
const address = evmc_address(bytes: hexToByteArray[20]("0x0001020000000000000000000000000000000000"))
var acc = Account(
balance: evmc_uint256be(bytes: hexToByteArray[32]("0x0100000000000000000000000000000000000000000000000000000000000000")),
code: @[10.byte, 11, 12, 13, 14, 15]
)
proc nim_host_create_context*(tx_context: evmc_tx_context): HostContext {.exportc, cdecl.} =
result = HostContext(tx_context: tx_context)
result.accounts[address] = acc
GC_ref(result)
proc nim_host_destroy_context(ctx: HostContext) {.exportc, cdecl.} =
proc nim_host_destroy_context*(ctx: HostContext) {.exportc, cdecl.} =
GC_unref(ctx)
proc nim_create_example_vm(): ptr evmc_vm {.exportc, cdecl.} =
result = create(evmc_vm)
init(result[])
proc nim_create_example_vm*(): ptr evmc_vm {.exportc, cdecl.} =
result = cast[ptr evmc_vm](new(evmc_vm))
copyMem(result, globalVM.addr, sizeof(globalVM))
init(result)

View File

@ -1,24 +1,24 @@
import ../evmc/[evmc, evmc_nim], unittest
import evmc_nim/nim_host
import stew/byteutils
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed under the Apache License, Version 2.0.
# This file may not be copied, modified, or distributed except according to
# those terms.
{.compile: "evmc_c/example_host.cpp".}
{.compile: "evmc_c/example_vm.cpp".}
{.passL: "-lstdc++"}
import os, unittest,
stew/byteutils,
../evmc/evmc, ../evmc/evmc_nim, ./nim_host
{.compile: "../../../examples/example_host.cpp".}
{.compile: "../../../examples/example_vm/example_vm.c".}
{.passC: "-I" & currentSourcePath.parentDir().parentDir().parentDir().parentDir() / "include".}
when defined(posix):
{.passC: "-std=c++11".}
proc example_host_get_interface(): ptr evmc_host_interface {.importc, cdecl.}
proc example_host_create_context(tx_context: var evmc_tx_context): evmc_host_context {.importc, cdecl.}
proc example_host_destroy_context(context: evmc_host_context) {.importc, cdecl.}
proc example_host_create_context(tx_context: evmc_tx_context): ptr evmc_host_context {.importc, cdecl.}
proc example_host_destroy_context(context: ptr evmc_host_context) {.importc, cdecl.}
proc evmc_create_example_vm(): ptr evmc_vm {.importc, cdecl.}
proc nim_host_get_interface(): ptr evmc_host_interface {.importc, cdecl.}
proc nim_host_create_context(tx_context: var evmc_tx_context): evmc_host_context {.importc, cdecl.}
proc nim_host_destroy_context(context: evmc_host_context) {.importc, cdecl.}
proc nim_create_example_vm(): ptr evmc_vm {.importc, cdecl.}
template runTest(testName: string, create_vm, get_host_interface, create_host_context, destroy_host_context: untyped) =
var vm = create_vm()
var host = get_host_interface()
@ -83,22 +83,6 @@ template runTest(testName: string, create_vm, get_host_interface, create_host_co
test "accountExists":
check hc.accountExists(address) == true
test "getBalance":
let bal = hc.getBalance(address)
check bal == balance
test "getCodeSize":
check hc.getCodeSize(address) == 6
test "getCodeHash":
let hash = hc.getCodeHash(address)
check hash == ahash
test "copyCode":
let acode = @[11.byte, 12, 13, 14, 15]
let bcode = hc.copyCode(address, 1)
check acode == bcode
test "selfdestruct":
hc.selfdestruct(address, address)
@ -137,11 +121,11 @@ template runTest(testName: string, create_vm, get_host_interface, create_host_co
test "execute and destroy":
var bn = $tx_context.block_number
var res = nvm.execute(EVMC_HOMESTEAD, msg, code)
var res = nvm.execute(EVMC_HOMESTEAD, msg.addr, code)
check res.status_code == EVMC_SUCCESS
check res.gas_left == 100000
check equalMem(bn[0].addr, res.output_data, bn.len)
res.release(res)
res.release(res.addr)
var empty_key: evmc_bytes32
let val = hc.getStorage(address, empty_key)
@ -151,14 +135,14 @@ template runTest(testName: string, create_vm, get_host_interface, create_host_co
destroy_host_context(ctx)
proc main() =
runTest("EVMC Nim to C API",
runTest("Nim EVMC wrapper: C++ host, C VM",
evmc_create_example_vm,
example_host_get_interface,
example_host_create_context,
example_host_destroy_context
)
runTest("EVMC Nim to Nim API",
runTest("Nim EVMC wrapper: Nim host, Nim VM",
nim_create_example_vm,
nim_host_get_interface,
nim_host_create_context,

View File

@ -1,695 +0,0 @@
# Nimbus
# Copyright (c) 2019 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
# The EVMC ABI version number of the interface declared in this file.
#
# The EVMC ABI version always equals the major version number of the EVMC project.
# The Host SHOULD check if the ABI versions match when dynamically loading VMs.
const
EVMC_ABI_VERSION* = 7.cint
type
# SPECIAL NOTE
# EVMC adopt C99 standard, and one of it's interface using
# C99 'bool' as return type. Nowhere in the C documentation
# mentions about it's size.
# chfast@ethereum/evmc told me about this "https://godbolt.org/z/marD9R"
# lucky for us, in practice C compilers agree to use one byte long for C99 bool.
# Nim sizeof(bool) also == 1.
# but still we need to be careful about this though.
c99bool* = bool
# The fixed size array of 32 bytes.
# 32 bytes of data capable of storing e.g. 256-bit hashes.
evmc_bytes32* = object
bytes*: array[32, byte]
# The alias for evmc_bytes32 to represent a big-endian 256-bit integer.
evmc_uint256be* = evmc_bytes32
# Big-endian 160-bit hash suitable for keeping an Ethereum address.
evmc_address* = object
bytes*: array[20, byte]
# The kind of call-like instruction.
evmc_call_kind* {.size: sizeof(cint).} = enum
EVMC_CALL = 0 # Request CALL.
EVMC_DELEGATECALL = 1 # Request DELEGATECALL. Valid since Homestead.
# The value param ignored.
EVMC_CALLCODE = 2 # Request CALLCODE.
EVMC_CREATE = 3 # Request CREATE.
EVMC_CREATE2 = 4 # Request CREATE2. Valid since Constantinople.
# The flags for ::evmc_message.
evmc_flags* {.size: sizeof(cint).} = enum
EVMC_STATIC = 1 # Static call mode.
# The message describing an EVM call,
# including a zero-depth calls from a transaction origin.
evmc_message* = object
# The kind of the call. For zero-depth calls ::EVMC_CALL SHOULD be used.
kind*: evmc_call_kind
# Additional flags modifying the call execution behavior.
# In the current version the only valid values are ::EVMC_STATIC or 0.
flags*: uint32
# The call depth.
depth*: int32
# The amount of gas for message execution.
gas*: int64
# The destination of the message.
destination*: evmc_address
# The sender of the message.
sender*: evmc_address
# The message input data.
# This MAY be NULL.
input_data*: ptr byte
# The size of the message input data.
# If input_data is NULL this MUST be 0.
# actually it's a size_t
input_size*: uint
# The amount of Ether transferred with the message.
value*: evmc_uint256be
# The optional value used in new contract address construction.
# Ignored unless kind is EVMC_CREATE2.
create2_salt*: evmc_bytes32
# The transaction and block data for execution.
evmc_tx_context* = object
tx_gas_price* : evmc_uint256be # The transaction gas price.
tx_origin* : evmc_address # The transaction origin account.
block_coinbase* : evmc_address # 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.
# @struct evmc_host_context
# The opaque data type representing the Host execution context.
# @see evmc_execute_fn().
evmc_host_context* = distinct pointer
# Get transaction context callback function.
#
# This callback function is used by an EVM to retrieve the transaction and
# block context.
#
# @param context The pointer to the Host execution context.
# @return The transaction context.
evmc_get_tx_context_fn* = proc(context: evmc_host_context): evmc_tx_context {.cdecl.}
# Get block hash callback function.
#
# This callback function is used by a VM to query the hash of the header of the given block.
# If the information about the requested block is not available, then this is signalled by
# returning null bytes.
#
# @param context The pointer to the Host execution context.
# @param number The block number.
# @return The block hash or null bytes
# if the information about the block is not available.
evmc_get_block_hash_fn* = proc(context: evmc_host_context, number: int64): evmc_bytes32 {.cdecl.}
# The execution status code.
#
# Successful execution is represented by ::EVMC_SUCCESS having value 0.
#
# Positive values represent failures defined by VM specifications with generic
# ::EVMC_FAILURE code of value 1.
#
# Status codes with negative values represent VM internal errors
# not provided by EVM specifications. These errors MUST not be passed back
# to the caller. They MAY be handled by the Client in predefined manner
# (see e.g. ::EVMC_REJECTED), otherwise internal errors are not recoverable.
# The generic representant of errors is ::EVMC_INTERNAL_ERROR but
# an EVM implementation MAY return negative status codes that are not defined
# in the EVMC documentation.
#
# @note
# In case new status codes are needed, please create an issue or pull request
# in the EVMC repository (https://github.com/ethereum/evmc).
evmc_status_code* = distinct cint
# EVMC_SUCCESS
# EVMC_FAILURE
# EVMC_REVERT
# EVMC_OUT_OF_GAS
# EVMC_INVALID_INSTRUCTION
# EVMC_UNDEFINED_INSTRUCTION
# EVMC_STACK_OVERFLOW
# EVMC_STACK_UNDERFLOW
# EVMC_BAD_JUMP_DESTINATION
# EVMC_INVALID_MEMORY_ACCESS
# EVMC_CALL_DEPTH_EXCEEDED
# EVMC_STATIC_MODE_VIOLATION
# EVMC_PRECOMPILE_FAILURE
# EVMC_CONTRACT_VALIDATION_FAILURE
# EVMC_ARGUMENT_OUT_OF_RANGE
# EVMC_WASM_UNREACHABLE_INSTRUCTION
# EVMC_WASM_TRAP
# EVMC_INTERNAL_ERROR
# EVMC_REJECTED
# EVMC_OUT_OF_MEMORY
# Releases resources assigned to an execution result.
#
# This function releases memory (and other resources, if any) assigned to the
# specified execution result making the result object invalid.
#
# @param result The execution result which resources are to be released. The
# result itself it not modified by this function, but becomes
# invalid and user MUST discard it as well.
# This MUST NOT be NULL.
#
# @note
# The result is passed by pointer to avoid (shallow) copy of the ::evmc_result
# struct. Think of this as the best possible C language approximation to
# passing objects by reference.
evmc_release_result_fn* = proc(result: var evmc_result) {.cdecl.}
# The EVM code execution result.
evmc_result* = object
# The execution status code.
status_code*: evmc_status_code
#The amount of gas left after the execution.
# If evmc_result::code is not ::EVMC_SUCCESS nor ::EVMC_REVERT
# the value MUST be 0.
gas_left*: int64
# The reference to output data.
#
# The output contains data coming from RETURN opcode (iff evmc_result::code
# field is ::EVMC_SUCCESS) or from REVERT opcode.
#
# The memory containing the output data is owned by EVM and has to be
# freed with evmc_result::release().
#
# This MAY be NULL.
output_data*: ptr byte
# The size of the output data.
# If output_data is NULL this MUST be 0.
output_size*: uint
# The method releasing all resources associated with the result object.
#
# This method (function pointer) is optional (MAY be NULL) and MAY be set
# by the VM implementation. If set it MUST be called by the user once to
# release memory and other resources associated with the result object.
# Once the resources are released the result object MUST NOT be used again.
#
# The suggested code pattern for releasing execution results:
# @code
# struct evmc_result result = ...;
# if (result.release)
# result.release(&result);
# @endcode
#
# @note
# It works similarly to C++ virtual destructor. Attaching the release
# function to the result itself allows VM composition.
release*: evmc_release_result_fn
# The address of the contract created by create instructions.
#
# This field has valid value only if:
# - it is a result of the Host method evmc_host_interface::call
# - and the result describes successful contract creation
# (evmc_result::status_code is ::EVMC_SUCCESS).
# In all other cases the address MUST be null bytes.
#
create_address*: evmc_address
# Reserved data that MAY be used by a evmc_result object creator.
#
# This reserved 4 bytes together with 20 bytes from create_address form
# 24 bytes of memory called "optional data" within evmc_result struct
# to be optionally used by the evmc_result object creator.
#
# @see evmc_result_optional_data, evmc_get_optional_data().
#
# Also extends the size of the evmc_result to 64 bytes (full cache line).
padding*: array[4, byte]
# Check account existence callback function.
#
# This callback function is used by the VM to check if
# there exists an account at given address.
# @param context The pointer to the Host execution context.
# @param address The address of the account the query is about.
# @return true if exists, false otherwise.
evmc_account_exists_fn* = proc(context: evmc_host_context, address: ptr evmc_address): c99bool {.cdecl.}
# Get storage callback function.
#
# This callback function is used by a VM to query the given account storage entry.
#
# @param context The Host execution context.
# @param address The address of the account.
# @param key The index of the account's storage entry.
# @return The storage value at the given storage key or null bytes
# if the account does not exist.
evmc_get_storage_fn* = proc(context: evmc_host_context, address: ptr evmc_address, key: ptr evmc_bytes32): evmc_bytes32 {.cdecl.}
# The effect of an attempt to modify a contract storage item.
#
# For the purpose of explaining the meaning of each element, the following
# notation is used:
# - 0 is zero value,
# - X != 0 (X is any value other than 0),
# - Y != X, Y != 0 (Y is any value other than X and 0),
# - Z != Y (Z is any value other than Y),
# - the "->" means the change from one value to another.
evmc_storage_status* {.size: sizeof(cint).} = enum
# The value of a storage item has been left unchanged: 0 -> 0 and X -> X.
EVMC_STORAGE_UNCHANGED = 0
# The value of a storage item has been modified: X -> Y.
EVMC_STORAGE_MODIFIED = 1
# A storage item has been modified after being modified before: X -> Y -> Z.
EVMC_STORAGE_MODIFIED_AGAIN = 2
# A new storage item has been added: 0 -> X.
EVMC_STORAGE_ADDED = 3
# A storage item has been deleted: X -> 0.
EVMC_STORAGE_DELETED = 4
# Set storage callback function.
#
# This callback function is used by a VM to update the given account storage entry.
# The VM MUST make sure that the account exists. This requirement is only a formality because
# VM implementations only modify storage of the account of the current execution context
# (i.e. referenced by evmc_message::destination).
#
# @param context The pointer to the Host execution context.
# @param address The address of the account.
# @param key The index of the storage entry.
# @param value The value to be stored.
# @return The effect on the storage item.
evmc_set_storage_fn* = proc(context: evmc_host_context, address: ptr evmc_address,
key, value: ptr evmc_bytes32): evmc_storage_status {.cdecl.}
# Get balance callback function.
#
# This callback function is used by a VM to query the balance of the given account.
#
# @param context The pointer to the Host execution context.
# @param address The address of the account.
# @return The balance of the given account or 0 if the account does not exist.
evmc_get_balance_fn* = proc(context: evmc_host_context, address: ptr evmc_address): evmc_uint256be {.cdecl.}
# Get code size callback function.
#
# This callback function is used by a VM to get the size of the code stored
# in the account at the given address.
#
# @param context The pointer to the Host execution context.
# @param address The address of the account.
# @return The size of the code in the account or 0 if the account does not exist.
evmc_get_code_size_fn* = proc(context: evmc_host_context, address: ptr evmc_address): uint {.cdecl.}
# Get code hash callback function.
#
# This callback function is used by a VM to get the keccak256 hash of the code stored
# in the account at the given address. For existing accounts not having a code, this
# function returns keccak256 hash of empty data.
#
# @param context The pointer to the Host execution context.
# @param address The address of the account.
# @return The hash of the code in the account or null bytes if the account does not exist.
evmc_get_code_hash_fn* = proc(context: evmc_host_context, address: ptr evmc_address): evmc_bytes32 {.cdecl.}
# Copy code callback function.
#
# This callback function is used by an EVM to request a copy of the code
# of the given account to the memory buffer provided by the EVM.
# The Client MUST copy the requested code, starting with the given offset,
# to the provided memory buffer up to the size of the buffer or the size of
# the code, whichever is smaller.
#
# @param context The pointer to the Host execution context. See ::evmc_host_context.
# @param address The address of the account.
# @param code_offset The offset of the code to copy.
# @param buffer_data The pointer to the memory buffer allocated by the EVM
# to store a copy of the requested code.
# @param buffer_size The size of the memory buffer.
# @return The number of bytes copied to the buffer by the Client.
evmc_copy_code_fn* = proc(context: evmc_host_context, address: ptr evmc_address,
code_offset: uint, buffer_data: ptr byte,
buffer_size: uint): uint {.cdecl.}
# Selfdestruct callback function.
#
# This callback function is used by an EVM to SELFDESTRUCT given contract.
# The execution of the contract will not be stopped, that is up to the EVM.
#
# @param context The pointer to the Host execution context. See ::evmc_host_context.
# @param address The address of the contract to be selfdestructed.
# @param beneficiary The address where the remaining ETH is going to be transferred.
evmc_selfdestruct_fn* = proc(context: evmc_host_context, address, beneficiary: ptr evmc_address) {.cdecl.}
# Log callback function.
#
# This callback function is used by an EVM to inform about a LOG that happened
# during an EVM bytecode execution.
#
# @param context The pointer to the Host execution context. See ::evmc_host_context.
# @param address The address of the contract that generated the log.
# @param data The pointer to unindexed data attached to the log.
# @param data_size The length of the data.
# @param topics The pointer to the array of topics attached to the log.
# @param topics_count The number of the topics. Valid values are between 0 and 4 inclusively.
evmc_emit_log_fn* = proc(context: evmc_host_context, address: ptr evmc_address,
data: ptr byte, data_size: uint,
topics: ptr evmc_bytes32, topics_count: uint) {.cdecl.}
# Pointer to the callback function supporting EVM calls.
#
# @param context The pointer to the Host execution context.
# @param msg The call parameters.
# @return The result of the call.
evmc_call_fn* = proc(context: evmc_host_context, msg: ptr evmc_message): evmc_result {.cdecl.}
# The Host interface.
#
# The set of all callback functions expected by VM instances. This is C
# realisation of vtable for OOP interface (only virtual methods, no data).
# Host implementations SHOULD create constant singletons of this (similarly
# to vtables) to lower the maintenance and memory management cost.
evmc_host_interface* = object
# Check account existence callback function.
account_exists*: evmc_account_exists_fn
# Get storage callback function.
get_storage*: evmc_get_storage_fn
# Set storage callback function.
set_storage*: evmc_set_storage_fn
# Get balance callback function.
get_balance*: evmc_get_balance_fn
# Get code size callback function.
get_code_size*: evmc_get_code_size_fn
# Get code hash callback function.
get_code_hash*: evmc_get_code_hash_fn
# Copy code callback function.
copy_code*: evmc_copy_code_fn
# Selfdestruct callback function.
selfdestruct*: evmc_selfdestruct_fn
# Call callback function.
call*: evmc_call_fn
# Get transaction context callback function.
get_tx_context*: evmc_get_tx_context_fn
# Get block hash callback function.
get_block_hash*: evmc_get_block_hash_fn
# Emit log callback function.
emit_log*: evmc_emit_log_fn
# Destroys the VM instance.
#
# @param vm The VM instance to be destroyed.
evmc_destroy_fn* = proc(vm: ptr evmc_vm) {.cdecl.}
# Possible outcomes of evmc_set_option.
evmc_set_option_result* {.size: sizeof(cint).} = enum
EVMC_SET_OPTION_SUCCESS = 0
EVMC_SET_OPTION_INVALID_NAME = 1
EVMC_SET_OPTION_INVALID_VALUE = 2
# Configures the VM instance.
#
# Allows modifying options of the VM instance.
# Options:
# - code cache behavior: on, off, read-only, ...
# - optimizations,
#
# @param vm The VM instance to be configured.
# @param name The option name. NULL-terminated string. Cannot be NULL.
# @param value The new option value. NULL-terminated string. Cannot be NULL.
# @return The outcome of the operation.
evmc_set_option_fn* = proc(vm: ptr evmc_vm, name, value: cstring): evmc_set_option_result {.cdecl.}
# EVM revision.
#
# The revision of the EVM specification based on the Ethereum
# upgrade / hard fork codenames.
evmc_revision* {.size: sizeof(cint).} = enum
# The Frontier revision.
# The one Ethereum launched with.
EVMC_FRONTIER = 0
# The Homestead revision.
# https://eips.ethereum.org/EIPS/eip-606
EVMC_HOMESTEAD = 1
# The Tangerine Whistle revision.
# https://eips.ethereum.org/EIPS/eip-608
EVMC_TANGERINE_WHISTLE = 2
# The Spurious Dragon revision.
# https://eips.ethereum.org/EIPS/eip-607
EVMC_SPURIOUS_DRAGON = 3
# The Byzantium revision.
# https://eips.ethereum.org/EIPS/eip-609
EVMC_BYZANTIUM = 4
# The Constantinople revision.
# https://eips.ethereum.org/EIPS/eip-1013
EVMC_CONSTANTINOPLE = 5
# The Petersburg revision.
# Other names: Constantinople2, ConstantinopleFix.
# https://eips.ethereum.org/EIPS/eip-1716
EVMC_PETERSBURG = 6
# The Istanbul revision.
# The spec draft: https://eips.ethereum.org/EIPS/eip-1679.
EVMC_ISTANBUL = 7
# The Berlin revision.
# The spec draft: https://eips.ethereum.org/EIPS/eip-2070.
EVMC_BERLIN = 8
# Executes the given code using the input from the message.
#
# This function MAY be invoked multiple times for a single VM instance.
#
# @param vm The VM instance. This argument MUST NOT be NULL.
# @param host The Host interface. This argument MUST NOT be NULL unless
# the @p vm has the ::EVMC_CAPABILITY_PRECOMPILES capability.
# @param context The opaque pointer to the Host execution context.
# This argument MAY be NULL. The VM MUST pass the same
# pointer to the methods of the @p host interface.
# The VM MUST NOT dereference the pointer.
# @param rev The requested EVM specification revision.
# @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL.
# @param code The reference to the code to be executed. This argument MAY be NULL.
# @param code_size The length of the code. If @p code is NULL this argument MUST be 0.
# @return The execution result.
evmc_execute_fn* = proc(vm: ptr evmc_vm, host: ptr evmc_host_interface,
context: evmc_host_context, rev: evmc_revision,
msg: evmc_message, code: ptr byte, code_size: uint): evmc_result {.cdecl.}
# Possible capabilities of a VM.
evmc_capabilities* = distinct uint32
# EVMC_CAPABILITY_EVM1
# EVMC_CAPABILITY_EWASM
# EVMC_CAPABILITY_PRECOMPILES
# Return the supported capabilities of the VM instance.
#
# This function MAY be invoked multiple times for a single VM instance,
# and its value MAY be influenced by calls to evmc_vm::set_option.
#
# @param vm The VM instance.
# @return The supported capabilities of the VM. @see evmc_capabilities.
evmc_get_capabilities_fn* = proc(vm: ptr evmc_vm): evmc_capabilities {.cdecl.}
# The VM instance.
#
# Defines the base struct of the VM implementation.
evmc_vm* = object
# EVMC ABI version implemented by the VM instance.
#
# Can be used to detect ABI incompatibilities.
# The EVMC ABI version represented by this file is in ::EVMC_ABI_VERSION.
abi_version*: cint
# The name of the EVMC VM implementation.
#
# It MUST be a NULL-terminated not empty string.
# The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed).
name*: cstring
# The version of the EVMC VM implementation, e.g. "1.2.3b4".
#
# It MUST be a NULL-terminated not empty string.
# The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed).
version*: cstring
# Pointer to function destroying the VM instance.
#
# This is a mandatory method and MUST NOT be set to NULL.
destroy*: evmc_destroy_fn
# Pointer to function executing a code by the VM instance.
#
# This is a mandatory method and MUST NOT be set to NULL.
execute*: evmc_execute_fn
# A method returning capabilities supported by the VM instance.
#
# The value returned MAY change when different options are set via the set_option() method.
#
# A Client SHOULD only rely on the value returned if it has queried it after
# it has called the set_option().
#
# This is a mandatory method and MUST NOT be set to NULL.
get_capabilities*: evmc_get_capabilities_fn
# Optional pointer to function modifying VM's options.
#
# If the VM does not support this feature the pointer can be NULL.
set_option*: evmc_set_option_fn
const
# The VM is capable of executing EVM1 bytecode.
EVMC_CAPABILITY_EVM1* = 1.evmc_capabilities
# The VM is capable of executing ewasm bytecode.
EVMC_CAPABILITY_EWASM* = 2.evmc_capabilities
# The VM is capable of executing the precompiled contracts
# defined for the range of destination addresses.
#
# The EIP-1352 (https://eips.ethereum.org/EIPS/eip-1352) specifies
# the range 0x000...0000 - 0x000...ffff of addresses
# reserved for precompiled and system contracts.
#
# This capability is **experimental** and MAY be removed without notice.
EVMC_CAPABILITY_PRECOMPILES* = 4.evmc_capabilities
# Execution finished with success.
EVMC_SUCCESS* = 0.evmc_status_code
# Generic execution failure.
EVMC_FAILURE* = 1.evmc_status_code
# Execution terminated with REVERT opcode.
#
# In this case the amount of gas left MAY be non-zero and additional output
# data MAY be provided in ::evmc_result.
EVMC_REVERT* = 2.evmc_status_code
# The execution has run out of gas.
EVMC_OUT_OF_GAS* = 3.evmc_status_code
# The designated INVALID instruction has been hit during execution.
#
# The EIP-141 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md)
# defines the instruction 0xfe as INVALID instruction to indicate execution
# abortion coming from high-level languages. This status code is reported
# in case this INVALID instruction has been encountered.
EVMC_INVALID_INSTRUCTION* = 4.evmc_status_code
# An undefined instruction has been encountered.
EVMC_UNDEFINED_INSTRUCTION* = 5.evmc_status_code
# The execution has attempted to put more items on the EVM stack
# than the specified limit.
EVMC_STACK_OVERFLOW* = 6.evmc_status_code
# Execution of an opcode has required more items on the EVM stack.
EVMC_STACK_UNDERFLOW* = 7.evmc_status_code
# Execution has violated the jump destination restrictions.
EVMC_BAD_JUMP_DESTINATION* = 8.evmc_status_code
# Tried to read outside memory bounds.
#
# An example is RETURNDATACOPY reading past the available buffer.
EVMC_INVALID_MEMORY_ACCESS* = 9.evmc_status_code
# Call depth has exceeded the limit (if any)
EVMC_CALL_DEPTH_EXCEEDED* = 10.evmc_status_code
# Tried to execute an operation which is restricted in static mode.
EVMC_STATIC_MODE_VIOLATION* = 11.evmc_status_code
# A call to a precompiled or system contract has ended with a failure.
#
# An example: elliptic curve functions handed invalid EC points.
EVMC_PRECOMPILE_FAILURE* = 12.evmc_status_code
# Contract validation has failed (e.g. due to EVM 1.5 jump validity,
# Casper's purity checker or ewasm contract rules).
EVMC_CONTRACT_VALIDATION_FAILURE* = 13.evmc_status_code
# An argument to a state accessing method has a value outside of the
# accepted range of values.
EVMC_ARGUMENT_OUT_OF_RANGE* = 14.evmc_status_code
# A WebAssembly `unreachable` instruction has been hit during execution.
EVMC_WASM_UNREACHABLE_INSTRUCTION* = 15.evmc_status_code
# A WebAssembly trap has been hit during execution. This can be for many
# reasons, including division by zero, validation errors, etc.
EVMC_WASM_TRAP* = 16.evmc_status_code
# EVM implementation generic internal error.
EVMC_INTERNAL_ERROR* = evmc_status_code(-1)
# The execution of the given code and/or message has been rejected
# by the EVM implementation.
#
# This error SHOULD be used to signal that the EVM is not able to or
# willing to execute the given code type or message.
# If an EVM returns the ::EVMC_REJECTED status code,
# the Client MAY try to execute it in other EVM implementation.
# For example, the Client tries running a code in the EVM 1.5. If the
# code is not supported there, the execution falls back to the EVM 1.0.
EVMC_REJECTED* = evmc_status_code(-2)
# The VM failed to allocate the amount of memory needed for execution.
EVMC_OUT_OF_MEMORY* = evmc_status_code(-3)
# The maximum EVM revision supported.
EVMC_MAX_REVISION* = EVMC_BERLIN
proc incl*(a: var evmc_capabilities, b: evmc_capabilities) {.inline.} =
a = evmc_capabilities(a.uint32 or b.uint32)
proc excl*(a: var evmc_capabilities, b: evmc_capabilities) {.inline.} =
a = evmc_capabilities(a.uint32 and (not b.uint32))
proc contains*(a, b: evmc_capabilities): bool {.inline.} =
(a.uint32 and b.uint32) != 0
proc `==`*(a, b: evmc_status_code): bool {.borrow.}

View File

@ -1,18 +0,0 @@
{.deadCodeElim: on.}
when defined(windows):
const
libevmjit* = "libevmjit.dll"
elif defined(macosx):
const
libevmjit* = "libevmjit.dylib"
else:
const
libevmjit* = "libevmjit.so"
import evmc
export evmc
proc evmjit_create*(): ptr evmc_vm {.cdecl, importc: "evmjit_create", dynlib: libevmjit.}
## Create EVMJIT instance.
##
## @return The EVMJIT instance.

View File

@ -1,941 +0,0 @@
/**
* EVMC: Ethereum Client-VM Connector API
*
* @copyright
* Copyright 2016-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*
* @defgroup EVMC EVMC
* @{
*/
#ifndef EVMC_H
#define EVMC_H
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 6)
/**
* Portable declaration of "deprecated" attribute.
*
* Available for clang and GCC 6+ compilers. The older GCC compilers know
* this attribute, but it cannot be applied to enum elements.
*/
#define EVMC_DEPRECATED __attribute__((deprecated))
#else
#define EVMC_DEPRECATED
#endif
#include <stdbool.h> /* Definition of bool, true and false. */
#include <stddef.h> /* Definition of size_t. */
#include <stdint.h> /* Definition of int64_t, uint64_t. */
#if __cplusplus
extern "C" {
#endif
/* BEGIN Python CFFI declarations */
enum
{
/**
* The EVMC ABI version number of the interface declared in this file.
*
* The EVMC ABI version always equals the major version number of the EVMC project.
* The Host SHOULD check if the ABI versions match when dynamically loading VMs.
*
* @see @ref versioning
*/
EVMC_ABI_VERSION = 7
};
/**
* The fixed size array of 32 bytes.
*
* 32 bytes of data capable of storing e.g. 256-bit hashes.
*/
typedef struct evmc_bytes32
{
/** The 32 bytes. */
uint8_t bytes[32];
} evmc_bytes32;
/**
* The alias for evmc_bytes32 to represent a big-endian 256-bit integer.
*/
typedef struct evmc_bytes32 evmc_uint256be;
/** Big-endian 160-bit hash suitable for keeping an Ethereum address. */
typedef struct evmc_address
{
/** The 20 bytes of the hash. */
uint8_t bytes[20];
} evmc_address;
/** The kind of call-like instruction. */
enum evmc_call_kind
{
EVMC_CALL = 0, /**< Request CALL. */
EVMC_DELEGATECALL = 1, /**< Request DELEGATECALL. Valid since Homestead.
The value param ignored. */
EVMC_CALLCODE = 2, /**< Request CALLCODE. */
EVMC_CREATE = 3, /**< Request CREATE. */
EVMC_CREATE2 = 4 /**< Request CREATE2. Valid since Constantinople.*/
};
/** The flags for ::evmc_message. */
enum evmc_flags
{
EVMC_STATIC = 1 /**< Static call mode. */
};
/**
* The message describing an EVM call,
* including a zero-depth calls from a transaction origin.
*/
struct evmc_message
{
/** The kind of the call. For zero-depth calls ::EVMC_CALL SHOULD be used. */
enum evmc_call_kind kind;
/**
* Additional flags modifying the call execution behavior.
* In the current version the only valid values are ::EVMC_STATIC or 0.
*/
uint32_t flags;
/** The call depth. */
int32_t depth;
/** The amount of gas for message execution. */
int64_t gas;
/** The destination of the message. */
evmc_address destination;
/** The sender of the message. */
evmc_address sender;
/**
* The message input data.
*
* This MAY be NULL.
*/
const uint8_t* input_data;
/**
* The size of the message input data.
*
* If input_data is NULL this MUST be 0.
*/
size_t input_size;
/**
* The amount of Ether transferred with the message.
*/
evmc_uint256be value;
/**
* The optional value used in new contract address construction.
*
* Ignored unless kind is EVMC_CREATE2.
*/
evmc_bytes32 create2_salt;
};
/** The transaction and block data for execution. */
struct evmc_tx_context
{
evmc_uint256be tx_gas_price; /**< The transaction gas price. */
evmc_address tx_origin; /**< The transaction origin account. */
evmc_address block_coinbase; /**< The miner of the block. */
int64_t block_number; /**< The block number. */
int64_t block_timestamp; /**< The block timestamp. */
int64_t block_gas_limit; /**< The block gas limit. */
evmc_uint256be block_difficulty; /**< The block difficulty. */
evmc_uint256be chain_id; /**< The blockchain's ChainID. */
};
/**
* @struct evmc_host_context
* The opaque data type representing the Host execution context.
* @see evmc_execute_fn().
*/
struct evmc_host_context;
/**
* Get transaction context callback function.
*
* This callback function is used by an EVM to retrieve the transaction and
* block context.
*
* @param context The pointer to the Host execution context.
* @return The transaction context.
*/
typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_host_context* context);
/**
* Get block hash callback function.
*
* This callback function is used by a VM to query the hash of the header of the given block.
* If the information about the requested block is not available, then this is signalled by
* returning null bytes.
*
* @param context The pointer to the Host execution context.
* @param number The block number.
* @return The block hash or null bytes
* if the information about the block is not available.
*/
typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_host_context* context, int64_t number);
/**
* The execution status code.
*
* Successful execution is represented by ::EVMC_SUCCESS having value 0.
*
* Positive values represent failures defined by VM specifications with generic
* ::EVMC_FAILURE code of value 1.
*
* Status codes with negative values represent VM internal errors
* not provided by EVM specifications. These errors MUST not be passed back
* to the caller. They MAY be handled by the Client in predefined manner
* (see e.g. ::EVMC_REJECTED), otherwise internal errors are not recoverable.
* The generic representant of errors is ::EVMC_INTERNAL_ERROR but
* an EVM implementation MAY return negative status codes that are not defined
* in the EVMC documentation.
*
* @note
* In case new status codes are needed, please create an issue or pull request
* in the EVMC repository (https://github.com/ethereum/evmc).
*/
enum evmc_status_code
{
/** Execution finished with success. */
EVMC_SUCCESS = 0,
/** Generic execution failure. */
EVMC_FAILURE = 1,
/**
* Execution terminated with REVERT opcode.
*
* In this case the amount of gas left MAY be non-zero and additional output
* data MAY be provided in ::evmc_result.
*/
EVMC_REVERT = 2,
/** The execution has run out of gas. */
EVMC_OUT_OF_GAS = 3,
/**
* The designated INVALID instruction has been hit during execution.
*
* The EIP-141 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md)
* defines the instruction 0xfe as INVALID instruction to indicate execution
* abortion coming from high-level languages. This status code is reported
* in case this INVALID instruction has been encountered.
*/
EVMC_INVALID_INSTRUCTION = 4,
/** An undefined instruction has been encountered. */
EVMC_UNDEFINED_INSTRUCTION = 5,
/**
* The execution has attempted to put more items on the EVM stack
* than the specified limit.
*/
EVMC_STACK_OVERFLOW = 6,
/** Execution of an opcode has required more items on the EVM stack. */
EVMC_STACK_UNDERFLOW = 7,
/** Execution has violated the jump destination restrictions. */
EVMC_BAD_JUMP_DESTINATION = 8,
/**
* Tried to read outside memory bounds.
*
* An example is RETURNDATACOPY reading past the available buffer.
*/
EVMC_INVALID_MEMORY_ACCESS = 9,
/** Call depth has exceeded the limit (if any) */
EVMC_CALL_DEPTH_EXCEEDED = 10,
/** Tried to execute an operation which is restricted in static mode. */
EVMC_STATIC_MODE_VIOLATION = 11,
/**
* A call to a precompiled or system contract has ended with a failure.
*
* An example: elliptic curve functions handed invalid EC points.
*/
EVMC_PRECOMPILE_FAILURE = 12,
/**
* Contract validation has failed (e.g. due to EVM 1.5 jump validity,
* Casper's purity checker or ewasm contract rules).
*/
EVMC_CONTRACT_VALIDATION_FAILURE = 13,
/**
* An argument to a state accessing method has a value outside of the
* accepted range of values.
*/
EVMC_ARGUMENT_OUT_OF_RANGE = 14,
/**
* A WebAssembly `unreachable` instruction has been hit during execution.
*/
EVMC_WASM_UNREACHABLE_INSTRUCTION = 15,
/**
* A WebAssembly trap has been hit during execution. This can be for many
* reasons, including division by zero, validation errors, etc.
*/
EVMC_WASM_TRAP = 16,
/** EVM implementation generic internal error. */
EVMC_INTERNAL_ERROR = -1,
/**
* The execution of the given code and/or message has been rejected
* by the EVM implementation.
*
* This error SHOULD be used to signal that the EVM is not able to or
* willing to execute the given code type or message.
* If an EVM returns the ::EVMC_REJECTED status code,
* the Client MAY try to execute it in other EVM implementation.
* For example, the Client tries running a code in the EVM 1.5. If the
* code is not supported there, the execution falls back to the EVM 1.0.
*/
EVMC_REJECTED = -2,
/** The VM failed to allocate the amount of memory needed for execution. */
EVMC_OUT_OF_MEMORY = -3
};
/* Forward declaration. */
struct evmc_result;
/**
* Releases resources assigned to an execution result.
*
* This function releases memory (and other resources, if any) assigned to the
* specified execution result making the result object invalid.
*
* @param result The execution result which resources are to be released. The
* result itself it not modified by this function, but becomes
* invalid and user MUST discard it as well.
* This MUST NOT be NULL.
*
* @note
* The result is passed by pointer to avoid (shallow) copy of the ::evmc_result
* struct. Think of this as the best possible C language approximation to
* passing objects by reference.
*/
typedef void (*evmc_release_result_fn)(const struct evmc_result* result);
/** The EVM code execution result. */
struct evmc_result
{
/** The execution status code. */
enum evmc_status_code status_code;
/**
* The amount of gas left after the execution.
*
* If evmc_result::code is not ::EVMC_SUCCESS nor ::EVMC_REVERT
* the value MUST be 0.
*/
int64_t gas_left;
/**
* The reference to output data.
*
* The output contains data coming from RETURN opcode (iff evmc_result::code
* field is ::EVMC_SUCCESS) or from REVERT opcode.
*
* The memory containing the output data is owned by EVM and has to be
* freed with evmc_result::release().
*
* This MAY be NULL.
*/
const uint8_t* output_data;
/**
* The size of the output data.
*
* If output_data is NULL this MUST be 0.
*/
size_t output_size;
/**
* The method releasing all resources associated with the result object.
*
* This method (function pointer) is optional (MAY be NULL) and MAY be set
* by the VM implementation. If set it MUST be called by the user once to
* release memory and other resources associated with the result object.
* Once the resources are released the result object MUST NOT be used again.
*
* The suggested code pattern for releasing execution results:
* @code
* struct evmc_result result = ...;
* if (result.release)
* result.release(&result);
* @endcode
*
* @note
* It works similarly to C++ virtual destructor. Attaching the release
* function to the result itself allows VM composition.
*/
evmc_release_result_fn release;
/**
* The address of the contract created by create instructions.
*
* This field has valid value only if:
* - it is a result of the Host method evmc_host_interface::call
* - and the result describes successful contract creation
* (evmc_result::status_code is ::EVMC_SUCCESS).
* In all other cases the address MUST be null bytes.
*/
evmc_address create_address;
/**
* Reserved data that MAY be used by a evmc_result object creator.
*
* This reserved 4 bytes together with 20 bytes from create_address form
* 24 bytes of memory called "optional data" within evmc_result struct
* to be optionally used by the evmc_result object creator.
*
* @see evmc_result_optional_data, evmc_get_optional_data().
*
* Also extends the size of the evmc_result to 64 bytes (full cache line).
*/
uint8_t padding[4];
};
/**
* Check account existence callback function.
*
* This callback function is used by the VM to check if
* there exists an account at given address.
* @param context The pointer to the Host execution context.
* @param address The address of the account the query is about.
* @return true if exists, false otherwise.
*/
typedef bool (*evmc_account_exists_fn)(struct evmc_host_context* context,
const evmc_address* address);
/**
* Get storage callback function.
*
* This callback function is used by a VM to query the given account storage entry.
*
* @param context The Host execution context.
* @param address The address of the account.
* @param key The index of the account's storage entry.
* @return The storage value at the given storage key or null bytes
* if the account does not exist.
*/
typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context,
const evmc_address* address,
const evmc_bytes32* key);
/**
* The effect of an attempt to modify a contract storage item.
*
* For the purpose of explaining the meaning of each element, the following
* notation is used:
* - 0 is zero value,
* - X != 0 (X is any value other than 0),
* - Y != X, Y != 0 (Y is any value other than X and 0),
* - Z != Y (Z is any value other than Y),
* - the "->" means the change from one value to another.
*/
enum evmc_storage_status
{
/**
* The value of a storage item has been left unchanged: 0 -> 0 and X -> X.
*/
EVMC_STORAGE_UNCHANGED = 0,
/**
* The value of a storage item has been modified: X -> Y.
*/
EVMC_STORAGE_MODIFIED = 1,
/**
* A storage item has been modified after being modified before: X -> Y -> Z.
*/
EVMC_STORAGE_MODIFIED_AGAIN = 2,
/**
* A new storage item has been added: 0 -> X.
*/
EVMC_STORAGE_ADDED = 3,
/**
* A storage item has been deleted: X -> 0.
*/
EVMC_STORAGE_DELETED = 4
};
/**
* Set storage callback function.
*
* This callback function is used by a VM to update the given account storage entry.
* The VM MUST make sure that the account exists. This requirement is only a formality because
* VM implementations only modify storage of the account of the current execution context
* (i.e. referenced by evmc_message::destination).
*
* @param context The pointer to the Host execution context.
* @param address The address of the account.
* @param key The index of the storage entry.
* @param value The value to be stored.
* @return The effect on the storage item.
*/
typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_host_context* context,
const evmc_address* address,
const evmc_bytes32* key,
const evmc_bytes32* value);
/**
* Get balance callback function.
*
* This callback function is used by a VM to query the balance of the given account.
*
* @param context The pointer to the Host execution context.
* @param address The address of the account.
* @return The balance of the given account or 0 if the account does not exist.
*/
typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_host_context* context,
const evmc_address* address);
/**
* Get code size callback function.
*
* This callback function is used by a VM to get the size of the code stored
* in the account at the given address.
*
* @param context The pointer to the Host execution context.
* @param address The address of the account.
* @return The size of the code in the account or 0 if the account does not exist.
*/
typedef size_t (*evmc_get_code_size_fn)(struct evmc_host_context* context,
const evmc_address* address);
/**
* Get code hash callback function.
*
* This callback function is used by a VM to get the keccak256 hash of the code stored
* in the account at the given address. For existing accounts not having a code, this
* function returns keccak256 hash of empty data.
*
* @param context The pointer to the Host execution context.
* @param address The address of the account.
* @return The hash of the code in the account or null bytes if the account does not exist.
*/
typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_host_context* context,
const evmc_address* address);
/**
* Copy code callback function.
*
* This callback function is used by an EVM to request a copy of the code
* of the given account to the memory buffer provided by the EVM.
* The Client MUST copy the requested code, starting with the given offset,
* to the provided memory buffer up to the size of the buffer or the size of
* the code, whichever is smaller.
*
* @param context The pointer to the Host execution context. See ::evmc_host_context.
* @param address The address of the account.
* @param code_offset The offset of the code to copy.
* @param buffer_data The pointer to the memory buffer allocated by the EVM
* to store a copy of the requested code.
* @param buffer_size The size of the memory buffer.
* @return The number of bytes copied to the buffer by the Client.
*/
typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context,
const evmc_address* address,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size);
/**
* Selfdestruct callback function.
*
* This callback function is used by an EVM to SELFDESTRUCT given contract.
* The execution of the contract will not be stopped, that is up to the EVM.
*
* @param context The pointer to the Host execution context. See ::evmc_host_context.
* @param address The address of the contract to be selfdestructed.
* @param beneficiary The address where the remaining ETH is going to be transferred.
*/
typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context,
const evmc_address* address,
const evmc_address* beneficiary);
/**
* Log callback function.
*
* This callback function is used by an EVM to inform about a LOG that happened
* during an EVM bytecode execution.
*
* @param context The pointer to the Host execution context. See ::evmc_host_context.
* @param address The address of the contract that generated the log.
* @param data The pointer to unindexed data attached to the log.
* @param data_size The length of the data.
* @param topics The pointer to the array of topics attached to the log.
* @param topics_count The number of the topics. Valid values are between 0 and 4 inclusively.
*/
typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context,
const evmc_address* address,
const uint8_t* data,
size_t data_size,
const evmc_bytes32 topics[],
size_t topics_count);
/**
* Pointer to the callback function supporting EVM calls.
*
* @param context The pointer to the Host execution context.
* @param msg The call parameters.
* @return The result of the call.
*/
typedef struct evmc_result (*evmc_call_fn)(struct evmc_host_context* context,
const struct evmc_message* msg);
/**
* The Host interface.
*
* The set of all callback functions expected by VM instances. This is C
* realisation of vtable for OOP interface (only virtual methods, no data).
* Host implementations SHOULD create constant singletons of this (similarly
* to vtables) to lower the maintenance and memory management cost.
*/
struct evmc_host_interface
{
/** Check account existence callback function. */
evmc_account_exists_fn account_exists;
/** Get storage callback function. */
evmc_get_storage_fn get_storage;
/** Set storage callback function. */
evmc_set_storage_fn set_storage;
/** Get balance callback function. */
evmc_get_balance_fn get_balance;
/** Get code size callback function. */
evmc_get_code_size_fn get_code_size;
/** Get code hash callback function. */
evmc_get_code_hash_fn get_code_hash;
/** Copy code callback function. */
evmc_copy_code_fn copy_code;
/** Selfdestruct callback function. */
evmc_selfdestruct_fn selfdestruct;
/** Call callback function. */
evmc_call_fn call;
/** Get transaction context callback function. */
evmc_get_tx_context_fn get_tx_context;
/** Get block hash callback function. */
evmc_get_block_hash_fn get_block_hash;
/** Emit log callback function. */
evmc_emit_log_fn emit_log;
};
/* Forward declaration. */
struct evmc_vm;
/**
* Destroys the VM instance.
*
* @param vm The VM instance to be destroyed.
*/
typedef void (*evmc_destroy_fn)(struct evmc_vm* vm);
/**
* Possible outcomes of evmc_set_option.
*/
enum evmc_set_option_result
{
EVMC_SET_OPTION_SUCCESS = 0,
EVMC_SET_OPTION_INVALID_NAME = 1,
EVMC_SET_OPTION_INVALID_VALUE = 2
};
/**
* Configures the VM instance.
*
* Allows modifying options of the VM instance.
* Options:
* - code cache behavior: on, off, read-only, ...
* - optimizations,
*
* @param vm The VM instance to be configured.
* @param name The option name. NULL-terminated string. Cannot be NULL.
* @param value The new option value. NULL-terminated string. Cannot be NULL.
* @return The outcome of the operation.
*/
typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_vm* vm,
char const* name,
char const* value);
/**
* EVM revision.
*
* The revision of the EVM specification based on the Ethereum
* upgrade / hard fork codenames.
*/
enum evmc_revision
{
/**
* The Frontier revision.
*
* The one Ethereum launched with.
*/
EVMC_FRONTIER = 0,
/**
* The Homestead revision.
*
* https://eips.ethereum.org/EIPS/eip-606
*/
EVMC_HOMESTEAD = 1,
/**
* The Tangerine Whistle revision.
*
* https://eips.ethereum.org/EIPS/eip-608
*/
EVMC_TANGERINE_WHISTLE = 2,
/**
* The Spurious Dragon revision.
*
* https://eips.ethereum.org/EIPS/eip-607
*/
EVMC_SPURIOUS_DRAGON = 3,
/**
* The Byzantium revision.
*
* https://eips.ethereum.org/EIPS/eip-609
*/
EVMC_BYZANTIUM = 4,
/**
* The Constantinople revision.
*
* https://eips.ethereum.org/EIPS/eip-1013
*/
EVMC_CONSTANTINOPLE = 5,
/**
* The Petersburg revision.
*
* Other names: Constantinople2, ConstantinopleFix.
*
* https://eips.ethereum.org/EIPS/eip-1716
*/
EVMC_PETERSBURG = 6,
/**
* The Istanbul revision.
*
* The spec draft: https://eips.ethereum.org/EIPS/eip-1679.
*/
EVMC_ISTANBUL = 7,
/**
* The Berlin revision.
*
* The spec draft: https://eips.ethereum.org/EIPS/eip-2070.
*/
EVMC_BERLIN = 8,
/** The maximum EVM revision supported. */
EVMC_MAX_REVISION = EVMC_BERLIN
};
/**
* Executes the given code using the input from the message.
*
* This function MAY be invoked multiple times for a single VM instance.
*
* @param vm The VM instance. This argument MUST NOT be NULL.
* @param host The Host interface. This argument MUST NOT be NULL unless
* the @p vm has the ::EVMC_CAPABILITY_PRECOMPILES capability.
* @param context The opaque pointer to the Host execution context.
* This argument MAY be NULL. The VM MUST pass the same
* pointer to the methods of the @p host interface.
* The VM MUST NOT dereference the pointer.
* @param rev The requested EVM specification revision.
* @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL.
* @param code The reference to the code to be executed. This argument MAY be NULL.
* @param code_size The length of the code. If @p code is NULL this argument MUST be 0.
* @return The execution result.
*/
typedef struct evmc_result (*evmc_execute_fn)(struct evmc_vm* vm,
const struct evmc_host_interface* host,
struct evmc_host_context* context,
enum evmc_revision rev,
const struct evmc_message* msg,
uint8_t const* code,
size_t code_size);
/**
* Possible capabilities of a VM.
*/
enum evmc_capabilities
{
/**
* The VM is capable of executing EVM1 bytecode.
*/
EVMC_CAPABILITY_EVM1 = (1u << 0),
/**
* The VM is capable of executing ewasm bytecode.
*/
EVMC_CAPABILITY_EWASM = (1u << 1),
/**
* The VM is capable of executing the precompiled contracts
* defined for the range of destination addresses.
*
* The EIP-1352 (https://eips.ethereum.org/EIPS/eip-1352) specifies
* the range 0x000...0000 - 0x000...ffff of addresses
* reserved for precompiled and system contracts.
*
* This capability is **experimental** and MAY be removed without notice.
*/
EVMC_CAPABILITY_PRECOMPILES = (1u << 2)
};
/**
* Alias for unsigned integer representing a set of bit flags of EVMC capabilities.
*
* @see evmc_capabilities
*/
typedef uint32_t evmc_capabilities_flagset;
/**
* Return the supported capabilities of the VM instance.
*
* This function MAY be invoked multiple times for a single VM instance,
* and its value MAY be influenced by calls to evmc_vm::set_option.
*
* @param vm The VM instance.
* @return The supported capabilities of the VM. @see evmc_capabilities.
*/
typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_vm* vm);
/**
* The VM instance.
*
* Defines the base struct of the VM implementation.
*/
struct evmc_vm
{
/**
* EVMC ABI version implemented by the VM instance.
*
* Can be used to detect ABI incompatibilities.
* The EVMC ABI version represented by this file is in ::EVMC_ABI_VERSION.
*/
const int abi_version;
/**
* The name of the EVMC VM implementation.
*
* It MUST be a NULL-terminated not empty string.
* The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed).
*/
const char* name;
/**
* The version of the EVMC VM implementation, e.g. "1.2.3b4".
*
* It MUST be a NULL-terminated not empty string.
* The content MUST be UTF-8 encoded (this implies ASCII encoding is also allowed).
*/
const char* version;
/**
* Pointer to function destroying the VM instance.
*
* This is a mandatory method and MUST NOT be set to NULL.
*/
evmc_destroy_fn destroy;
/**
* Pointer to function executing a code by the VM instance.
*
* This is a mandatory method and MUST NOT be set to NULL.
*/
evmc_execute_fn execute;
/**
* A method returning capabilities supported by the VM instance.
*
* The value returned MAY change when different options are set via the set_option() method.
*
* A Client SHOULD only rely on the value returned if it has queried it after
* it has called the set_option().
*
* This is a mandatory method and MUST NOT be set to NULL.
*/
evmc_get_capabilities_fn get_capabilities;
/**
* Optional pointer to function modifying VM's options.
*
* If the VM does not support this feature the pointer can be NULL.
*/
evmc_set_option_fn set_option;
};
/* END Python CFFI declarations */
#if EVMC_DOCUMENTATION
/**
* Example of a function creating an instance of an example EVM implementation.
*
* Each EVM implementation MUST provide a function returning an EVM instance.
* The function SHOULD be named `evmc_create_<vm-name>(void)`. If the VM name contains hyphens
* replaces them with underscores in the function names.
*
* @par Binaries naming convention
* For VMs distributed as shared libraries, the name of the library SHOULD match the VM name.
* The convetional library filename prefixes and extensions SHOULD be ignored by the Client.
* For example, the shared library with the "beta-interpreter" implementation may be named
* `libbeta-interpreter.so`.
*
* @return The VM instance or NULL indicating instance creation failure.
*/
struct evmc_vm* evmc_create_example_vm(void);
#endif
#if __cplusplus
}
#endif
#endif
/** @} */

View File

@ -1,805 +0,0 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
#pragma once
#include "evmc.h"
#include "helpers.h"
#include <functional>
#include <initializer_list>
#include <utility>
/// EVMC C++ API - wrappers and bindings for C++
/// @ingroup cpp
namespace evmc
{
/// The big-endian 160-bit hash suitable for keeping an Ethereum address.
///
/// This type wraps C ::evmc_address to make sure objects of this type are always initialized.
struct address : evmc_address
{
/// Default and converting constructor.
///
/// Initializes bytes to zeros if not other @p init value provided.
constexpr address(evmc_address init = {}) noexcept : evmc_address{init} {}
/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
};
/// The fixed size array of 32 bytes for storing 256-bit EVM values.
///
/// This type wraps C ::evmc_bytes32 to make sure objects of this type are always initialized.
struct bytes32 : evmc_bytes32
{
/// Default and converting constructor.
///
/// Initializes bytes to zeros if not other @p init value provided.
constexpr bytes32(evmc_bytes32 init = {}) noexcept : evmc_bytes32{init} {}
/// Explicit operator converting to bool.
constexpr inline explicit operator bool() const noexcept;
};
/// The alias for evmc::bytes32 to represent a big-endian 256-bit integer.
using uint256be = bytes32;
/// Loads 64 bits / 8 bytes of data from the given @p bytes array in big-endian order.
constexpr inline uint64_t load64be(const uint8_t* bytes) noexcept
{
return (uint64_t{bytes[0]} << 56) | (uint64_t{bytes[1]} << 48) | (uint64_t{bytes[2]} << 40) |
(uint64_t{bytes[3]} << 32) | (uint64_t{bytes[4]} << 24) | (uint64_t{bytes[5]} << 16) |
(uint64_t{bytes[6]} << 8) | uint64_t{bytes[7]};
}
/// Loads 32 bits / 4 bytes of data from the given @p bytes array in big-endian order.
constexpr inline uint32_t load32be(const uint8_t* bytes) noexcept
{
return (uint32_t{bytes[0]} << 24) | (uint32_t{bytes[1]} << 16) | (uint32_t{bytes[2]} << 8) |
uint32_t{bytes[3]};
}
namespace fnv
{
constexpr auto prime = 0x100000001b3; ///< The 64-bit FNV prime number.
constexpr auto offset_basis = 0xcbf29ce484222325; ///< The 64-bit FNV offset basis.
/// The hashing transformation for 64-bit inputs based on the FNV-1a formula.
constexpr inline uint64_t fnv1a_by64(uint64_t h, uint64_t x) noexcept
{
return (h ^ x) * prime;
}
} // namespace fnv
/// The "equal to" comparison operator for the evmc::address type.
constexpr bool operator==(const address& a, const address& b) noexcept
{
// TODO: Report bug in clang keeping unnecessary bswap.
return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load32be(&a.bytes[16]) == load32be(&b.bytes[16]);
}
/// The "not equal to" comparison operator for the evmc::address type.
constexpr bool operator!=(const address& a, const address& b) noexcept
{
return !(a == b);
}
/// The "less than" comparison operator for the evmc::address type.
constexpr bool operator<(const address& a, const address& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load32be(&a.bytes[16]) < load32be(&b.bytes[16]));
}
/// The "greater than" comparison operator for the evmc::address type.
constexpr bool operator>(const address& a, const address& b) noexcept
{
return b < a;
}
/// The "less than or equal to" comparison operator for the evmc::address type.
constexpr bool operator<=(const address& a, const address& b) noexcept
{
return !(b < a);
}
/// The "greater than or equal to" comparison operator for the evmc::address type.
constexpr bool operator>=(const address& a, const address& b) noexcept
{
return !(a < b);
}
/// The "equal to" comparison operator for the evmc::bytes32 type.
constexpr bool operator==(const bytes32& a, const bytes32& b) noexcept
{
return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
load64be(&a.bytes[24]) == load64be(&b.bytes[24]);
}
/// The "not equal to" comparison operator for the evmc::bytes32 type.
constexpr bool operator!=(const bytes32& a, const bytes32& b) noexcept
{
return !(a == b);
}
/// The "less than" comparison operator for the evmc::bytes32 type.
constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept
{
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
load64be(&a.bytes[16]) < load64be(&b.bytes[16])) ||
(load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
load64be(&a.bytes[24]) < load64be(&b.bytes[24]));
}
/// The "greater than" comparison operator for the evmc::bytes32 type.
constexpr bool operator>(const bytes32& a, const bytes32& b) noexcept
{
return b < a;
}
/// The "less than or equal to" comparison operator for the evmc::bytes32 type.
constexpr bool operator<=(const bytes32& a, const bytes32& b) noexcept
{
return !(b < a);
}
/// The "greater than or equal to" comparison operator for the evmc::bytes32 type.
constexpr bool operator>=(const bytes32& a, const bytes32& b) noexcept
{
return !(a < b);
}
/// Checks if the given address is the zero address.
constexpr inline bool is_zero(const address& a) noexcept
{
return a == address{};
}
constexpr address::operator bool() const noexcept
{
return !is_zero(*this);
}
/// Checks if the given bytes32 object has all zero bytes.
constexpr inline bool is_zero(const bytes32& a) noexcept
{
return a == bytes32{};
}
constexpr bytes32::operator bool() const noexcept
{
return !is_zero(*this);
}
namespace literals
{
namespace internal
{
template <typename T, T... Ints>
struct integer_sequence
{
};
template <uint8_t... Bytes>
using byte_sequence = integer_sequence<uint8_t, Bytes...>;
template <char... Chars>
using char_sequence = integer_sequence<char, Chars...>;
template <typename, typename>
struct concatenate;
template <uint8_t... Bytes1, uint8_t... Bytes2>
struct concatenate<byte_sequence<Bytes1...>, byte_sequence<Bytes2...>>
{
using type = byte_sequence<Bytes1..., Bytes2...>;
};
template <uint8_t D>
constexpr uint8_t parse_hex_digit() noexcept
{
static_assert((D >= '0' && D <= '9') || (D >= 'a' && D <= 'f') || (D >= 'A' && D <= 'F'),
"literal must be hexadecimal integer");
return static_cast<uint8_t>(
(D >= '0' && D <= '9') ? D - '0' : (D >= 'a' && D <= 'f') ? D - 'a' + 10 : D - 'A' + 10);
}
template <typename>
struct parse_digits;
template <uint8_t Digit1, uint8_t Digit2>
struct parse_digits<byte_sequence<Digit1, Digit2>>
{
using type = byte_sequence<static_cast<uint8_t>(parse_hex_digit<Digit1>() << 4) |
parse_hex_digit<Digit2>()>;
};
template <uint8_t Digit1, uint8_t Digit2, uint8_t... Rest>
struct parse_digits<byte_sequence<Digit1, Digit2, Rest...>>
{
using type = typename concatenate<typename parse_digits<byte_sequence<Digit1, Digit2>>::type,
typename parse_digits<byte_sequence<Rest...>>::type>::type;
};
template <typename, typename>
struct parse_literal;
template <typename T, char Prefix1, char Prefix2, char... Literal>
struct parse_literal<T, char_sequence<Prefix1, Prefix2, Literal...>>
{
static_assert(Prefix1 == '0' && Prefix2 == 'x', "literal must be in hexadecimal notation");
static_assert(sizeof...(Literal) == sizeof(T) * 2, "literal must match the result type size");
template <uint8_t... Bytes>
static constexpr T create_from(byte_sequence<Bytes...>) noexcept
{
return T{{{Bytes...}}};
}
static constexpr T get() noexcept
{
return create_from(typename parse_digits<byte_sequence<Literal...>>::type{});
}
};
template <typename T, char Digit>
struct parse_literal<T, char_sequence<Digit>>
{
static_assert(Digit == '0', "only 0 is allowed as a single digit literal");
static constexpr T get() noexcept { return {}; }
};
template <typename T, char... Literal>
constexpr T parse() noexcept
{
return parse_literal<T, char_sequence<Literal...>>::get();
}
} // namespace internal
/// Literal for evmc::address.
template <char... Literal>
constexpr address operator"" _address() noexcept
{
return internal::parse<address, Literal...>();
}
/// Literal for evmc::bytes32.
template <char... Literal>
constexpr bytes32 operator"" _bytes32() noexcept
{
return internal::parse<bytes32, Literal...>();
}
} // namespace literals
using namespace literals;
/// Alias for evmc_make_result().
constexpr auto make_result = evmc_make_result;
/// @copydoc evmc_result
///
/// This is a RAII wrapper for evmc_result and objects of this type
/// automatically release attached resources.
class result : private evmc_result
{
public:
using evmc_result::create_address;
using evmc_result::gas_left;
using evmc_result::output_data;
using evmc_result::output_size;
using evmc_result::status_code;
/// Creates the result from the provided arguments.
///
/// The provided output is copied to memory allocated with malloc()
/// and the evmc_result::release function is set to one invoking free().
///
/// @param _status_code The status code.
/// @param _gas_left The amount of gas left.
/// @param _output_data The pointer to the output.
/// @param _output_size The output size.
result(evmc_status_code _status_code,
int64_t _gas_left,
const uint8_t* _output_data,
size_t _output_size) noexcept
: evmc_result{make_result(_status_code, _gas_left, _output_data, _output_size)}
{}
/// Converting constructor from raw evmc_result.
explicit result(evmc_result const& res) noexcept : evmc_result{res} {}
/// Destructor responsible for automatically releasing attached resources.
~result() noexcept
{
if (release != nullptr)
release(this);
}
/// Move constructor.
result(result&& other) noexcept : evmc_result{other}
{
other.release = nullptr; // Disable releasing of the rvalue object.
}
/// Move assignment operator.
///
/// The self-assigment MUST never happen.
///
/// @param other The other result object.
/// @return The reference to the left-hand side object.
result& operator=(result&& other) noexcept
{
this->~result(); // Release this object.
static_cast<evmc_result&>(*this) = other; // Copy data.
other.release = nullptr; // Disable releasing of the rvalue object.
return *this;
}
/// Releases the ownership and returns the raw copy of evmc_result.
///
/// This method drops the ownership of the result
/// (result's resources are not going to be released when this object is destructed).
/// It is the caller's responsibility having the returned copy of the result to release it.
/// This object MUST NOT be used after this method is invoked.
///
/// @return The copy of this object converted to raw evmc_result.
evmc_result release_raw() noexcept
{
const auto out = evmc_result{*this}; // Copy data.
this->release = nullptr; // Disable releasing of this object.
return out;
}
};
/// The EVMC Host interface
class HostInterface
{
public:
virtual ~HostInterface() noexcept = default;
/// @copydoc evmc_host_interface::account_exists
virtual bool account_exists(const address& addr) const noexcept = 0;
/// @copydoc evmc_host_interface::get_storage
virtual bytes32 get_storage(const address& addr, const bytes32& key) const noexcept = 0;
/// @copydoc evmc_host_interface::set_storage
virtual evmc_storage_status set_storage(const address& addr,
const bytes32& key,
const bytes32& value) noexcept = 0;
/// @copydoc evmc_host_interface::get_balance
virtual uint256be get_balance(const address& addr) const noexcept = 0;
/// @copydoc evmc_host_interface::get_code_size
virtual size_t get_code_size(const address& addr) const noexcept = 0;
/// @copydoc evmc_host_interface::get_code_hash
virtual bytes32 get_code_hash(const address& addr) const noexcept = 0;
/// @copydoc evmc_host_interface::copy_code
virtual size_t copy_code(const address& addr,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) const noexcept = 0;
/// @copydoc evmc_host_interface::selfdestruct
virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
/// @copydoc evmc_host_interface::call
virtual result call(const evmc_message& msg) noexcept = 0;
/// @copydoc evmc_host_interface::get_tx_context
virtual evmc_tx_context get_tx_context() const noexcept = 0;
/// @copydoc evmc_host_interface::get_block_hash
virtual bytes32 get_block_hash(int64_t block_number) const noexcept = 0;
/// @copydoc evmc_host_interface::emit_log
virtual void emit_log(const address& addr,
const uint8_t* data,
size_t data_size,
const bytes32 topics[],
size_t num_topics) noexcept = 0;
};
/// Wrapper around EVMC host context / host interface.
///
/// To be used by VM implementations as better alternative to using ::evmc_host_context directly.
class HostContext : public HostInterface
{
const evmc_host_interface* host = nullptr;
evmc_host_context* context = nullptr;
mutable evmc_tx_context tx_context = {};
public:
/// Default constructor for null Host context.
HostContext() = default;
/// Constructor from the EVMC Host primitives.
/// @param interface The reference to the Host interface.
/// @param ctx The pointer to the Host context object. This parameter MAY be null.
HostContext(const evmc_host_interface& interface, evmc_host_context* ctx) noexcept
: host{&interface}, context{ctx}
{}
bool account_exists(const address& address) const noexcept final
{
return host->account_exists(context, &address);
}
bytes32 get_storage(const address& address, const bytes32& key) const noexcept final
{
return host->get_storage(context, &address, &key);
}
evmc_storage_status set_storage(const address& address,
const bytes32& key,
const bytes32& value) noexcept final
{
return host->set_storage(context, &address, &key, &value);
}
uint256be get_balance(const address& address) const noexcept final
{
return host->get_balance(context, &address);
}
size_t get_code_size(const address& address) const noexcept final
{
return host->get_code_size(context, &address);
}
bytes32 get_code_hash(const address& address) const noexcept final
{
return host->get_code_hash(context, &address);
}
size_t copy_code(const address& address,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) const noexcept final
{
return host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
}
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
{
host->selfdestruct(context, &addr, &beneficiary);
}
result call(const evmc_message& message) noexcept final
{
return result{host->call(context, &message)};
}
/// @copydoc HostInterface::get_tx_context()
///
/// The implementation caches the received transaction context
/// by assuming that the block timestamp should never be zero.
///
/// @return The cached transaction context.
evmc_tx_context get_tx_context() const noexcept final
{
if (tx_context.block_timestamp == 0)
tx_context = host->get_tx_context(context);
return tx_context;
}
bytes32 get_block_hash(int64_t number) const noexcept final
{
return host->get_block_hash(context, number);
}
void emit_log(const address& addr,
const uint8_t* data,
size_t data_size,
const bytes32 topics[],
size_t topics_count) noexcept final
{
host->emit_log(context, &addr, data, data_size, topics, topics_count);
}
};
/// Abstract class to be used by Host implementations.
///
/// When implementing EVMC Host, you can directly inherit from the evmc::Host class.
/// This way your implementation will be simpler by avoiding manual handling
/// of the ::evmc_host_context and the ::evmc_host_interface.
class Host : public HostInterface
{
public:
/// Provides access to the global host interface.
/// @returns Reference to the host interface object.
static const evmc_host_interface& get_interface() noexcept;
/// Converts the Host object to the opaque host context pointer.
/// @returns Pointer to evmc_host_context.
evmc_host_context* to_context() noexcept { return reinterpret_cast<evmc_host_context*>(this); }
/// Converts the opaque host context pointer back to the original Host object.
/// @tparam DerivedClass The class derived from the Host class.
/// @param context The opaque host context pointer.
/// @returns The pointer to DerivedClass.
template <typename DerivedClass = Host>
static DerivedClass* from_context(evmc_host_context* context) noexcept
{
// Get pointer of the Host base class.
auto* h = reinterpret_cast<Host*>(context);
// Additional downcast, only possible if DerivedClass inherits from Host.
return static_cast<DerivedClass*>(h);
}
};
/// @copybrief evmc_vm
///
/// This is a RAII wrapper for evmc_vm, and object of this type
/// automatically destroys the VM instance.
class VM
{
public:
VM() noexcept = default;
/// Converting constructor from evmc_vm.
explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {}
/// Destructor responsible for automatically destroying the VM instance.
~VM() noexcept
{
if (m_instance != nullptr)
m_instance->destroy(m_instance);
}
VM(const VM&) = delete;
VM& operator=(const VM&) = delete;
/// Move constructor.
VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
/// Move assignment operator.
VM& operator=(VM&& other) noexcept
{
this->~VM();
m_instance = other.m_instance;
other.m_instance = nullptr;
return *this;
}
/// The constructor that captures a VM instance and configures the instance
/// with the provided list of options.
inline VM(evmc_vm* vm,
std::initializer_list<std::pair<const char*, const char*>> options) noexcept;
/// Checks if contains a valid pointer to the VM instance.
explicit operator bool() const noexcept { return m_instance != nullptr; }
/// Checks whenever the VM instance is ABI compatible with the current EVMC API.
bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; }
/// @copydoc evmc_vm::name
char const* name() const noexcept { return m_instance->name; }
/// @copydoc evmc_vm::version
char const* version() const noexcept { return m_instance->version; }
/// Checks if the VM has the given capability.
bool has_capability(evmc_capabilities capability) const noexcept
{
return (get_capabilities() & static_cast<evmc_capabilities_flagset>(capability)) != 0;
}
/// @copydoc evmc::vm::get_capabilities
evmc_capabilities_flagset get_capabilities() const noexcept
{
return m_instance->get_capabilities(m_instance);
}
/// @copydoc evmc_set_option()
evmc_set_option_result set_option(const char name[], const char value[]) noexcept
{
return evmc_set_option(m_instance, name, value);
}
/// @copydoc evmc_execute()
result execute(const evmc_host_interface& host,
evmc_host_context* ctx,
evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)};
}
/// Convenient variant of the VM::execute() that takes reference to evmc::Host class.
result execute(Host& host,
evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size);
}
/// Executes code without the Host context.
///
/// The same as
/// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision,
/// const evmc_message&, const uint8_t*, size_t),
/// but without providing the Host context and interface.
/// This method is for experimental precompiles support where execution is
/// guaranteed not to require any Host access.
result execute(evmc_revision rev,
const evmc_message& msg,
const uint8_t* code,
size_t code_size) noexcept
{
return result{
m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)};
}
private:
evmc_vm* m_instance = nullptr;
};
inline VM::VM(evmc_vm* vm,
std::initializer_list<std::pair<const char*, const char*>> options) noexcept
: m_instance{vm}
{
// This constructor is implemented outside of the class definition to workaround a doxygen bug.
for (const auto& option : options)
set_option(option.first, option.second);
}
namespace internal
{
inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->account_exists(*addr);
}
inline evmc_bytes32 get_storage(evmc_host_context* h,
const evmc_address* addr,
const evmc_bytes32* key) noexcept
{
return Host::from_context(h)->get_storage(*addr, *key);
}
inline evmc_storage_status set_storage(evmc_host_context* h,
const evmc_address* addr,
const evmc_bytes32* key,
const evmc_bytes32* value) noexcept
{
return Host::from_context(h)->set_storage(*addr, *key, *value);
}
inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->get_balance(*addr);
}
inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->get_code_size(*addr);
}
inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept
{
return Host::from_context(h)->get_code_hash(*addr);
}
inline size_t copy_code(evmc_host_context* h,
const evmc_address* addr,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) noexcept
{
return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size);
}
inline void selfdestruct(evmc_host_context* h,
const evmc_address* addr,
const evmc_address* beneficiary) noexcept
{
Host::from_context(h)->selfdestruct(*addr, *beneficiary);
}
inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept
{
return Host::from_context(h)->call(*msg).release_raw();
}
inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept
{
return Host::from_context(h)->get_tx_context();
}
inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept
{
return Host::from_context(h)->get_block_hash(block_number);
}
inline void emit_log(evmc_host_context* h,
const evmc_address* addr,
const uint8_t* data,
size_t data_size,
const evmc_bytes32 topics[],
size_t num_topics) noexcept
{
Host::from_context(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
num_topics);
}
} // namespace internal
inline const evmc_host_interface& Host::get_interface() noexcept
{
static constexpr evmc_host_interface interface{
::evmc::internal::account_exists, ::evmc::internal::get_storage,
::evmc::internal::set_storage, ::evmc::internal::get_balance,
::evmc::internal::get_code_size, ::evmc::internal::get_code_hash,
::evmc::internal::copy_code, ::evmc::internal::selfdestruct,
::evmc::internal::call, ::evmc::internal::get_tx_context,
::evmc::internal::get_block_hash, ::evmc::internal::emit_log};
return interface;
}
} // namespace evmc
namespace std
{
/// Hash operator template specialization for evmc::address. Needed for unordered containers.
template <>
struct hash<evmc::address>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::address& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(fnv1a_by64(
fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])), load64be(&s.bytes[8])),
load32be(&s.bytes[16])));
}
};
/// Hash operator template specialization for evmc::bytes32. Needed for unordered containers.
template <>
struct hash<evmc::bytes32>
{
/// Hash operator using FNV1a-based folding.
constexpr size_t operator()(const evmc::bytes32& s) const noexcept
{
using namespace evmc;
using namespace fnv;
return static_cast<size_t>(
fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])),
load64be(&s.bytes[8])),
load64be(&s.bytes[16])),
load64be(&s.bytes[24])));
}
};
} // namespace std

View File

@ -1,186 +0,0 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2016-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
/// @file
/// Example implementation of an EVMC Host.
#include "evmc.hpp"
#include <algorithm>
#include <map>
#include <vector>
using namespace evmc::literals;
namespace evmc
{
struct account
{
evmc::uint256be balance = {};
std::vector<uint8_t> code;
std::map<evmc::bytes32, evmc::bytes32> storage;
virtual evmc::bytes32 code_hash() const
{
// Extremely dumb "hash" function.
evmc::bytes32 ret{};
for (std::vector<uint8_t>::size_type i = 0; i != code.size(); i++)
{
auto v = code[i];
ret.bytes[v % sizeof(ret.bytes)] ^= v;
}
return ret;
}
};
using accounts = std::map<evmc::address, account>;
} // namespace evmc
class ExampleHost : public evmc::Host
{
evmc::accounts accounts;
evmc_tx_context tx_context{};
public:
ExampleHost() = default;
explicit ExampleHost(evmc_tx_context& _tx_context) noexcept : tx_context{_tx_context} {};
ExampleHost(evmc_tx_context& _tx_context, evmc::accounts& _accounts) noexcept
: accounts{_accounts}, tx_context{_tx_context} {};
bool account_exists(const evmc::address& addr) const noexcept final
{
return accounts.find(addr) != accounts.end();
}
evmc::bytes32 get_storage(const evmc::address& addr, const evmc::bytes32& key) const
noexcept final
{
const auto account_iter = accounts.find(addr);
if (account_iter == accounts.end())
return {};
const auto storage_iter = account_iter->second.storage.find(key);
if (storage_iter != account_iter->second.storage.end())
return storage_iter->second;
return {};
}
evmc_storage_status set_storage(const evmc::address& addr,
const evmc::bytes32& key,
const evmc::bytes32& value) noexcept final
{
auto& account = accounts[addr];
auto prev_value = account.storage[key];
account.storage[key] = value;
return (prev_value == value) ? EVMC_STORAGE_UNCHANGED : EVMC_STORAGE_MODIFIED;
}
evmc::uint256be get_balance(const evmc::address& addr) const noexcept final
{
auto it = accounts.find(addr);
if (it != accounts.end())
return it->second.balance;
return {};
}
size_t get_code_size(const evmc::address& addr) const noexcept final
{
auto it = accounts.find(addr);
if (it != accounts.end())
return it->second.code.size();
return 0;
}
evmc::bytes32 get_code_hash(const evmc::address& addr) const noexcept final
{
auto it = accounts.find(addr);
if (it != accounts.end())
return it->second.code_hash();
return {};
}
size_t copy_code(const evmc::address& addr,
size_t code_offset,
uint8_t* buffer_data,
size_t buffer_size) const noexcept final
{
const auto it = accounts.find(addr);
if (it == accounts.end())
return 0;
const auto& code = it->second.code;
if (code_offset >= code.size())
return 0;
const auto n = std::min(buffer_size, code.size() - code_offset);
if (n > 0)
std::copy_n(&code[code_offset], n, buffer_data);
return n;
}
void selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept final
{
(void)addr;
(void)beneficiary;
}
evmc::result call(const evmc_message& msg) noexcept final
{
return {EVMC_REVERT, msg.gas, msg.input_data, msg.input_size};
}
evmc_tx_context get_tx_context() const noexcept final { return tx_context; }
evmc::bytes32 get_block_hash(int64_t number) const noexcept final
{
const int64_t current_block_number = get_tx_context().block_number;
return (number < current_block_number && number >= current_block_number - 256) ?
0xb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5f_bytes32 :
0_bytes32;
}
void emit_log(const evmc::address& addr,
const uint8_t* data,
size_t data_size,
const evmc::bytes32 topics[],
size_t topics_count) noexcept final
{
(void)addr;
(void)data;
(void)data_size;
(void)topics;
(void)topics_count;
}
};
extern "C" {
const evmc_host_interface* example_host_get_interface()
{
return &evmc::Host::get_interface();
}
evmc_host_context* example_host_create_context(evmc_tx_context &tx_context)
{
evmc::accounts accounts;
evmc::account acc;
evmc_address addr = {{0, 1, 2}};
acc.balance = {{1, 0}};
acc.code = {10, 11, 12, 13, 14, 15};
accounts[addr] = acc;
auto host = new ExampleHost{tx_context, accounts};
return host->to_context();
}
void example_host_destroy_context(evmc_host_context* context)
{
delete evmc::Host::from_context<ExampleHost>(context);
}
}

View File

@ -1,235 +0,0 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2016-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
/// @file
/// Example implementation of the EVMC VM interface.
///
/// This VM does not do anything useful except for showing
/// how EVMC VM API should be implemented.
/// The implementation is done in C only, but could be done in C++ in very
/// similar way.
#include "evmc.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C" {
/// The example VM instance struct extending the evmc_vm.
struct example_vm
{
struct evmc_vm instance; ///< The base struct.
int verbose; ///< The verbosity level.
};
/// The implementation of the evmc_vm::destroy() method.
static void destroy(struct evmc_vm* vm)
{
free(vm);
}
/// The example implementation of the evmc_vm::get_capabilities() method.
static evmc_capabilities_flagset get_capabilities(struct evmc_vm* vm)
{
(void)vm;
return EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM;
}
/// Example VM options.
///
/// The implementation of the evmc_vm::set_option() method.
/// VMs are allowed to omit this method implementation.
static enum evmc_set_option_result set_option(struct evmc_vm* instance,
const char* name,
const char* value)
{
struct example_vm* vm = (struct example_vm*)instance;
if (strcmp(name, "verbose") == 0)
{
if (!value)
return EVMC_SET_OPTION_INVALID_VALUE;
char* end = NULL;
long int v = strtol(value, &end, 0);
if (end == value) // Parsing the value failed.
return EVMC_SET_OPTION_INVALID_VALUE;
if (v > 9 || v < -1) // Not in the valid range.
return EVMC_SET_OPTION_INVALID_VALUE;
vm->verbose = (int)v;
return EVMC_SET_OPTION_SUCCESS;
}
return EVMC_SET_OPTION_INVALID_NAME;
}
/// The implementation of the evmc_result::release() method that frees
/// the output buffer attached to the result object.
static void free_result_output_data(const struct evmc_result* result)
{
free((uint8_t*)result->output_data);
}
/// The example implementation of the evmc_vm::execute() method.
static struct evmc_result execute(struct evmc_vm* instance,
const struct evmc_host_interface* host,
struct evmc_host_context* context,
enum evmc_revision rev,
const struct evmc_message* msg,
const uint8_t* code,
size_t code_size)
{
struct evmc_result ret = {.status_code = EVMC_INTERNAL_ERROR};
if (code_size == 0)
{
// In case of empty code return a fancy error message.
const char* error = rev == EVMC_BYZANTIUM ? "Welcome to Byzantium!" : "Hello Ethereum!";
ret.output_data = (const uint8_t*)error;
ret.output_size = strlen(error);
ret.status_code = EVMC_FAILURE;
ret.gas_left = msg->gas / 10;
ret.release = NULL; // We don't need to release the constant messages.
return ret;
}
struct example_vm* vm = (struct example_vm*)instance;
// Simulate executing by checking for some code patterns.
// Solidity inline assembly is used in the examples instead of EVM bytecode.
// Assembly: `{ mstore(0, address()) return(0, msize()) }`.
const char return_address[] = "\x30\x60\x00\x52\x59\x60\x00\xf3";
// Assembly: `{ sstore(0, add(sload(0), 1)) }`
const char counter[] = "\x60\x01\x60\x00\x54\x01\x60\x00\x55";
// Assembly: `{ mstore(0, number()) return(0, msize()) }`
const char return_block_number[] = "\x43\x60\x00\x52\x59\x60\x00\xf3";
// Assembly: `{ sstore(0, number()) mstore(0, number()) return(0, msize()) }`
const char save_return_block_number[] = "\x43\x60\x00\x55\x43\x60\x00\x52\x59\x60\x00\xf3";
// Assembly: PUSH(0) 6x DUP1 CALL
const char make_a_call[] = "\x60\x00\x80\x80\x80\x80\x80\x80\xf1";
if (msg->kind == EVMC_CREATE)
{
ret.status_code = EVMC_SUCCESS;
ret.gas_left = msg->gas / 10;
return ret;
}
else if (code_size == (sizeof(return_address) - 1) &&
strncmp((const char*)code, return_address, code_size) == 0)
{
static const size_t address_size = sizeof(msg->destination);
uint8_t* output_data = (uint8_t*)malloc(address_size);
if (!output_data)
{
// malloc failed, report internal error.
ret.status_code = EVMC_INTERNAL_ERROR;
return ret;
}
memcpy(output_data, &msg->destination, address_size);
ret.status_code = EVMC_SUCCESS;
ret.output_data = output_data;
ret.output_size = address_size;
ret.release = &free_result_output_data;
return ret;
}
else if (code_size == (sizeof(counter) - 1) &&
strncmp((const char*)code, counter, code_size) == 0)
{
const evmc_bytes32 key = {{0}};
evmc_bytes32 value = host->get_storage(context, &msg->destination, &key);
value.bytes[31]++;
host->set_storage(context, &msg->destination, &key, &value);
ret.status_code = EVMC_SUCCESS;
return ret;
}
else if (code_size == (sizeof(return_block_number) - 1) &&
strncmp((const char*)code, return_block_number, code_size) == 0)
{
const struct evmc_tx_context tx_context = host->get_tx_context(context);
const size_t output_size = 20;
uint8_t* output_data = (uint8_t*)calloc(1, output_size);
snprintf((char*)output_data, output_size, "%u", (unsigned)tx_context.block_number);
ret.status_code = EVMC_SUCCESS;
ret.gas_left = msg->gas / 2;
ret.output_data = output_data;
ret.output_size = output_size;
ret.release = &free_result_output_data;
return ret;
}
else if (code_size == (sizeof(save_return_block_number) - 1) &&
strncmp((const char*)code, save_return_block_number, code_size) == 0)
{
const struct evmc_tx_context tx_context = host->get_tx_context(context);
const size_t output_size = 20;
// Store block number.
const evmc_bytes32 key = {{0}};
evmc_bytes32 value = {{0}};
// NOTE: assume block number is <= 255
value.bytes[31] = (uint8_t)tx_context.block_number;
host->set_storage(context, &msg->destination, &key, &value);
// Return block number.
uint8_t* output_data = (uint8_t*)calloc(1, output_size);
snprintf((char*)output_data, output_size, "%u", (unsigned)tx_context.block_number);
ret.status_code = EVMC_SUCCESS;
ret.gas_left = msg->gas / 2;
ret.output_data = output_data;
ret.output_size = output_size;
ret.release = &free_result_output_data;
return ret;
}
else if (code_size == (sizeof(make_a_call) - 1) &&
strncmp((const char*)code, make_a_call, code_size) == 0)
{
struct evmc_message call_msg;
memset(&call_msg, 0, sizeof(call_msg));
call_msg.kind = EVMC_CALL;
call_msg.depth = msg->depth + 1;
call_msg.gas = msg->gas - (msg->gas / 64);
call_msg.sender = msg->destination;
return host->call(context, &call_msg);
}
ret.status_code = EVMC_FAILURE;
ret.gas_left = 0;
if (vm->verbose)
printf("Execution done.\n");
return ret;
}
/// @cond internal
#if !defined(PROJECT_VERSION)
/// The dummy project version if not provided by the build system.
#define PROJECT_VERSION "0.0.0"
#endif
/// @endcond
struct evmc_vm* evmc_create_example_vm()
{
struct evmc_vm init = {
.abi_version = EVMC_ABI_VERSION,
.name = "example_vm",
.version = PROJECT_VERSION,
.destroy = destroy,
.execute = execute,
.get_capabilities = get_capabilities,
.set_option = set_option,
};
struct example_vm* vm = (example_vm*)calloc(1, sizeof(struct example_vm));
struct evmc_vm* interface = &vm->instance;
memcpy(interface, &init, sizeof(init));
return interface;
}
}

View File

@ -1,212 +0,0 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018-2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/
/**
* EVMC Helpers
*
* A collection of C helper functions for invoking a VM instance methods.
* These are convenient for languages where invoking function pointers
* is "ugly" or impossible (such as Go).
*
* It also contains helpers (overloaded operators) for using EVMC types effectively in C++.
*
* @defgroup helpers EVMC Helpers
* @{
*/
#pragma once
#include "evmc.h"
#include <stdlib.h>
#include <string.h>
/**
* Returns true if the VM has a compatible ABI version.
*/
static inline bool evmc_is_abi_compatible(struct evmc_vm* vm)
{
return vm->abi_version == EVMC_ABI_VERSION;
}
/**
* Returns the name of the VM.
*/
static inline const char* evmc_vm_name(struct evmc_vm* vm)
{
return vm->name;
}
/**
* Returns the version of the VM.
*/
static inline const char* evmc_vm_version(struct evmc_vm* vm)
{
return vm->version;
}
/**
* Checks if the VM has the given capability.
*
* @see evmc_get_capabilities_fn
*/
static inline bool evmc_vm_has_capability(struct evmc_vm* vm, enum evmc_capabilities capability)
{
return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0;
}
/**
* Destroys the VM instance.
*
* @see evmc_destroy_fn
*/
static inline void evmc_destroy(struct evmc_vm* vm)
{
vm->destroy(vm);
}
/**
* Sets the option for the VM, if the feature is supported by the VM.
*
* @see evmc_set_option_fn
*/
static inline enum evmc_set_option_result evmc_set_option(struct evmc_vm* vm,
char const* name,
char const* value)
{
if (vm->set_option)
return vm->set_option(vm, name, value);
return EVMC_SET_OPTION_INVALID_NAME;
}
/**
* Executes code in the VM instance.
*
* @see evmc_execute_fn.
*/
static inline struct evmc_result evmc_execute(struct evmc_vm* vm,
const struct evmc_host_interface* host,
struct evmc_host_context* context,
enum evmc_revision rev,
const struct evmc_message* msg,
uint8_t const* code,
size_t code_size)
{
return vm->execute(vm, host, context, rev, msg, code, code_size);
}
/// The evmc_result release function using free() for releasing the memory.
///
/// This function is used in the evmc_make_result(),
/// but may be also used in other case if convenient.
///
/// @param result The result object.
static void evmc_free_result_memory(const struct evmc_result* result)
{
free((uint8_t*)result->output_data);
}
/// Creates the result from the provided arguments.
///
/// The provided output is copied to memory allocated with malloc()
/// and the evmc_result::release function is set to one invoking free().
///
/// In case of memory allocation failure, the result has all fields zeroed
/// and only evmc_result::status_code is set to ::EVMC_OUT_OF_MEMORY internal error.
///
/// @param status_code The status code.
/// @param gas_left The amount of gas left.
/// @param output_data The pointer to the output.
/// @param output_size The output size.
static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code,
int64_t gas_left,
const uint8_t* output_data,
size_t output_size)
{
struct evmc_result result;
memset(&result, 0, sizeof(result));
if (output_size != 0)
{
uint8_t* buffer = (uint8_t*)malloc(output_size);
if (!buffer)
{
result.status_code = EVMC_OUT_OF_MEMORY;
return result;
}
memcpy(buffer, output_data, output_size);
result.output_data = buffer;
result.output_size = output_size;
result.release = evmc_free_result_memory;
}
result.status_code = status_code;
result.gas_left = gas_left;
return result;
}
/**
* Releases the resources allocated to the execution result.
*
* @param result The result object to be released. MUST NOT be NULL.
*
* @see evmc_result::release() evmc_release_result_fn
*/
static inline void evmc_release_result(struct evmc_result* result)
{
if (result->release)
result->release(result);
}
/**
* Helpers for optional storage of evmc_result.
*
* In some contexts (i.e. evmc_result::create_address is unused) objects of
* type evmc_result contains a memory storage that MAY be used by the object
* owner. This group defines helper types and functions for accessing
* the optional storage.
*
* @defgroup result_optional_storage Result Optional Storage
* @{
*/
/**
* The union representing evmc_result "optional storage".
*
* The evmc_result struct contains 24 bytes of optional storage that can be
* reused by the object creator if the object does not contain
* evmc_result::create_address.
*
* A VM implementation MAY use this memory to keep additional data
* when returning result from evmc_execute_fn().
* The host application MAY use this memory to keep additional data
* when returning result of performed calls from evmc_call_fn().
*
* @see evmc_get_optional_storage(), evmc_get_const_optional_storage().
*/
union evmc_result_optional_storage
{
uint8_t bytes[24]; /**< 24 bytes of optional storage. */
void* pointer; /**< Optional pointer. */
};
/** Provides read-write access to evmc_result "optional storage". */
static inline union evmc_result_optional_storage* evmc_get_optional_storage(
struct evmc_result* result)
{
return (union evmc_result_optional_storage*)&result->create_address;
}
/** Provides read-only access to evmc_result "optional storage". */
static inline const union evmc_result_optional_storage* evmc_get_const_optional_storage(
const struct evmc_result* result)
{
return (const union evmc_result_optional_storage*)&result->create_address;
}
/** @} */
/** @} */