implement Nim to C tests
This commit is contained in:
parent
a0269efd5b
commit
3ba42ffcc9
|
@ -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.}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue