implement Nim to C tests

This commit is contained in:
andri lim 2019-12-16 14:12:07 +07:00
parent a0269efd5b
commit 3ba42ffcc9
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
4 changed files with 193 additions and 17 deletions

View File

@ -29,14 +29,14 @@ type
# 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]
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]
bytes*: array[20, byte]
# The kind of call-like instruction.
evmc_call_kind* {.size: sizeof(cint).} = enum
@ -434,7 +434,7 @@ type
# Destroys the VM instance.
#
# @param vm The VM instance to be destroyed.
evmc_destroy_fn* = proc(vm: var evmc_vm) {.cdecl.}
evmc_destroy_fn* = proc(vm: ptr evmc_vm) {.cdecl.}
# Possible outcomes of evmc_set_option.
evmc_set_option_result* {.size: sizeof(cint).} = enum
@ -453,7 +453,7 @@ type
# @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: var evmc_vm, name, value: cstring): evmc_set_option_result {.cdecl.}
evmc_set_option_fn* = proc(vm: ptr evmc_vm, name, value: cstring): evmc_set_option_result {.cdecl.}
# EVM revision.
#
@ -513,7 +513,7 @@ type
# @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: var evmc_vm, host: evmc_host_interface,
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.}
@ -530,7 +530,7 @@ type
#
# @param vm The VM instance.
# @return The supported capabilities of the VM. @see evmc_capabilities.
evmc_get_capabilities_fn* = proc(vm: evmc_vm): evmc_capabilities {.cdecl.}
evmc_get_capabilities_fn* = proc(vm: ptr evmc_vm): evmc_capabilities {.cdecl.}
# The VM instance.
#
@ -688,3 +688,8 @@ proc incl*(a: var evmc_capabilities, b: evmc_capabilities) {.inline.} =
proc excl*(a: var evmc_capabilities, b: evmc_capabilities) {.inline.} =
a = evmc_capabilities(a.uint32 and (not b.uint32))
proc contains*(a, b: evmc_capabilities): bool {.inline.} =
(a.uint32 and b.uint32) != 0
proc `==`*(a, b: evmc_status_code): bool {.borrow.}

View File

@ -2,9 +2,20 @@ import evmc
type
HostContext* = object
host: evmc_host_interface
host: ptr evmc_host_interface
context: evmc_host_context
EvmcVM* = object
vm: ptr evmc_vm
hc: HostContext
proc init*(x: var HostContext, host: ptr evmc_host_interface, context: evmc_host_context) =
x.host = host
x.context = context
proc init*(x: typedesc[HostContext], host: ptr evmc_host_interface, context: evmc_host_context): HostContext =
result.init(host, context)
proc getTxContext*(ctx: HostContext): evmc_tx_context =
ctx.host.get_tx_context(ctx.context)
@ -24,14 +35,21 @@ proc setStorage*(ctx: HostContext, address: evmc_address,
proc getBalance*(ctx: HostContext, address: evmc_address): evmc_uint256be =
ctx.host.get_balance(ctx.context, address)
proc getCodeSize*(ctx: HostContext, address: evmc_address): uint =
ctx.host.get_code_size(ctx.context, address)
proc getCodeSize*(ctx: HostContext, address: evmc_address): int =
ctx.host.get_code_size(ctx.context, address).int
proc getCodeHash*(ctx: HostContext, address: evmc_address): evmc_bytes32 =
ctx.host.get_code_hash(ctx.context, address)
proc copyCode*(ctx: HostContext, address: evmc_address, code_offset: uint, buffer: openArray[byte]): uint =
ctx.host.copy_code(ctx.context, address, code_offset, buffer[0].unsafeAddr, buffer.len.uint)
proc copyCode*(ctx: HostContext, address: evmc_address, codeOffset: int = 0): seq[byte] =
let size = ctx.getCodeSize(address)
if size - codeOffset > 0:
result = newSeq[byte](size - codeOffset)
let read = ctx.host.copy_code(ctx.context, address, code_offset.uint, result[0].addr, result.len.uint).int
doAssert(read == result.len)
proc copyCode*(ctx: HostContext, address: evmc_address, codeOffset: int, output: ptr byte, output_len: int): int =
ctx.host.copy_code(ctx.context, address, code_offset.uint, output, output_len.uint).int
proc selfdestruct*(ctx: HostContext, address, beneficiary: evmc_address) =
ctx.host.selfdestruct(ctx.context, address, beneficiary)
@ -41,3 +59,37 @@ proc emitLog*(ctx: HostContext, address: evmc_address, data: openArray[byte], to
proc call*(ctx: HostContext, msg: evmc_message): evmc_result =
ctx.host.call(ctx.context, msg)
proc init*(x: var EvmcVM, vm: ptr evmc_vm, hc: HostContext) =
x.vm = vm
x.hc = hc
proc init*(x: typedesc[EvmcVM], vm: ptr evmc_vm, hc: HostContext): EvmcVM =
result.init(vm, hc)
proc init*(x: typedesc[EvmcVM], vm: ptr evmc_vm): EvmcVM =
result.init(vm, HostContext())
proc isABICompatible*(vm: EvmcVM): bool =
vm.vm.abi_version == EVMC_ABI_VERSION
proc name*(vm: EvmcVM): string =
$vm.vm.name
proc version*(vm: EvmcVM): string =
$vm.vm.version
proc getCapabilities*(vm: EvmcVM): evmc_capabilities =
vm.vm.get_capabilities(vm.vm)
proc setOption*(vm: EvmcVM, name, value: string): evmc_set_option_result =
if not vm.vm.set_option.isNil:
return vm.vm.set_option(vm.vm, name, value)
result = EVMC_SET_OPTION_INVALID_NAME
proc execute*(vm: EvmcVM, rev: evmc_revision, msg: evmc_message, code: openArray[byte]): evmc_result =
vm.vm.execute(vm.vm, vm.hc.host, vm.hc.context, rev, msg, code[0].unsafeAddr, code.len.uint)
proc destroy*(vm: EvmcVM) =
vm.vm.destroy(vm.vm)

View File

@ -159,7 +159,6 @@ public:
}
};
extern "C" {
const evmc_host_interface* example_host_get_interface()
@ -169,7 +168,14 @@ const evmc_host_interface* example_host_get_interface()
evmc_host_context* example_host_create_context(evmc_tx_context tx_context)
{
auto host = new ExampleHost{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();
}

View File

@ -1,17 +1,130 @@
import ../evmc/[evmc, evmc_nim]
import ../evmc/[evmc, evmc_nim], unittest
import stew/byteutils
{.compile: "evmc_c/example_host.cpp".}
{.compile: "evmc_c/example_vm.c".}
{.passL: "-lstdc++"}
proc example_host_get_interface(): ptr evmc_host_interface {.importc, cdecl.}
proc example_host_create_context(tx_context: evmc_tx_context): ptr evmc_host_context {.importc, cdecl.}
proc example_host_destroy_context(context: ptr evmc_host_context) {.importc, cdecl.}
proc example_host_create_context(tx_context: evmc_tx_context): evmc_host_context {.importc, cdecl.}
proc example_host_destroy_context(context: evmc_host_context) {.importc, cdecl.}
proc evmc_create_example_vm(): ptr evmc_vm {.importc, cdecl.}
proc main() =
echo "tx_context: ", sizeof(evmc_tx_context)
var vm = evmc_create_example_vm()
var host = example_host_get_interface()
var code = hexToSeqByte("4360005543600052596000f3")
var input = "Hello World!"
const gas = 200000'i64
var address: evmc_address
hexToByteArray("0x0001020000000000000000000000000000000000", address.bytes)
var balance: evmc_uint256be
hexToByteArray("0x0100000000000000000000000000000000000000000000000000000000000000", balance.bytes)
var ahash = evmc_bytes32(bytes: [0.byte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
var tx_context = evmc_tx_context(
block_number: 42,
block_timestamp: 66,
block_gas_limit: gas * 2
)
var msg = evmc_message(
kind: EVMC_CALL,
sender: address,
destination: address,
value: balance,
input_data: cast[ptr byte](input[0].addr),
input_size: input.len.uint,
gas: gas,
depth: 0
)
var ctx = example_host_create_context(tx_context)
var hc = HostContext.init(host, ctx)
suite "EVMC Nim to C API, host interface tests":
setup:
var
key: evmc_bytes32
value: evmc_bytes32
hexToByteArray("0x0000000000000000000000000000000000000000000000000000000000000001", key.bytes)
hexToByteArray("0x0000000000000000000000000000000000000000000000000000000000000101", value.bytes)
test "getTxContext":
let txc = hc.getTxContext()
check tx_context.block_number == txc.block_number
check tx_context.block_timestamp == txc.block_timestamp
check tx_context.block_gas_limit == txc.block_gas_limit
test "getBlockHash":
var b10c: evmc_bytes32
hexToByteArray("0xb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5f",
b10c.bytes)
let blockHash = hc.getBlockHash(tx_context.block_number - 1)
check blockHash == b10c
test "setStorage":
check hc.setStorage(address, key, value) == EVMC_STORAGE_MODIFIED
test "getStorage":
let val = hc.getStorage(address, key)
check val == value
test "accountExists":
check hc.accountExists(address) == true
test "getBalance":
let bal = hc.getBalance(address)
check bal == balance
test "getCodeSize":
check hc.getCodeSize(address) == 6
test "getCodeHash":
let hash = hc.getCodeHash(address)
check hash == ahash
test "copyCode":
let acode = @[11.byte, 12, 13, 14, 15]
let bcode = hc.copyCode(address, 1)
check acode == bcode
test "selfdestruct":
hc.selfdestruct(address, address)
test "emitlog":
hc.emitLog(address, code, [ahash])
test "call":
let res = hc.call(msg)
check res.status_code == EVMC_REVERT
check res.gas_left == msg.gas
check res.output_size == msg.input_size
check equalMem(res.output_data, msg.input_data, msg.input_size)
# no need to release the result, it's a fake one
suite "EVMC Nim to C API, vm interface tests":
setup:
var nvm = EvmcVM.init(vm, hc)
test "isABICompatible":
check nvm.isABICompatible() == true
test "getCapabilities":
let cap = nvm.getCapabilities()
check EVMC_CAPABILITY_EVM1 in cap
check EVMC_CAPABILITY_EWASM in cap
test "setOption":
check nvm.setOption("verbose", "2") == EVMC_SET_OPTION_SUCCESS
check nvm.setOption("debug", "true") == EVMC_SET_OPTION_INVALID_NAME
test "execute and destroy":
var res = nvm.execute(EVMC_HOMESTEAD, msg, code)
check res.gas_left == 100000
res.release(res)
nvm.destroy()
example_host_destroy_context(ctx)
main()