mirror of
https://github.com/status-im/evmc.git
synced 2025-02-23 08:28:15 +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
|
mode = ScriptMode.Verbose
|
||||||
|
|
||||||
packageName = "evmc"
|
packageName = "evmc"
|
||||||
@ -14,6 +19,9 @@ proc test(name: string, lang: string = "cpp") =
|
|||||||
if not dirExists "build":
|
if not dirExists "build":
|
||||||
mkDir "build"
|
mkDir "build"
|
||||||
--run
|
--run
|
||||||
|
--forceBuild
|
||||||
|
--verbosity:0
|
||||||
|
--hints:off
|
||||||
switch("out", ("./build/" & name))
|
switch("out", ("./build/" & name))
|
||||||
setCommand lang, "tests/" & name & ".nim"
|
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
|
type
|
||||||
HostContext* = object
|
HostContext* = object
|
||||||
host: ptr evmc_host_interface
|
host: ptr evmc_host_interface
|
||||||
context: evmc_host_context
|
context: ptr evmc_host_context
|
||||||
|
|
||||||
EvmcVM* = object
|
EvmcVM* = object
|
||||||
vm: ptr evmc_vm
|
vm: ptr evmc_vm
|
||||||
hc: HostContext
|
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.host = host
|
||||||
x.context = context
|
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)
|
result.init(host, context)
|
||||||
|
|
||||||
proc getTxContext*(ctx: HostContext): evmc_tx_context =
|
proc getTxContext*(ctx: HostContext): evmc_tx_context =
|
||||||
@ -79,7 +84,7 @@ proc name*(vm: EvmcVM): string =
|
|||||||
proc version*(vm: EvmcVM): string =
|
proc version*(vm: EvmcVM): string =
|
||||||
$vm.vm.version
|
$vm.vm.version
|
||||||
|
|
||||||
proc getCapabilities*(vm: EvmcVM): evmc_capabilities =
|
proc getCapabilities*(vm: EvmcVM): evmc_capabilities_flagset =
|
||||||
vm.vm.get_capabilities(vm.vm)
|
vm.vm.get_capabilities(vm.vm)
|
||||||
|
|
||||||
proc setOption*(vm: EvmcVM, name, value: string): evmc_set_option_result =
|
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
|
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)
|
vm.vm.execute(vm.vm, vm.hc.host, vm.hc.context, rev, msg, code[0].unsafeAddr, code.len.uint)
|
||||||
|
|
||||||
proc destroy*(vm: EvmcVM) =
|
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
|
# Copyright (c) 2018-2020 Status Research & Development GmbH
|
||||||
import ../../evmc/evmc
|
# Licensed under the Apache License, Version 2.0.
|
||||||
import stew/byteutils
|
# This file may not be copied, modified, or distributed except according to
|
||||||
|
# those terms.
|
||||||
|
|
||||||
|
import tables, hashes, strutils,
|
||||||
|
stew/byteutils,
|
||||||
|
../evmc/evmc
|
||||||
|
|
||||||
type
|
type
|
||||||
Account = ref object
|
Account = ref object
|
||||||
@ -12,6 +17,24 @@ type
|
|||||||
tx_context: evmc_tx_context
|
tx_context: evmc_tx_context
|
||||||
accounts: Table[evmc_address, Account]
|
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 =
|
proc hash*(x: evmc_bytes32): Hash =
|
||||||
result = hash(x.bytes)
|
result = hash(x.bytes)
|
||||||
|
|
||||||
@ -24,9 +47,12 @@ proc codeHash(acc: Account): evmc_bytes32 =
|
|||||||
let idx = v.int mod sizeof(result.bytes)
|
let idx = v.int mod sizeof(result.bytes)
|
||||||
result.bytes[idx] = result.bytes[idx] xor v
|
result.bytes[idx] = result.bytes[idx] xor v
|
||||||
|
|
||||||
proc evmcReleaseResultImpl(result: var evmc_result) {.cdecl.} =
|
proc evmcReleaseResultImpl(result: ptr evmc_result) {.cdecl.} =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
converter toEVMCHostContext*(ctx: HostContext): ptr evmc_host_context =
|
||||||
|
cast[ptr evmc_host_context](ctx)
|
||||||
|
|
||||||
proc evmcGetTxContextImpl(ctx: HostContext): evmc_tx_context {.cdecl.} =
|
proc evmcGetTxContextImpl(ctx: HostContext): evmc_tx_context {.cdecl.} =
|
||||||
ctx.tx_context
|
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:
|
if number < current_block_number and number >= current_block_number - 256:
|
||||||
result.bytes = hash
|
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
|
address in ctx.accounts
|
||||||
|
|
||||||
proc evmcGetStorageImpl(ctx: HostContext, address: var evmc_address, key: var evmc_bytes32): evmc_bytes32 {.cdecl.} =
|
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,
|
proc evmcExecuteImpl(vm: ptr evmc_vm, host: ptr evmc_host_interface,
|
||||||
ctx: HostContext, rev: evmc_revision,
|
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"
|
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):
|
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.status_code = EVMC_FAILURE
|
||||||
result.gas_left = 0
|
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_EVM1)
|
||||||
result.incl(EVMC_CAPABILITY_EWASM)
|
result.incl(EVMC_CAPABILITY_EWASM)
|
||||||
|
|
||||||
@ -143,67 +169,38 @@ proc evmcDestroyImpl(vm: ptr evmc_vm) {.cdecl.} =
|
|||||||
dealloc(vm)
|
dealloc(vm)
|
||||||
|
|
||||||
proc init_host_interface(): evmc_host_interface =
|
proc init_host_interface(): evmc_host_interface =
|
||||||
# workaround for nim cpp codegen bug
|
result.account_exists = cast[evmc_account_exists_fn](evmcAccountExistsImpl)
|
||||||
{.emit: [result.account_exists, " = (", evmc_account_exists_fn, ")", evmcAccountExistsImpl, ";" ].}
|
result.get_storage = cast[evmc_get_storage_fn](evmcGetStorageImpl)
|
||||||
{.emit: [result.get_storage, " = (", evmc_get_storage_fn, ")", evmcGetStorageImpl, ";" ].}
|
result.set_storage = cast[evmc_set_storage_fn](evmcSetStorageImpl)
|
||||||
{.emit: [result.set_storage, " = (", evmc_set_storage_fn, ")", evmcSetStorageImpl, ";" ].}
|
result.get_balance = cast[evmc_get_balance_fn](evmcGetBalanceImpl)
|
||||||
{.emit: [result.get_balance, " = (", evmc_get_balance_fn, ")", evmcGetBalanceImpl, ";" ].}
|
result.get_code_size = cast[evmc_get_code_size_fn](evmcGetCodeSizeImpl)
|
||||||
{.emit: [result.get_code_size, " = (", evmc_get_code_size_fn, ")", evmcGetCodeSizeImpl, ";" ].}
|
result.get_code_hash = cast[evmc_get_code_hash_fn](evmcGetCodeHashImpl)
|
||||||
{.emit: [result.get_code_hash, " = (", evmc_get_code_hash_fn, ")", evmcGetCodeHashImpl, ";" ].}
|
result.copy_code = cast[evmc_copy_code_fn](evmcCopyCodeImpl)
|
||||||
{.emit: [result.copy_code, " = (", evmc_copy_code_fn, ")", evmcCopyCodeImpl, ";" ].}
|
result.selfdestruct = cast[evmc_selfdestruct_fn](evmcSelfdestructImpl)
|
||||||
{.emit: [result.selfdestruct, " = (", evmc_selfdestruct_fn, ")", evmcSelfdestructImpl, ";" ].}
|
result.call = cast[evmc_call_fn](evmcCallImpl)
|
||||||
{.emit: [result.call, " = (", evmc_call_fn, ")", evmcCallImpl, ";" ].}
|
result.get_tx_context = cast[evmc_get_tx_context_fn](evmcGetTxContextImpl)
|
||||||
{.emit: [result.get_tx_context, " = (", evmc_get_tx_context_fn, ")", evmcGetTxContextImpl, ";" ].}
|
result.get_block_hash = cast[evmc_get_block_hash_fn](evmcGetBlockHashImpl)
|
||||||
{.emit: [result.get_block_hash, " = (", evmc_get_block_hash_fn, ")", evmcGetBlockHashImpl, ";" ].}
|
result.emit_log = cast[evmc_emit_log_fn](evmcEmitLogImpl)
|
||||||
{.emit: [result.emit_log, " = (", evmc_emit_log_fn, ")", evmcEmitLogImpl, ";" ].}
|
|
||||||
|
|
||||||
#result.account_exists = cast[evmc_account_exists_fn](evmcAccountExistsImpl)
|
proc init(vm: ptr evmc_vm) {.exportc, cdecl.} =
|
||||||
#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
|
|
||||||
vm.destroy = evmcDestroyImpl
|
vm.destroy = evmcDestroyImpl
|
||||||
|
vm.execute = cast[evmc_execute_fn](evmcExecuteImpl)
|
||||||
{.emit: [vm.execute, " = (", evmc_execute_fn, ")", evmcExecuteImpl, ";" ].}
|
|
||||||
#vm.execute = cast[evmc_execute_fn](evmcExecuteImpl)
|
|
||||||
|
|
||||||
vm.get_capabilities = evmcGetCapabilitiesImpl
|
vm.get_capabilities = evmcGetCapabilitiesImpl
|
||||||
vm.set_option = evmcSetOptionImpl
|
vm.set_option = evmcSetOptionImpl
|
||||||
|
|
||||||
let gHost = init_host_interface()
|
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
|
result = gHost.unsafeAddr
|
||||||
|
|
||||||
proc nim_host_create_context(tx_context: evmc_tx_context): HostContext {.exportc, cdecl.} =
|
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]
|
|
||||||
)
|
|
||||||
|
|
||||||
result = HostContext(tx_context: tx_context)
|
result = HostContext(tx_context: tx_context)
|
||||||
result.accounts[address] = acc
|
|
||||||
GC_ref(result)
|
GC_ref(result)
|
||||||
|
|
||||||
proc nim_host_destroy_context(ctx: HostContext) {.exportc, cdecl.} =
|
proc nim_host_destroy_context*(ctx: HostContext) {.exportc, cdecl.} =
|
||||||
GC_unref(ctx)
|
GC_unref(ctx)
|
||||||
|
|
||||||
proc nim_create_example_vm(): ptr evmc_vm {.exportc, cdecl.} =
|
proc nim_create_example_vm*(): ptr evmc_vm {.exportc, cdecl.} =
|
||||||
result = create(evmc_vm)
|
result = cast[ptr evmc_vm](new(evmc_vm))
|
||||||
init(result[])
|
copyMem(result, globalVM.addr, sizeof(globalVM))
|
||||||
|
init(result)
|
||||||
|
|
@ -1,24 +1,24 @@
|
|||||||
import ../evmc/[evmc, evmc_nim], unittest
|
# Copyright (c) 2018-2020 Status Research & Development GmbH
|
||||||
import evmc_nim/nim_host
|
# Licensed under the Apache License, Version 2.0.
|
||||||
import stew/byteutils
|
# This file may not be copied, modified, or distributed except according to
|
||||||
|
# those terms.
|
||||||
|
|
||||||
{.compile: "evmc_c/example_host.cpp".}
|
import os, unittest,
|
||||||
{.compile: "evmc_c/example_vm.cpp".}
|
stew/byteutils,
|
||||||
{.passL: "-lstdc++"}
|
../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):
|
when defined(posix):
|
||||||
{.passC: "-std=c++11".}
|
{.passC: "-std=c++11".}
|
||||||
|
|
||||||
proc example_host_get_interface(): ptr evmc_host_interface {.importc, cdecl.}
|
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_create_context(tx_context: evmc_tx_context): ptr evmc_host_context {.importc, cdecl.}
|
||||||
proc example_host_destroy_context(context: 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 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) =
|
template runTest(testName: string, create_vm, get_host_interface, create_host_context, destroy_host_context: untyped) =
|
||||||
var vm = create_vm()
|
var vm = create_vm()
|
||||||
var host = get_host_interface()
|
var host = get_host_interface()
|
||||||
@ -83,22 +83,6 @@ template runTest(testName: string, create_vm, get_host_interface, create_host_co
|
|||||||
test "accountExists":
|
test "accountExists":
|
||||||
check hc.accountExists(address) == true
|
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":
|
test "selfdestruct":
|
||||||
hc.selfdestruct(address, address)
|
hc.selfdestruct(address, address)
|
||||||
|
|
||||||
@ -137,11 +121,11 @@ template runTest(testName: string, create_vm, get_host_interface, create_host_co
|
|||||||
|
|
||||||
test "execute and destroy":
|
test "execute and destroy":
|
||||||
var bn = $tx_context.block_number
|
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.status_code == EVMC_SUCCESS
|
||||||
check res.gas_left == 100000
|
check res.gas_left == 100000
|
||||||
check equalMem(bn[0].addr, res.output_data, bn.len)
|
check equalMem(bn[0].addr, res.output_data, bn.len)
|
||||||
res.release(res)
|
res.release(res.addr)
|
||||||
|
|
||||||
var empty_key: evmc_bytes32
|
var empty_key: evmc_bytes32
|
||||||
let val = hc.getStorage(address, empty_key)
|
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)
|
destroy_host_context(ctx)
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
runTest("EVMC Nim to C API",
|
runTest("Nim EVMC wrapper: C++ host, C VM",
|
||||||
evmc_create_example_vm,
|
evmc_create_example_vm,
|
||||||
example_host_get_interface,
|
example_host_get_interface,
|
||||||
example_host_create_context,
|
example_host_create_context,
|
||||||
example_host_destroy_context
|
example_host_destroy_context
|
||||||
)
|
)
|
||||||
|
|
||||||
runTest("EVMC Nim to Nim API",
|
runTest("Nim EVMC wrapper: Nim host, Nim VM",
|
||||||
nim_create_example_vm,
|
nim_create_example_vm,
|
||||||
nim_host_get_interface,
|
nim_host_get_interface,
|
||||||
nim_host_create_context,
|
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