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.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
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,
|
sets, eth/[common, keys], eth/trie/db as triedb,
|
||||||
../constants, ../errors, ../vm_state, ../vm_types,
|
../constants, ../errors, ../vm_state, ../vm_types,
|
||||||
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
||||||
./code_stream, ./memory, ./message, ./stack, ../db/[state_db, db_chain],
|
./code_stream, ./memory, ./message, ./stack, ../db/[state_db, db_chain],
|
||||||
../utils/header, stew/[byteutils, ranges], precompiles,
|
../utils/header, stew/[byteutils, ranges], precompiles,
|
||||||
transaction_tracer
|
transaction_tracer, evmc/evmc, evmc_helpers, evmc_api
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "vm computation"
|
topics = "vm computation"
|
||||||
|
@ -247,3 +247,4 @@ proc prepareTracer*(c: Computation) =
|
||||||
c.vmState.tracer.prepare(c.msg.depth)
|
c.vmState.tracer.prepare(c.msg.depth)
|
||||||
|
|
||||||
include interpreter_dispatch
|
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