evmc host implementation
This commit is contained in:
parent
3c57cd638f
commit
e25f2bb82f
|
@ -6,13 +6,13 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
chronicles, strformat, strutils, sequtils, macros, math, options,
|
||||
chronicles, strformat, strutils, sequtils, macros, math, options, times,
|
||||
sets, eth/[common, keys], eth/trie/db as triedb,
|
||||
../constants, ../errors, ../vm_state, ../vm_types,
|
||||
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
||||
./code_stream, ./memory, ./message, ./stack, ../db/[state_db, db_chain],
|
||||
../utils/header, stew/[byteutils, ranges], precompiles,
|
||||
transaction_tracer
|
||||
transaction_tracer, evmc/evmc, evmc_helpers, evmc_api
|
||||
|
||||
logScope:
|
||||
topics = "vm computation"
|
||||
|
@ -247,3 +247,4 @@ proc prepareTracer*(c: Computation) =
|
|||
c.vmState.tracer.prepare(c.msg.depth)
|
||||
|
||||
include interpreter_dispatch
|
||||
include evmc_host
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2019 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import evmc/evmc, evmc_helpers, eth/common
|
||||
|
||||
proc nim_host_get_interface(): ptr evmc_host_interface {.importc, cdecl.}
|
||||
proc nim_host_create_context(vmstate: pointer, msg: ptr evmc_message): evmc_host_context {.importc, cdecl.}
|
||||
proc nim_host_destroy_context(ctx: evmc_host_context) {.importc, cdecl.}
|
||||
proc nim_create_nimbus_vm(): ptr evmc_vm {.importc, cdecl.}
|
||||
|
||||
type
|
||||
HostContext* = object
|
||||
host: ptr evmc_host_interface
|
||||
context*: evmc_host_context
|
||||
|
||||
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)
|
||||
|
||||
proc getBlockHash*(ctx: HostContext, number: int64): Hash256 =
|
||||
Hash256.fromEvmc ctx.host.get_block_hash(ctx.context, number)
|
||||
|
||||
proc accountExists*(ctx: HostContext, address: EthAddress): bool =
|
||||
var address = toEvmc(address)
|
||||
ctx.host.account_exists(ctx.context, address.addr).bool
|
||||
|
||||
proc getStorage*(ctx: HostContext, address: EthAddress, key: Uint256): Uint256 =
|
||||
var
|
||||
address = toEvmc(address)
|
||||
key = toEvmc(key)
|
||||
Uint256.fromEvmc ctx.host.get_storage(ctx.context, address.addr, key.addr)
|
||||
|
||||
proc setStorage*(ctx: HostContext, address: EthAddress,
|
||||
key, value: Uint256): evmc_storage_status =
|
||||
var
|
||||
address = toEvmc(address)
|
||||
key = toEvmc(key)
|
||||
value = toEvmc(value)
|
||||
ctx.host.set_storage(ctx.context, address.addr, key.addr, value.addr)
|
||||
|
||||
proc getBalance*(ctx: HostContext, address: EthAddress): Uint256 =
|
||||
var address = toEvmc(address)
|
||||
Uint256.fromEvmc ctx.host.get_balance(ctx.context, address.addr)
|
||||
|
||||
proc getCodeSize*(ctx: HostContext, address: EthAddress): int =
|
||||
var address = toEvmc(address)
|
||||
ctx.host.get_code_size(ctx.context, address.addr).int
|
||||
|
||||
proc getCodeHash*(ctx: HostContext, address: EthAddress): Hash256 =
|
||||
var address = toEvmc(address)
|
||||
Hash256.fromEvmc ctx.host.get_code_hash(ctx.context, address.addr)
|
||||
|
||||
proc copyCode*(ctx: HostContext, address: EthAddress, codeOffset: int = 0): seq[byte] =
|
||||
let size = ctx.getCodeSize(address)
|
||||
var address = toEvmc(address)
|
||||
if size - codeOffset > 0:
|
||||
result = newSeq[byte](size - codeOffset)
|
||||
let read = ctx.host.copy_code(ctx.context, address.addr, code_offset.uint, result[0].addr, result.len.uint).int
|
||||
doAssert(read == result.len)
|
||||
|
||||
proc selfdestruct*(ctx: HostContext, address, beneficiary: EthAddress) =
|
||||
var
|
||||
address = toEvmc(address)
|
||||
beneficiary = toEvmc(beneficiary)
|
||||
ctx.host.selfdestruct(ctx.context, address.addr, beneficiary.addr)
|
||||
|
||||
proc emitLog*(ctx: HostContext, address: EthAddress, data: openArray[byte], topics: openArray[evmc_bytes32]) =
|
||||
var address = toEvmc(address)
|
||||
ctx.host.emit_log(ctx.context, address.addr, data[0].unsafeAddr, data.len.uint, topics[0].unsafeAddr, topics.len.uint)
|
||||
|
||||
proc call*(ctx: HostContext, msg: evmc_message): evmc_result =
|
||||
ctx.host.call(ctx.context, msg.unsafeAddr)
|
||||
|
||||
#proc vmHost*(vmState: BaseVMState, gasPrice: GasInt, origin: EthAddress): HostContext =
|
||||
# let host = nim_host_get_interface()
|
||||
# let context = nim_host_create_context(cast[pointer](vmState), gasPrice, toEvmc(origin))
|
||||
# result.init(host, context)
|
||||
#
|
||||
#proc destroy*(hc: HostContext) =
|
||||
# nim_host_destroy_context(hc.context)
|
|
@ -0,0 +1,43 @@
|
|||
import eth/common, stint, evmc/evmc
|
||||
|
||||
const
|
||||
evmc_native* {.booldefine.} = false
|
||||
|
||||
func toEvmc*(a: EthAddress): evmc_address {.inline.} =
|
||||
cast[evmc_address](a)
|
||||
|
||||
func toEvmc*(h: Hash256): evmc_bytes32 {.inline.} =
|
||||
cast[evmc_bytes32](h)
|
||||
|
||||
func toEvmc*(n: Uint256): evmc_uint256be {.inline.} =
|
||||
when evmc_native:
|
||||
cast[evmc_uint256be](n)
|
||||
else:
|
||||
cast[evmc_uint256be](n.toByteArrayBE)
|
||||
|
||||
func fromEvmc*(T: type, n: evmc_bytes32): T {.inline.} =
|
||||
when T is Hash256:
|
||||
cast[Hash256](n)
|
||||
elif T is Uint256:
|
||||
when evmc_native:
|
||||
cast[Uint256](n)
|
||||
else:
|
||||
Uint256.fromBytesBE(n.bytes)
|
||||
else:
|
||||
{.error: "cannot convert unsupported evmc type".}
|
||||
|
||||
func fromEvmc*(a: evmc_address): EthAddress {.inline.} =
|
||||
cast[EthAddress](a)
|
||||
|
||||
when isMainModule:
|
||||
import constants
|
||||
var a: evmc_address
|
||||
a.bytes[19] = 3.byte
|
||||
var na = fromEvmc(a)
|
||||
assert(a == toEvmc(na))
|
||||
var b = stuint(10, 256)
|
||||
var eb = b.toEvmc
|
||||
assert(b == fromEvmc(Uint256, eb))
|
||||
var h = EMPTY_SHA3
|
||||
var eh = toEvmc(h)
|
||||
assert(h == fromEvmc(Hash256, eh))
|
|
@ -0,0 +1,179 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2019 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
proc hostReleaseResultImpl(result: var evmc_result) {.cdecl.} =
|
||||
discard
|
||||
|
||||
proc hostGetTxContextImpl(ctx: Computation): evmc_tx_context {.cdecl.} =
|
||||
let vmstate = ctx.vmState
|
||||
result.tx_gas_price = toEvmc(vmstate.txGasPrice.u256)
|
||||
result.tx_origin = toEvmc(vmstate.txOrigin)
|
||||
result.block_coinbase = toEvmc(vmstate.coinbase)
|
||||
result.block_number = vmstate.blockNumber.truncate(int64)
|
||||
result.block_timestamp = vmstate.timestamp.toUnix()
|
||||
result.block_gas_limit = int64(vmstate.blockHeader.gasLimit)
|
||||
result.block_difficulty = toEvmc(vmstate.difficulty)
|
||||
result.chain_id = toEvmc(vmstate.chaindb.config.chainId.u256)
|
||||
|
||||
proc hostGetBlockHashImpl(ctx: Computation, number: int64): evmc_bytes32 {.cdecl.} =
|
||||
ctx.vmState.getAncestorHash(number.u256).toEvmc()
|
||||
|
||||
proc hostAccountExistsImpl(ctx: Computation, address: var evmc_address): c99bool {.cdecl.} =
|
||||
ctx.vmState.readOnlyStateDB.accountExists(fromEvmc(address))
|
||||
|
||||
proc hostGetStorageImpl(ctx: Computation, address: var evmc_address, key: var evmc_bytes32): evmc_bytes32 {.cdecl.} =
|
||||
let storageAddr = fromEvmc(address)
|
||||
assert storageAddr == ctx.msg.contractAddress
|
||||
let (storage, _) = ctx.vmState.accountDB.getStorage(storageAddr, Uint256.fromEvmc(key))
|
||||
storage.toEvmc()
|
||||
|
||||
proc hostSetStorageImpl(ctx: Computation, address: var evmc_address,
|
||||
key, value: var evmc_bytes32): evmc_storage_status {.cdecl.} =
|
||||
let
|
||||
storageAddr = fromEvmc(address)
|
||||
slot = Uint256.fromEvmc(key)
|
||||
newValue = Uint256.fromEvmc(value)
|
||||
statedb = ctx.vmState.readOnlyStateDb
|
||||
(currValue, _) = statedb.getStorage(storageAddr, slot)
|
||||
|
||||
assert storageAddr == ctx.msg.contractAddress
|
||||
|
||||
if newValue == currValue:
|
||||
return EVMC_STORAGE_UNCHANGED
|
||||
|
||||
var
|
||||
status = EVMC_STORAGE_MODIFIED
|
||||
origValue = statedb.getCommittedStorage(storageAddr, slot)
|
||||
|
||||
if origValue == currValue or ctx.fork >= FkIstanbul:
|
||||
if currValue == 0:
|
||||
status = EVMC_STORAGE_ADDED
|
||||
elif newValue == 0:
|
||||
status = EVMC_STORAGE_DELETED
|
||||
# refunds += sstoreRefundGas
|
||||
else:
|
||||
status = EVMC_STORAGE_MODIFIED_AGAIN
|
||||
#if origValue != 0:
|
||||
# if currValue == 0:
|
||||
# refunds -= sstoreRefundGas # Can go negative
|
||||
# if newValue == 0:
|
||||
# refunds += sstoreRefundGas
|
||||
#if origValue == newValue:
|
||||
# if origValue == 0:
|
||||
# refunds += sstoreSetGas - sstoreUnchangedGas
|
||||
# else:
|
||||
# refunds += sstoreResetGas - sstoreUnchangedGas
|
||||
|
||||
ctx.vmState.mutateStateDB:
|
||||
db.setStorage(storageAddr, slot, newValue)
|
||||
|
||||
result = status
|
||||
|
||||
proc hostGetBalanceImpl(ctx: Computation, address: var evmc_address): evmc_uint256be {.cdecl.} =
|
||||
ctx.vmState.readOnlyStateDB.getBalance(fromEvmc(address)).toEvmc()
|
||||
|
||||
proc hostGetCodeSizeImpl(ctx: Computation, address: var evmc_address): uint {.cdecl.} =
|
||||
ctx.vmState.readOnlyStateDB.getCode(fromEvmc(address)).len.uint
|
||||
|
||||
proc hostGetCodeHashImpl(ctx: Computation, address: var evmc_address): evmc_bytes32 {.cdecl.} =
|
||||
ctx.vmstate.readOnlyStateDB.getCodeHash(fromEvmc(address)).toEvmc()
|
||||
|
||||
proc hostCopyCodeImpl(ctx: Computation, address: var evmc_address,
|
||||
codeOffset: uint, bufferData: ptr byte,
|
||||
bufferSize: uint): uint {.cdecl.} =
|
||||
|
||||
var code = ctx.vmState.readOnlyStateDB.getCode(fromEvmc(address))
|
||||
|
||||
# Handle "big offset" edge case.
|
||||
if codeOffset > code.len.uint:
|
||||
return 0
|
||||
|
||||
let maxToCopy = code.len - codeOffset.int
|
||||
let numToCopy = min(maxToCopy, bufferSize.int)
|
||||
if numToCopy > 0:
|
||||
copyMem(bufferData, code.slice(codeOffset.int).baseAddr, numToCopy)
|
||||
result = numToCopy.uint
|
||||
|
||||
proc hostSelfdestructImpl(ctx: Computation, address, beneficiary: var evmc_address) {.cdecl.} =
|
||||
assert fromEvmc(address) == ctx.msg.contractAddress
|
||||
ctx.registerAccountForDeletion(fromEvmc(beneficiary))
|
||||
|
||||
proc hostEmitLogImpl(ctx: Computation, address: var evmc_address,
|
||||
data: ptr byte, dataSize: uint,
|
||||
topics: UncheckedArray[evmc_bytes32], topicsCount: uint) {.cdecl.} =
|
||||
var log: Log
|
||||
log.topics = newSeq[Topic](topicsCount)
|
||||
for i in 0 ..< topicsCount:
|
||||
log.topics[i] = topics[i].bytes
|
||||
|
||||
log.data = newSeq[byte](dataSize)
|
||||
copyMem(log.data[0].addr, data, dataSize)
|
||||
log.address = fromEvmc(address)
|
||||
ctx.addLogEntry(log)
|
||||
|
||||
proc hostCallImpl(ctx: Computation, msg: var evmc_message): evmc_result {.cdecl.} =
|
||||
discard
|
||||
|
||||
proc initHostInterface(): evmc_host_interface =
|
||||
result.account_exists = cast[evmc_account_exists_fn](hostAccountExistsImpl)
|
||||
result.get_storage = cast[evmc_get_storage_fn](hostGetStorageImpl)
|
||||
result.set_storage = cast[evmc_set_storage_fn](hostSetStorageImpl)
|
||||
result.get_balance = cast[evmc_get_balance_fn](hostGetBalanceImpl)
|
||||
result.get_code_size = cast[evmc_get_code_size_fn](hostGetCodeSizeImpl)
|
||||
result.get_code_hash = cast[evmc_get_code_hash_fn](hostGetCodeHashImpl)
|
||||
result.copy_code = cast[evmc_copy_code_fn](hostCopyCodeImpl)
|
||||
result.selfdestruct = cast[evmc_selfdestruct_fn](hostSelfdestructImpl)
|
||||
result.call = cast[evmc_call_fn](hostCallImpl)
|
||||
result.get_tx_context = cast[evmc_get_tx_context_fn](hostGetTxContextImpl)
|
||||
result.get_block_hash = cast[evmc_get_block_hash_fn](hostGetBlockHashImpl)
|
||||
result.emit_log = cast[evmc_emit_log_fn](hostEmitLogImpl)
|
||||
|
||||
proc vmSetOptionImpl(vm: ptr evmc_vm, name, value: cstring): evmc_set_option_result {.cdecl.} =
|
||||
return EVMC_SET_OPTION_INVALID_NAME
|
||||
|
||||
proc vmExecuteImpl(vm: ptr evmc_vm, host: ptr evmc_host_interface,
|
||||
ctx: Computation, rev: evmc_revision,
|
||||
msg: evmc_message, code: ptr byte, code_size: uint): evmc_result {.cdecl.} =
|
||||
discard
|
||||
|
||||
proc vmGetCapabilitiesImpl(vm: ptr evmc_vm): evmc_capabilities {.cdecl.} =
|
||||
result.incl(EVMC_CAPABILITY_EVM1)
|
||||
|
||||
proc vmDestroyImpl(vm: ptr evmc_vm) {.cdecl.} =
|
||||
dealloc(vm)
|
||||
|
||||
const
|
||||
EVMC_HOST_NAME = "nimbus_vm"
|
||||
EVMC_VM_VERSION = "0.0.1"
|
||||
|
||||
proc init(vm: var evmc_vm) =
|
||||
vm.abi_version = EVMC_ABI_VERSION
|
||||
vm.name = EVMC_HOST_NAME
|
||||
vm.version = EVMC_VM_VERSION
|
||||
vm.destroy = vmDestroyImpl
|
||||
vm.execute = cast[evmc_execute_fn](vmExecuteImpl)
|
||||
vm.get_capabilities = vmGetCapabilitiesImpl
|
||||
vm.set_option = vmSetOptionImpl
|
||||
|
||||
let gHost = initHostInterface()
|
||||
proc nim_host_get_interface(): ptr evmc_host_interface {.exportc, cdecl.} =
|
||||
result = gHost.unsafeAddr
|
||||
|
||||
proc nim_host_create_context(vmstate: BaseVmState, msg: ptr evmc_message): Computation {.exportc, cdecl.} =
|
||||
#result = HostContext(
|
||||
# vmState: vmstate,
|
||||
# gasPrice: GasInt(gasPrice),
|
||||
# origin: fromEvmc(origin)
|
||||
#)
|
||||
GC_ref(result)
|
||||
|
||||
proc nim_host_destroy_context(ctx: Computation) {.exportc, cdecl.} =
|
||||
GC_unref(ctx)
|
||||
|
||||
proc nim_create_nimbus_vm(): ptr evmc_vm {.exportc, cdecl.} =
|
||||
result = create(evmc_vm)
|
||||
init(result[])
|
Loading…
Reference in New Issue