evmc host implementation

This commit is contained in:
andri lim 2020-01-16 11:20:13 +07:00 committed by zah
parent 3c57cd638f
commit e25f2bb82f
4 changed files with 315 additions and 2 deletions

View File

@ -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

90
nimbus/vm/evmc_api.nim Normal file
View File

@ -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)

View File

@ -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))

179
nimbus/vm/evmc_host.nim Normal file
View File

@ -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[])