remove evmc code from vm2

why:
  handled by original vm
This commit is contained in:
Jordan Hrycaj 2021-04-08 17:37:33 +01:00 committed by zah
parent b7bf84a71f
commit cf2d771c4d
9 changed files with 26 additions and 643 deletions

View File

@ -23,100 +23,66 @@ import
when defined(chronicles_log_level): when defined(chronicles_log_level):
import stew/byteutils import stew/byteutils
when defined(evmc_enabled):
import evmc/evmc, evmc_helpers, evmc_api, stew/ranges/ptr_arith
logScope: logScope:
topics = "vm computation" topics = "vm computation"
const
evmc_enabled* = defined(evmc_enabled)
template getCoinbase*(c: Computation): EthAddress = template getCoinbase*(c: Computation): EthAddress =
when evmc_enabled: block:
c.host.getTxContext().block_coinbase
else:
c.vmState.coinbase c.vmState.coinbase
template getTimestamp*(c: Computation): int64 = template getTimestamp*(c: Computation): int64 =
when evmc_enabled: block:
c.host.getTxContext().block_timestamp
else:
c.vmState.timestamp.toUnix c.vmState.timestamp.toUnix
template getBlockNumber*(c: Computation): Uint256 = template getBlockNumber*(c: Computation): Uint256 =
when evmc_enabled: block:
c.host.getTxContext().block_number.u256
else:
c.vmState.blockNumber.blockNumberToVmWord c.vmState.blockNumber.blockNumberToVmWord
template getDifficulty*(c: Computation): DifficultyInt = template getDifficulty*(c: Computation): DifficultyInt =
when evmc_enabled: block:
Uint256.fromEvmc c.host.getTxContext().block_difficulty
else:
c.vmState.difficulty c.vmState.difficulty
template getGasLimit*(c: Computation): GasInt = template getGasLimit*(c: Computation): GasInt =
when evmc_enabled: block:
c.host.getTxContext().block_gas_limit.GasInt
else:
c.vmState.gasLimit c.vmState.gasLimit
template getChainId*(c: Computation): uint = template getChainId*(c: Computation): uint =
when evmc_enabled: block:
Uint256.fromEvmc(c.host.getTxContext().chain_id).truncate(uint)
else:
c.vmState.chaindb.config.chainId.uint c.vmState.chaindb.config.chainId.uint
template getOrigin*(c: Computation): EthAddress = template getOrigin*(c: Computation): EthAddress =
when evmc_enabled: block:
c.host.getTxContext().tx_origin
else:
c.vmState.txOrigin c.vmState.txOrigin
template getGasPrice*(c: Computation): GasInt = template getGasPrice*(c: Computation): GasInt =
when evmc_enabled: block:
Uint256.fromEvmc(c.host.getTxContext().tx_gas_price).truncate(GasInt)
else:
c.vmState.txGasPrice c.vmState.txGasPrice
template getBlockHash*(c: Computation, blockNumber: Uint256): Hash256 = template getBlockHash*(c: Computation, blockNumber: Uint256): Hash256 =
when evmc_enabled: block:
c.host.getBlockHash(blockNumber)
else:
c.vmState.getAncestorHash(blockNumber.vmWordToBlockNumber) c.vmState.getAncestorHash(blockNumber.vmWordToBlockNumber)
template accountExists*(c: Computation, address: EthAddress): bool = template accountExists*(c: Computation, address: EthAddress): bool =
when evmc_enabled: block:
c.host.accountExists(address)
else:
if c.fork >= FkSpurious: if c.fork >= FkSpurious:
not c.vmState.readOnlyStateDB.isDeadAccount(address) not c.vmState.readOnlyStateDB.isDeadAccount(address)
else: else:
c.vmState.readOnlyStateDB.accountExists(address) c.vmState.readOnlyStateDB.accountExists(address)
template getStorage*(c: Computation, slot: Uint256): Uint256 = template getStorage*(c: Computation, slot: Uint256): Uint256 =
when evmc_enabled: block:
c.host.getStorage(c.msg.contractAddress, slot)
else:
c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot) c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot)
template getBalance*(c: Computation, address: EthAddress): Uint256 = template getBalance*(c: Computation, address: EthAddress): Uint256 =
when evmc_enabled: block:
c.host.getBalance(address)
else:
c.vmState.readOnlyStateDB.getBalance(address) c.vmState.readOnlyStateDB.getBalance(address)
template getCodeSize*(c: Computation, address: EthAddress): uint = template getCodeSize*(c: Computation, address: EthAddress): uint =
when evmc_enabled: block:
c.host.getCodeSize(address)
else:
uint(c.vmState.readOnlyStateDB.getCodeSize(address)) uint(c.vmState.readOnlyStateDB.getCodeSize(address))
template getCodeHash*(c: Computation, address: EthAddress): Hash256 = template getCodeHash*(c: Computation, address: EthAddress): Hash256 =
when evmc_enabled: block:
c.host.getCodeHash(address)
else:
let db = c.vmState.readOnlyStateDB let db = c.vmState.readOnlyStateDB
if not db.accountExists(address) or db.isEmptyAccount(address): if not db.accountExists(address) or db.isEmptyAccount(address):
default(Hash256) default(Hash256)
@ -124,15 +90,11 @@ template getCodeHash*(c: Computation, address: EthAddress): Hash256 =
db.getCodeHash(address) db.getCodeHash(address)
template selfDestruct*(c: Computation, address: EthAddress) = template selfDestruct*(c: Computation, address: EthAddress) =
when evmc_enabled: block:
c.host.selfDestruct(c.msg.contractAddress, address)
else:
c.execSelfDestruct(address) c.execSelfDestruct(address)
template getCode*(c: Computation, address: EthAddress): seq[byte] = template getCode*(c: Computation, address: EthAddress): seq[byte] =
when evmc_enabled: block:
c.host.copyCode(address)
else:
c.vmState.readOnlyStateDB.getCode(address) c.vmState.readOnlyStateDB.getCode(address)
proc generateContractAddress(c: Computation, salt: Uint256): EthAddress = proc generateContractAddress(c: Computation, salt: Uint256): EthAddress =
@ -162,11 +124,6 @@ proc newComputation*(vmState: BaseVMState, message: Message, salt= 0.u256): Comp
else: else:
result.code = newCodeStream(vmState.readOnlyStateDb.getCode(message.codeAddress)) result.code = newCodeStream(vmState.readOnlyStateDb.getCode(message.codeAddress))
when evmc_enabled:
result.host.init(
nim_host_get_interface(),
cast[evmc_host_context](result)
)
template gasCosts*(c: Computation): untyped = template gasCosts*(c: Computation): untyped =
c.vmState.gasCosts c.vmState.gasCosts
@ -401,6 +358,3 @@ proc prepareTracer*(c: Computation) {.inline.} =
c.vmState.tracer.prepare(c.msg.depth) c.vmState.tracer.prepare(c.msg.depth)
include interpreter_dispatch include interpreter_dispatch
when defined(evmc_enabled):
include evmc_host

View File

@ -1,147 +0,0 @@
# 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, ../constants
type
# we are not using EVMC original signature here
# because we want to trick the compiler
# and reduce unnecessary conversion/typecast
# TODO: move this type definition to nim-evmc
# after we have implemented ABI compatibility test
# TODO: investigate the possibility to use Big Endian VMWord
# directly if it's not involving stint computation
# and we can reduce unecessary conversion further
nimbus_tx_context* = object
tx_gas_price* : evmc_uint256be # The transaction gas price.
tx_origin* : EthAddress # The transaction origin account.
block_coinbase* : EthAddress # The miner of the block.
block_number* : int64 # The block number.
block_timestamp* : int64 # The block timestamp.
block_gas_limit* : int64 # The block gas limit.
block_difficulty*: evmc_uint256be # The block difficulty.
chain_id* : evmc_uint256be # The blockchain's ChainID.
nimbus_message* = object
kind*: evmc_call_kind
flags*: uint32
depth*: int32
gas*: int64
destination*: EthAddress
sender*: EthAddress
input_data*: ptr byte
input_size*: uint
value*: evmc_uint256be
create2_salt*: evmc_bytes32
nimbus_result* = object
status_code*: evmc_status_code
gas_left*: int64
output_data*: ptr byte
output_size*: uint
release*: proc(result: var nimbus_result) {.cdecl, gcsafe.}
create_address*: EthAddress
padding*: array[4, byte]
nimbus_host_interface* = object
account_exists*: proc(context: evmc_host_context, address: EthAddress): bool {.cdecl, gcsafe.}
get_storage*: proc(context: evmc_host_context, address: EthAddress, key: ptr evmc_uint256be): evmc_uint256be {.cdecl, gcsafe.}
set_storage*: proc(context: evmc_host_context, address: EthAddress,
key, value: ptr evmc_uint256be): evmc_storage_status {.cdecl, gcsafe.}
get_balance*: proc(context: evmc_host_context, address: EthAddress): evmc_uint256be {.cdecl, gcsafe.}
get_code_size*: proc(context: evmc_host_context, address: EthAddress): uint {.cdecl, gcsafe.}
get_code_hash*: proc(context: evmc_host_context, address: EthAddress): Hash256 {.cdecl, gcsafe.}
copy_code*: proc(context: evmc_host_context, address: EthAddress,
code_offset: int, buffer_data: ptr byte,
buffer_size: int): int {.cdecl, gcsafe.}
selfdestruct*: proc(context: evmc_host_context, address, beneficiary: EthAddress) {.cdecl, gcsafe.}
call*: proc(context: evmc_host_context, msg: ptr nimbus_message): nimbus_result {.cdecl, gcsafe.}
get_tx_context*: proc(context: evmc_host_context): nimbus_tx_context {.cdecl, gcsafe.}
get_block_hash*: proc(context: evmc_host_context, number: int64): Hash256 {.cdecl, gcsafe.}
emit_log*: proc(context: evmc_host_context, address: EthAddress,
data: ptr byte, data_size: uint,
topics: ptr evmc_bytes32, topics_count: uint) {.cdecl, gcsafe.}
proc nim_host_get_interface*(): ptr nimbus_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 nimbus_host_interface
context*: evmc_host_context
proc init*(x: var HostContext, host: ptr nimbus_host_interface, context: evmc_host_context) =
x.host = host
x.context = context
proc init*(x: typedesc[HostContext], host: ptr nimbus_host_interface, context: evmc_host_context): HostContext =
result.init(host, context)
proc getTxContext*(ctx: HostContext): nimbus_tx_context {.inline.} =
ctx.host.get_tx_context(ctx.context)
proc getBlockHash*(ctx: HostContext, number: Uint256): Hash256 =
let
blockNumber = ctx.getTxContext().block_number.u256
ancestorDepth = blockNumber - number - 1
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH:
return
if number >= blockNumber:
return
ctx.host.get_block_hash(ctx.context, number.truncate(int64))
proc accountExists*(ctx: HostContext, address: EthAddress): bool {.inline.} =
ctx.host.account_exists(ctx.context, address)
proc getStorage*(ctx: HostContext, address: EthAddress, key: Uint256): Uint256 =
var key = toEvmc(key)
Uint256.fromEvmc ctx.host.get_storage(ctx.context, address, key.addr)
proc setStorage*(ctx: HostContext, address: EthAddress,
key, value: Uint256): evmc_storage_status {.inline.} =
var
key = toEvmc(key)
value = toEvmc(value)
ctx.host.set_storage(ctx.context, address, key.addr, value.addr)
proc getBalance*(ctx: HostContext, address: EthAddress): Uint256 {.inline.} =
Uint256.fromEvmc ctx.host.get_balance(ctx.context, address)
proc getCodeSize*(ctx: HostContext, address: EthAddress): uint {.inline.} =
ctx.host.get_code_size(ctx.context, address)
proc getCodeHash*(ctx: HostContext, address: EthAddress): Hash256 {.inline.} =
ctx.host.get_code_hash(ctx.context, address)
proc copyCode*(ctx: HostContext, address: EthAddress, codeOffset: int = 0): seq[byte] =
let size = ctx.getCodeSize(address).int
if size - codeOffset > 0:
result = newSeq[byte](size - codeOffset)
let read = ctx.host.copy_code(ctx.context, address,
codeOffset, result[0].addr, result.len)
doAssert(read == result.len)
proc selfdestruct*(ctx: HostContext, address, beneficiary: EthAddress) {.inline.} =
ctx.host.selfdestruct(ctx.context, address, beneficiary)
proc emitLog*(ctx: HostContext, address: EthAddress, data: openArray[byte],
topics: ptr evmc_bytes32, topicsCount: int) {.inline.} =
ctx.host.emit_log(ctx.context, address, if data.len > 0: data[0].unsafeAddr else: nil,
data.len.uint, topics, topicsCount.uint)
proc call*(ctx: HostContext, msg: nimbus_message): nimbus_result {.inline.} =
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

@ -1,43 +0,0 @@
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))

View File

@ -1,252 +0,0 @@
# 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(res: var nimbus_result) {.cdecl, gcsafe.} =
dealloc(res.output_data)
proc hostGetTxContextImpl(ctx: Computation): nimbus_tx_context {.cdecl.} =
let vmstate = ctx.vmState
result.tx_gas_price = toEvmc(vmstate.txGasPrice.u256)
result.tx_origin = vmstate.txOrigin
result.block_coinbase = 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.uint.u256)
proc hostGetBlockHashImpl(ctx: Computation, number: int64): Hash256 {.cdecl.} =
ctx.vmState.getAncestorHash(number.u256)
proc hostAccountExistsImpl(ctx: Computation, address: EthAddress): bool {.cdecl.} =
let db = ctx.vmState.readOnlyStateDB
if ctx.fork >= FkSpurious:
not db.isDeadAccount(address)
else:
db.accountExists(address)
proc hostGetStorageImpl(ctx: Computation, address: EthAddress, key: var evmc_bytes32): evmc_bytes32 {.cdecl.} =
ctx.vmState.accountDB.getStorage(address, Uint256.fromEvmc(key)).toEvmc()
proc sstoreNetGasMetering(ctx: Computation): bool {.inline.} =
ctx.fork in {FkConstantinople, FkIstanbul, FkBerlin}
proc hostSetStorageImpl(ctx: Computation, address: EthAddress,
key, value: var evmc_bytes32): evmc_storage_status {.cdecl.} =
let
slot = Uint256.fromEvmc(key)
newValue = Uint256.fromEvmc(value)
statedb = ctx.vmState.readOnlyStateDb
currValue = statedb.getStorage(address, slot)
assert address == ctx.msg.contractAddress
var
status = EVMC_STORAGE_MODIFIED
gasRefund = 0.GasInt
origValue = 0.u256
if newValue == currValue:
status = EVMC_STORAGE_UNCHANGED
else:
origValue = statedb.getCommittedStorage(address, slot)
if origValue == currValue or not ctx.sstoreNetGasMetering():
if currValue == 0:
status = EVMC_STORAGE_ADDED
elif newValue == 0:
status = EVMC_STORAGE_DELETED
else:
status = EVMC_STORAGE_MODIFIED_AGAIN
ctx.vmState.mutateStateDB:
db.setStorage(address, slot, newValue)
let gasParam = GasParams(kind: Op.Sstore,
s_status: status,
s_currentValue: currValue,
s_originalValue: origValue
)
gasRefund = ctx.gasCosts[Sstore].c_handler(newValue, gasParam)[1]
if gasRefund != 0:
ctx.gasMeter.refundGas(gasRefund)
result = status
proc hostGetBalanceImpl(ctx: Computation, address: EthAddress): evmc_bytes32 {.cdecl.} =
ctx.vmState.readOnlyStateDB.getBalance(address).toEvmc()
proc hostGetCodeSizeImpl(ctx: Computation, address: EthAddress): uint {.cdecl.} =
ctx.vmState.readOnlyStateDB.getCode(address).len.uint
proc hostGetCodeHashImpl(ctx: Computation, address: EthAddress): Hash256 {.cdecl.} =
let db = ctx.vmstate.readOnlyStateDB
if not db.accountExists(address):
return
if db.isEmptyAccount(address):
return
db.getCodeHash(address)
proc hostCopyCodeImpl(ctx: Computation, address: EthAddress,
codeOffset: int, bufferData: ptr byte,
bufferSize: int): int {.cdecl.} =
var code = ctx.vmState.readOnlyStateDB.getCode(address)
# Handle "big offset" edge case.
if codeOffset > code.len:
return 0
let maxToCopy = code.len - codeOffset
let numToCopy = min(maxToCopy, bufferSize)
if numToCopy > 0:
copyMem(bufferData, code[codeOffset].addr, numToCopy)
result = numToCopy
proc hostSelfdestructImpl(ctx: Computation, address, beneficiary: EthAddress) {.cdecl.} =
assert address == ctx.msg.contractAddress
ctx.execSelfDestruct(beneficiary)
proc hostEmitLogImpl(ctx: Computation, address: EthAddress,
data: ptr byte, dataSize: int,
topics: UncheckedArray[evmc_bytes32], topicsCount: int) {.cdecl.} =
var log: Log
if topicsCount > 0:
log.topics = newSeq[Topic](topicsCount)
for i in 0 ..< topicsCount:
log.topics[i] = topics[i].bytes
log.data = @(makeOpenArray(data, dataSize))
log.address = address
ctx.addLogEntry(log)
template createImpl(c: Computation, m: nimbus_message, res: nimbus_result) =
# TODO: use evmc_message to evoid copy
let childMsg = Message(
kind: CallKind(m.kind),
depth: m.depth,
gas: m.gas,
sender: m.sender,
value: Uint256.fromEvmc(m.value),
data: @(makeOpenArray(m.inputData, m.inputSize.int))
)
let child = newComputation(c.vmState, childMsg, Uint256.fromEvmc(m.create2_salt))
child.execCallOrCreate()
if not child.shouldBurnGas:
res.gas_left = child.gasMeter.gasRemaining
if child.isSuccess:
c.merge(child)
res.status_code = EVMC_SUCCESS
res.create_address = child.msg.contractAddress
else:
res.status_code = if child.shouldBurnGas: EVMC_FAILURE else: EVMC_REVERT
if child.output.len > 0:
# TODO: can we move the ownership of seq to raw pointer?
res.output_size = child.output.len.uint
res.output_data = cast[ptr byte](alloc(child.output.len))
copyMem(res.output_data, child.output[0].addr, child.output.len)
res.release = hostReleaseResultImpl
template callImpl(c: Computation, m: nimbus_message, res: nimbus_result) =
let childMsg = Message(
kind: CallKind(m.kind),
depth: m.depth,
gas: m.gas,
sender: m.sender,
codeAddress: m.destination,
contractAddress: if m.kind == EVMC_CALL: m.destination else: c.msg.contractAddress,
value: Uint256.fromEvmc(m.value),
data: @(makeOpenArray(m.inputData, m.inputSize.int)),
flags: MsgFlags(m.flags)
)
let child = newComputation(c.vmState, childMsg)
child.execCallOrCreate()
if not child.shouldBurnGas:
res.gas_left = child.gasMeter.gasRemaining
if child.isSuccess:
c.merge(child)
res.status_code = EVMC_SUCCESS
else:
res.status_code = if child.shouldBurnGas: EVMC_FAILURE else: EVMC_REVERT
if child.output.len > 0:
# TODO: can we move the ownership of seq to raw pointer?
res.output_size = child.output.len.uint
res.output_data = cast[ptr byte](alloc(child.output.len))
copyMem(res.output_data, child.output[0].addr, child.output.len)
res.release = hostReleaseResultImpl
proc hostCallImpl(ctx: Computation, msg: var nimbus_message): nimbus_result {.cdecl.} =
if msg.kind == EVMC_CREATE or msg.kind == EVMC_CREATE2:
createImpl(ctx, msg, result)
else:
callImpl(ctx, msg, result)
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 nimbus_host_interface {.exportc, cdecl.} =
result = cast[ptr nimbus_host_interface](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[])

View File

@ -15,9 +15,6 @@ import
./utils/[macros_gen_opcodes, utils_numeric], ./utils/[macros_gen_opcodes, utils_numeric],
./opcode_values, ./vm_forks, ../../errors ./opcode_values, ./vm_forks, ../../errors
when defined(evmc_enabled):
import evmc/evmc
# Gas Fee Schedule # Gas Fee Schedule
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf # Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf
type type
@ -72,8 +69,6 @@ type
case kind*: Op case kind*: Op
of Sstore: of Sstore:
when defined(evmc_enabled):
s_status*: evmc_storage_status
s_currentValue*: Uint256 s_currentValue*: Uint256
s_originalValue*: Uint256 s_originalValue*: Uint256
of Call, CallCode, DelegateCall, StaticCall: of Call, CallCode, DelegateCall, StaticCall:
@ -244,32 +239,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
CleanRefund = SSTORE_RESET_GAS - SLOAD_GAS # resetting to the original non-zero value CleanRefund = SSTORE_RESET_GAS - SLOAD_GAS # resetting to the original non-zero value
ClearRefund = FeeSchedule[RefundsClear]# clearing an originally existing storage slot ClearRefund = FeeSchedule[RefundsClear]# clearing an originally existing storage slot
when defined(evmc_enabled): block:
const
sstoreDirty = when fork < FkConstantinople or fork == FkPetersburg: CleanGas
else: DirtyGas
case gasParams.s_status
of EVMC_STORAGE_ADDED: result.gasCost = InitGas
of EVMC_STORAGE_MODIFIED: result.gasCost = CleanGas
of EVMC_STORAGE_DELETED:
result.gasCost = CleanGas
result.gasRefund += ClearRefund
of EVMC_STORAGE_UNCHANGED: result.gasCost = sstoreDirty
of EVMC_STORAGE_MODIFIED_AGAIN:
result.gasCost = sstoreDirty
if not gasParams.s_originalValue.isZero:
if gasParams.s_currentValue.isZero:
result.gasRefund -= ClearRefund
if value.isZero:
result.gasRefund += ClearRefund
if gasParams.s_originalValue == value:
if gasParams.s_originalValue.isZero:
result.gasRefund += InitRefund
else:
result.gasRefund += CleanRefund
else:
when fork < FkConstantinople or fork == FkPetersburg: when fork < FkConstantinople or fork == FkPetersburg:
let isStorageEmpty = gasParams.s_currentValue.isZero let isStorageEmpty = gasParams.s_currentValue.isZero

View File

@ -14,9 +14,6 @@ import
../../errors, ../../constants, ../../errors, ../../constants,
../../db/[db_chain, accounts_cache] ../../db/[db_chain, accounts_cache]
when defined(evmc_enabled):
import ../evmc_api, ../evmc_helpers, evmc/evmc
logScope: logScope:
topics = "opcode impl" topics = "opcode impl"
@ -439,7 +436,6 @@ op sload, inline = true, slot:
## 0x54, Load word from storage. ## 0x54, Load word from storage.
push: c.getStorage(slot) push: c.getStorage(slot)
when not evmc_enabled:
template sstoreImpl(c: Computation, slot, newValue: Uint256) = template sstoreImpl(c: Computation, slot, newValue: Uint256) =
let currentValue {.inject.} = c.getStorage(slot) let currentValue {.inject.} = c.getStorage(slot)
@ -455,26 +451,12 @@ when not evmc_enabled:
c.vmState.mutateStateDB: c.vmState.mutateStateDB:
db.setStorage(c.msg.contractAddress, slot, newValue) db.setStorage(c.msg.contractAddress, slot, newValue)
when evmc_enabled:
template sstoreEvmc(c: Computation, slot, newValue: Uint256) =
let
currentValue {.inject.} = c.getStorage(slot)
status = c.host.setStorage(c.msg.contractAddress, slot, newValue)
gasParam = GasParams(kind: Op.Sstore, s_status: status)
gasCost = c.gasCosts[Sstore].c_handler(newValue, gasParam)[0]
c.gasMeter.consumeGas(gasCost, &"SSTORE: {c.msg.contractAddress}[{slot}] -> {newValue} ({currentValue})")
op sstore, inline = false, slot, newValue: op sstore, inline = false, slot, newValue:
## 0x55, Save word to storage. ## 0x55, Save word to storage.
checkInStaticContext(c) checkInStaticContext(c)
block:
when evmc_enabled:
sstoreEvmc(c, slot, newValue)
else:
sstoreImpl(c, slot, newValue) sstoreImpl(c, slot, newValue)
when not evmc_enabled:
template sstoreNetGasMeteringImpl(c: Computation, slot, newValue: Uint256) = template sstoreNetGasMeteringImpl(c: Computation, slot, newValue: Uint256) =
let stateDB = c.vmState.readOnlyStateDB let stateDB = c.vmState.readOnlyStateDB
let currentValue {.inject.} = c.getStorage(slot) let currentValue {.inject.} = c.getStorage(slot)
@ -500,18 +482,12 @@ op sstoreEIP2200, inline = false, slot, newValue:
if c.gasMeter.gasRemaining <= SentryGasEIP2200: if c.gasMeter.gasRemaining <= SentryGasEIP2200:
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE") raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
block:
when evmc_enabled:
sstoreEvmc(c, slot, newValue)
else:
sstoreNetGasMeteringImpl(c, slot, newValue) sstoreNetGasMeteringImpl(c, slot, newValue)
op sstoreEIP1283, inline = false, slot, newValue: op sstoreEIP1283, inline = false, slot, newValue:
checkInStaticContext(c) checkInStaticContext(c)
block:
when evmc_enabled:
sstoreEvmc(c, slot, newValue)
else:
sstoreNetGasMeteringImpl(c, slot, newValue) sstoreNetGasMeteringImpl(c, slot, newValue)
proc jumpImpl(c: Computation, jumpTarget: UInt256) = proc jumpImpl(c: Computation, jumpTarget: UInt256) =
@ -645,29 +621,7 @@ template genCreate(callName: untyped, opCode: Op): untyped =
createMsgGas -= createMsgGas div 64 createMsgGas -= createMsgGas div 64
c.gasMeter.consumeGas(createMsgGas, reason="CREATE") c.gasMeter.consumeGas(createMsgGas, reason="CREATE")
when evmc_enabled: block:
let msg = nimbus_message(
kind: callKind.evmc_call_kind,
depth: (c.msg.depth + 1).int32,
gas: createMsgGas,
sender: c.msg.contractAddress,
input_data: c.memory.readPtr(memPos),
input_size: memLen.uint,
value: toEvmc(endowment),
create2_salt: toEvmc(salt)
)
var res = c.host.call(msg)
c.returnData = @(makeOpenArray(res.outputData, res.outputSize.int))
c.gasMeter.returnGas(res.gas_left)
if res.status_code == EVMC_SUCCESS:
c.stack.top(res.create_address)
# TODO: a good candidate for destructor
if not res.release.isNil:
res.release(res)
else:
let childMsg = Message( let childMsg = Message(
kind: callKind, kind: callKind,
depth: c.msg.depth + 1, depth: c.msg.depth + 1,
@ -828,36 +782,7 @@ template genCall(callName: untyped, opCode: Op): untyped =
c.gasMeter.returnGas(childGasLimit) c.gasMeter.returnGas(childGasLimit)
return return
when evmc_enabled: block:
let msg = nimbus_message(
kind: callKind.evmc_call_kind,
depth: (c.msg.depth + 1).int32,
gas: childGasLimit,
sender: sender,
destination: destination,
input_data: c.memory.readPtr(memInPos),
input_size: memInLen.uint,
value: toEvmc(value),
flags: flags.uint32
)
var res = c.host.call(msg)
c.returnData = @(makeOpenArray(res.outputData, res.outputSize.int))
let actualOutputSize = min(memOutLen, c.returnData.len)
if actualOutputSize > 0:
c.memory.write(memOutPos,
c.returnData.toOpenArray(0, actualOutputSize - 1))
c.gasMeter.returnGas(res.gas_left)
if res.status_code == EVMC_SUCCESS:
c.stack.top(1)
# TODO: a good candidate for destructor
if not res.release.isNil:
res.release(res)
else:
let msg = Message( let msg = Message(
kind: callKind, kind: callKind,
depth: c.msg.depth + 1, depth: c.msg.depth + 1,
@ -1058,7 +983,5 @@ op sstoreEIP2929, inline = false, slot, newValue:
db.accessList(c.msg.contractAddress, slot) db.accessList(c.msg.contractAddress, slot)
c.gasMeter.consumeGas(ColdSloadCost, reason = "sstoreEIP2929") c.gasMeter.consumeGas(ColdSloadCost, reason = "sstoreEIP2929")
when evmc_enabled: block:
sstoreEvmc(c, slot, newValue)
else:
sstoreNetGasMeteringImpl(c, slot, newValue) sstoreNetGasMeteringImpl(c, slot, newValue)

View File

@ -14,9 +14,6 @@ import
../../types, ../../../errors, ../gas_meter, ../opcode_values, ../../types, ../../../errors, ../gas_meter, ../opcode_values,
./utils_numeric ./utils_numeric
when defined(evmc_enabled):
import ../../evmc_api, evmc/evmc
proc pop(tree: var NimNode): NimNode = proc pop(tree: var NimNode): NimNode =
## Returns the last value of a NimNode and remove it ## Returns the last value of a NimNode and remove it
result = tree[tree.len-1] result = tree[tree.len-1]
@ -122,15 +119,7 @@ proc logImpl(c: Computation, opcode: Op, topicCount: int) =
reason="Memory expansion, Log topic and data gas cost") reason="Memory expansion, Log topic and data gas cost")
c.memory.extend(memPos, len) c.memory.extend(memPos, len)
when evmc_enabled: block:
var topics: array[4, evmc_bytes32]
for i in 0 ..< topicCount:
topics[i].bytes = c.stack.popTopic()
c.host.emitLog(c.msg.contractAddress,
c.memory.read(memPos, len),
topics[0].addr, topicCount)
else:
var log: Log var log: Log
log.topics = newSeqOfCap[Topic](topicCount) log.topics = newSeqOfCap[Topic](topicCount)
for i in 0 ..< topicCount: for i in 0 ..< topicCount:

View File

@ -47,11 +47,6 @@ proc read*(memory: var Memory, startPos: Natural, size: Natural): seq[byte] =
# TODO: use an openarray[byte] # TODO: use an openarray[byte]
result = memory.bytes[startPos ..< (startPos + size)] result = memory.bytes[startPos ..< (startPos + size)]
when defined(evmc_enabled):
proc readPtr*(memory: var Memory, startPos: Natural): ptr byte =
if memory.bytes.len == 0 or startPos >= memory.bytes.len: return
result = memory.bytes[startPos].addr
proc write*(memory: var Memory, startPos: Natural, value: openarray[byte]) = proc write*(memory: var Memory, startPos: Natural, value: openarray[byte]) =
let size = value.len let size = value.len
if size == 0: if size == 0:

View File

@ -21,10 +21,6 @@ import
# TODO - will be hidden at a lower layer # TODO - will be hidden at a lower layer
../db/[db_chain, accounts_cache] ../db/[db_chain, accounts_cache]
when defined(evmc_enabled):
import
./evmc_api
type type
VMFlag* = enum VMFlag* = enum
ExecutionOK ExecutionOK
@ -73,8 +69,6 @@ type
Computation* = ref object Computation* = ref object
# The execution computation # The execution computation
vmState*: BaseVMState vmState*: BaseVMState
when defined(evmc_enabled):
host*: HostContext
msg*: Message msg*: Message
memory*: Memory memory*: Memory
stack*: Stack stack*: Stack