mirror of
https://github.com/status-im/evmc.git
synced 2025-02-23 08:28:15 +00:00
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.
|
# The fixed size array of 32 bytes.
|
||||||
# 32 bytes of data capable of storing e.g. 256-bit hashes.
|
# 32 bytes of data capable of storing e.g. 256-bit hashes.
|
||||||
evmc_bytes32* = object
|
evmc_bytes32* = object
|
||||||
bytes: array[32, byte]
|
bytes*: array[32, byte]
|
||||||
|
|
||||||
# The alias for evmc_bytes32 to represent a big-endian 256-bit integer.
|
# The alias for evmc_bytes32 to represent a big-endian 256-bit integer.
|
||||||
evmc_uint256be* = evmc_bytes32
|
evmc_uint256be* = evmc_bytes32
|
||||||
|
|
||||||
# Big-endian 160-bit hash suitable for keeping an Ethereum address.
|
# Big-endian 160-bit hash suitable for keeping an Ethereum address.
|
||||||
evmc_address* = object
|
evmc_address* = object
|
||||||
bytes: array[20, byte]
|
bytes*: array[20, byte]
|
||||||
|
|
||||||
# The kind of call-like instruction.
|
# The kind of call-like instruction.
|
||||||
evmc_call_kind* {.size: sizeof(cint).} = enum
|
evmc_call_kind* {.size: sizeof(cint).} = enum
|
||||||
@ -434,7 +434,7 @@ type
|
|||||||
# Destroys the VM instance.
|
# Destroys the VM instance.
|
||||||
#
|
#
|
||||||
# @param vm The VM instance to be destroyed.
|
# @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.
|
# Possible outcomes of evmc_set_option.
|
||||||
evmc_set_option_result* {.size: sizeof(cint).} = enum
|
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 name The option name. NULL-terminated string. Cannot be NULL.
|
||||||
# @param value The new option value. NULL-terminated string. Cannot be NULL.
|
# @param value The new option value. NULL-terminated string. Cannot be NULL.
|
||||||
# @return The outcome of the operation.
|
# @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.
|
# EVM revision.
|
||||||
#
|
#
|
||||||
@ -513,7 +513,7 @@ type
|
|||||||
# @param code The reference to the code to be executed. This argument MAY 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.
|
# @param code_size The length of the code. If @p code is NULL this argument MUST be 0.
|
||||||
# @return The execution result.
|
# @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,
|
context: evmc_host_context, rev: evmc_revision,
|
||||||
msg: evmc_message, code: ptr byte, code_size: uint): evmc_result {.cdecl.}
|
msg: evmc_message, code: ptr byte, code_size: uint): evmc_result {.cdecl.}
|
||||||
|
|
||||||
@ -530,7 +530,7 @@ type
|
|||||||
#
|
#
|
||||||
# @param vm The VM instance.
|
# @param vm The VM instance.
|
||||||
# @return The supported capabilities of the VM. @see evmc_capabilities.
|
# @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.
|
# 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.} =
|
proc excl*(a: var evmc_capabilities, b: evmc_capabilities) {.inline.} =
|
||||||
a = evmc_capabilities(a.uint32 and (not b.uint32))
|
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
|
type
|
||||||
HostContext* = object
|
HostContext* = object
|
||||||
host: evmc_host_interface
|
host: ptr evmc_host_interface
|
||||||
context: evmc_host_context
|
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 =
|
proc getTxContext*(ctx: HostContext): evmc_tx_context =
|
||||||
ctx.host.get_tx_context(ctx.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 =
|
proc getBalance*(ctx: HostContext, address: evmc_address): evmc_uint256be =
|
||||||
ctx.host.get_balance(ctx.context, address)
|
ctx.host.get_balance(ctx.context, address)
|
||||||
|
|
||||||
proc getCodeSize*(ctx: HostContext, address: evmc_address): uint =
|
proc getCodeSize*(ctx: HostContext, address: evmc_address): int =
|
||||||
ctx.host.get_code_size(ctx.context, address)
|
ctx.host.get_code_size(ctx.context, address).int
|
||||||
|
|
||||||
proc getCodeHash*(ctx: HostContext, address: evmc_address): evmc_bytes32 =
|
proc getCodeHash*(ctx: HostContext, address: evmc_address): evmc_bytes32 =
|
||||||
ctx.host.get_code_hash(ctx.context, address)
|
ctx.host.get_code_hash(ctx.context, address)
|
||||||
|
|
||||||
proc copyCode*(ctx: HostContext, address: evmc_address, code_offset: uint, buffer: openArray[byte]): uint =
|
proc copyCode*(ctx: HostContext, address: evmc_address, codeOffset: int = 0): seq[byte] =
|
||||||
ctx.host.copy_code(ctx.context, address, code_offset, buffer[0].unsafeAddr, buffer.len.uint)
|
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) =
|
proc selfdestruct*(ctx: HostContext, address, beneficiary: evmc_address) =
|
||||||
ctx.host.selfdestruct(ctx.context, address, beneficiary)
|
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 =
|
proc call*(ctx: HostContext, msg: evmc_message): evmc_result =
|
||||||
ctx.host.call(ctx.context, msg)
|
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" {
|
extern "C" {
|
||||||
|
|
||||||
const evmc_host_interface* example_host_get_interface()
|
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)
|
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();
|
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_host.cpp".}
|
||||||
{.compile: "evmc_c/example_vm.c".}
|
{.compile: "evmc_c/example_vm.c".}
|
||||||
{.passL: "-lstdc++"}
|
{.passL: "-lstdc++"}
|
||||||
|
|
||||||
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: evmc_tx_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: ptr 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 evmc_create_example_vm(): ptr evmc_vm {.importc, cdecl.}
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
echo "tx_context: ", sizeof(evmc_tx_context)
|
|
||||||
var vm = evmc_create_example_vm()
|
var vm = evmc_create_example_vm()
|
||||||
var host = example_host_get_interface()
|
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()
|
main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user