mirror of
https://github.com/status-im/evmc.git
synced 2025-02-22 16:08:22 +00:00
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:
parent
2aa39b9f61
commit
ad51a35021
@ -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
6
bindings/nim/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# Nim
|
||||
nimcache/
|
||||
|
||||
# Executables shall be put in an ignored build/ directory
|
||||
build/
|
||||
|
20
bindings/nim/README.md
Normal file
20
bindings/nim/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Nim EVMC wrapper
|
||||
|
||||
[](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)
|
||||
|
@ -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
127
bindings/nim/evmc/evmc.nim
Normal 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
|
@ -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) =
|
57
bindings/nim/evmc/generate_wrapper.sh
Executable file
57
bindings/nim/evmc/generate_wrapper.sh
Executable 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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
695
evmc/evmc.nim
695
evmc/evmc.nim
@ -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.}
|
@ -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.
|
@ -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
|
||||
/** @} */
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @} */
|
Loading…
x
Reference in New Issue
Block a user