Merge branch 'master' into fluffy-evm-integration

This commit is contained in:
bhartnett 2024-12-02 16:19:08 +08:00
commit 83d603a849
No known key found for this signature in database
GPG Key ID: 076F2830DA6BD535
30 changed files with 284 additions and 1175 deletions

View File

@ -91,7 +91,11 @@ proc canAddPendingTransfer(
try:
let contentIds = transfers[nodeId]
(contentIds.len() < limit) and not contentIds.contains(contentId)
if (contentIds.len() < limit) and not contentIds.contains(contentId):
return true
else:
debug "Pending transfer limit reached for peer", nodeId, contentId
return false
except KeyError as e:
raiseAssert(e.msg)

View File

@ -37,13 +37,7 @@ type
tx: TransactionRef
updatedCache: TrieDatabaseRef
PreimagesBackendRef = ref object of RootObj
cfHandle: ColFamilyHandleRef
tx: TransactionRef
updatedCache: TrieDatabaseRef
DatabaseBackendRef =
AccountsBackendRef | StorageBackendRef | BytecodeBackendRef | PreimagesBackendRef
DatabaseBackendRef = AccountsBackendRef | StorageBackendRef | BytecodeBackendRef
DatabaseRef* = ref object
rocksDb: OptimisticTxDbRef
@ -51,7 +45,6 @@ type
accountsBackend: AccountsBackendRef
storageBackend: StorageBackendRef
bytecodeBackend: BytecodeBackendRef
preimagesBackend: PreimagesBackendRef
proc init*(T: type DatabaseRef, baseDir: string): Result[T, string] =
let dbPath = baseDir / "db"
@ -79,9 +72,6 @@ proc init*(T: type DatabaseRef, baseDir: string): Result[T, string] =
bytecodeBackend = BytecodeBackendRef(
cfHandle: db.getColFamilyHandle(COL_FAMILY_NAME_BYTECODE).get()
)
preimagesBackend = PreimagesBackendRef(
cfHandle: db.getColFamilyHandle(COL_FAMILY_NAME_PREIMAGES).get()
)
ok(
T(
@ -90,7 +80,6 @@ proc init*(T: type DatabaseRef, baseDir: string): Result[T, string] =
accountsBackend: accountsBackend,
storageBackend: storageBackend,
bytecodeBackend: bytecodeBackend,
preimagesBackend: preimagesBackend,
)
)
@ -138,9 +127,6 @@ proc getStorageBackend*(db: DatabaseRef): TrieDatabaseRef {.inline.} =
proc getBytecodeBackend*(db: DatabaseRef): TrieDatabaseRef {.inline.} =
trieDB(db.bytecodeBackend)
proc getPreimagesBackend*(db: DatabaseRef): TrieDatabaseRef {.inline.} =
trieDB(db.preimagesBackend)
proc getAccountsUpdatedCache*(db: DatabaseRef): TrieDatabaseRef {.inline.} =
db.accountsBackend.updatedCache
@ -179,12 +165,10 @@ proc beginTransaction*(db: DatabaseRef): Result[void, string] =
db.accountsBackend.tx = tx
db.storageBackend.tx = tx
db.bytecodeBackend.tx = tx
db.preimagesBackend.tx = tx
db.accountsBackend.updatedCache = newMemoryDB()
db.storageBackend.updatedCache = newMemoryDB()
db.bytecodeBackend.updatedCache = newMemoryDB()
db.preimagesBackend.updatedCache = nil # not used
ok()

View File

@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2024 Status Research & Development GmbH
# Copyright (c) 2021-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))

View File

@ -16,7 +16,6 @@ import
eth/common/[hashes, accounts, headers, addresses],
../db/[ledger, core_db],
../constants,
../utils/utils,
./chain_config
# ------------------------------------------------------------------------------
@ -79,10 +78,6 @@ proc toGenesisHeader*(
result.excessBlobGas = Opt.some g.excessBlobGas.get(0'u64)
result.parentBeaconBlockRoot = Opt.some g.parentBeaconBlockRoot.get(default(Hash32))
if fork >= Prague:
const EmptyRequestsHash = calcRequestsHash()
result.requestsHash = Opt.some(EmptyRequestsHash)
proc toGenesisHeader*(
genesis: Genesis;
fork: HardFork;

View File

@ -453,7 +453,6 @@ proc getNonce*(ac: LedgerRef, address: Address): AccountNonce =
proc getCode*(ac: LedgerRef,
address: Address,
returnHash: static[bool] = false): auto =
# Always returns non-nil!
let acc = ac.getAccount(address, false)
if acc.isNil:
when returnHash:
@ -526,7 +525,7 @@ proc getDelegateAddress*(ac: LedgerRef, address: Address): Address =
let delegateTo = parseDelegationAddress(code).valueOr:
return
delegateTo
proc getCommittedStorage*(ac: LedgerRef, address: Address, slot: UInt256): UInt256 =
let acc = ac.getAccount(address, false)
if acc.isNil:

View File

@ -11,6 +11,7 @@
{.push raises: [].}
import
std/sequtils,
".."/[db/ledger, constants],
"."/[code_stream, memory, message, stack, state],
"."/[types],
@ -21,8 +22,7 @@ import
../utils/utils,
../common/common,
eth/common/eth_types_rlp,
chronicles, chronos,
sets
chronicles, chronos
export
common
@ -260,39 +260,44 @@ template resolveCode*(c: Computation, address: Address): CodeBytesRef =
c.vmState.readOnlyStateDB.resolveCode(address)
proc newComputation*(vmState: BaseVMState, sysCall: bool, message: Message,
salt: ContractSalt = ZERO_CONTRACTSALT): Computation =
isPrecompile, keepStack: bool, salt: ContractSalt = ZERO_CONTRACTSALT): Computation =
new result
result.vmState = vmState
result.msg = message
result.memory = EvmMemory.init()
result.stack = EvmStack.init()
result.returnStack = @[]
result.gasMeter.init(message.gas)
result.sysCall = sysCall
result.keepStack = keepStack
if result.msg.isCreate():
result.msg.contractAddress = result.generateContractAddress(salt)
result.code = CodeStream.init(message.data)
message.data = @[]
else:
if vmState.fork >= FkPrague:
result.code = CodeStream.init(
vmState.readOnlyStateDB.resolveCode(message.codeAddress))
if not isPrecompile:
result.memory = EvmMemory.init()
result.stack = EvmStack.init()
if result.msg.isCreate():
result.msg.contractAddress = result.generateContractAddress(salt)
result.code = CodeStream.init(message.data)
message.data = @[]
else:
result.code = CodeStream.init(
vmState.readOnlyStateDB.getCode(message.codeAddress))
if vmState.fork >= FkPrague:
result.code = CodeStream.init(
vmState.readOnlyStateDB.resolveCode(message.codeAddress))
else:
result.code = CodeStream.init(
vmState.readOnlyStateDB.getCode(message.codeAddress))
func newComputation*(vmState: BaseVMState, sysCall: bool,
message: Message, code: CodeBytesRef): Computation =
message: Message, code: CodeBytesRef, isPrecompile, keepStack: bool, ): Computation =
new result
result.vmState = vmState
result.msg = message
result.memory = EvmMemory.init()
result.stack = EvmStack.init()
result.returnStack = @[]
result.gasMeter.init(message.gas)
result.code = CodeStream.init(code)
result.sysCall = sysCall
result.keepStack = keepStack
if not isPrecompile:
result.code = CodeStream.init(code)
result.memory = EvmMemory.init()
result.stack = EvmStack.init()
template gasCosts*(c: Computation): untyped =
c.vmState.gasCosts
@ -317,6 +322,12 @@ proc commit*(c: Computation) =
proc dispose*(c: Computation) =
c.vmState.stateDB.safeDispose(c.savePoint)
if c.stack != nil:
if c.keepStack:
c.finalStack = toSeq(c.stack.items())
c.stack.dispose()
c.stack = nil
c.savePoint = nil
proc rollback*(c: Computation) =

View File

@ -34,7 +34,7 @@ proc blockhashOp(cpt: VmCpt): EvmResultVoid =
## 0x40, Get the hash of one of the 256 most recent complete blocks.
template block256(top, number, conv) =
if number > high(BlockNumber).u256:
top = zero(UInt256)
conv(zero(UInt256), top)
else:
conv(cpt.getBlockHash(number.truncate(BlockNumber)), top)
@ -82,7 +82,7 @@ proc blobHashOp(cpt: VmCpt): EvmResultVoid =
if index < len:
conv(cpt.getVersionedHash(index).data, top)
else:
top = zero(UInt256)
conv(zero(UInt256), top)
cpt.stack.unaryWithTop(blob256)

View File

@ -21,6 +21,7 @@ import
../../../core/eip7702,
../../computation,
../../memory,
../../precompiles,
../../stack,
../../types,
../gas_costs,
@ -215,7 +216,9 @@ else:
# need to provide explicit <c> and <child> for capturing in chainTo proc()
# <memPos> and <memLen> are provided by value and need not be captured
var
child = newComputation(c.vmState, false, childMsg)
precompile = getPrecompile(c.fork, childMsg.codeAddress)
child = newComputation(
c.vmState, false, childMsg, isPrecompile = precompile.isSome(), keepStack = false)
c.chainTo(child):
if not child.shouldBurnGas:

View File

@ -67,7 +67,7 @@ else:
# need to provide explicit <c> and <child> for capturing in chainTo proc()
var
child = newComputation(c.vmState, false, childMsg, salt)
child = newComputation(c.vmState, false, childMsg, false, false, salt)
c.chainTo(child):
if not child.shouldBurnGas:

View File

@ -68,7 +68,7 @@ proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid
when evmc_enabled:
var topics: array[4, evmc_bytes32]
for i in 0 ..< topicCount:
topics[i].bytes = c.stack.lsPeekTopic(^(i+3))
topics[i].bytes = c.stack.lsPeekTopic(^(i+3)).data
c.host.emitLog(c.msg.contractAddress,
c.memory.read(memPos, len),
@ -77,7 +77,7 @@ proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid
var log: Log
log.topics = newSeqOfCap[Topic](topicCount)
for i in 0 ..< topicCount:
log.topics.add Bytes32 c.stack.lsPeekTopic(^(i+3))
log.topics.add c.stack.lsPeekTopic(^(i+3))
assign(log.data, c.memory.read(memPos, len))
log.address = c.msg.contractAddress

View File

@ -178,7 +178,7 @@ proc mstore8Op(cpt: VmCpt): EvmResultVoid =
proc sloadOp(cpt: VmCpt): EvmResultVoid =
## 0x54, Load word from storage.
template sload256(top, slot, conv) =
top = cpt.getStorage(slot)
conv(cpt.getStorage(slot), top)
cpt.stack.unaryWithTop(sload256)
proc sloadEIP2929Op(cpt: VmCpt): EvmResultVoid =
@ -186,7 +186,7 @@ proc sloadEIP2929Op(cpt: VmCpt): EvmResultVoid =
template sloadEIP2929(top, slot, conv) =
let gasCost = cpt.gasEip2929AccountCheck(cpt.msg.contractAddress, slot)
? cpt.opcodeGasCost(Sload, gasCost, reason = "sloadEIP2929")
top = cpt.getStorage(slot)
conv(cpt.getStorage(slot), top)
cpt.stack.unaryWithTop(sloadEIP2929)
# -------

View File

@ -197,8 +197,11 @@ proc executeOpcodes*(c: Computation, shouldPrepareTracer: bool = true) =
let fork = c.fork
block blockOne:
if c.continuation.isNil and c.execPrecompiles(fork):
break blockOne
if c.continuation.isNil:
let precompile = c.fork.getPrecompile(c.msg.codeAddress)
if precompile.isSome:
c.execPrecompile(precompile[])
break blockOne
let cont = c.continuation
if not cont.isNil:

View File

@ -70,10 +70,6 @@ func getMaxPrecompileAddr(fork: EVMFork): PrecompileAddresses =
func validPrecompileAddr(addrByte, maxPrecompileAddr: byte): bool =
(addrByte in PrecompileAddresses.low.byte .. maxPrecompileAddr)
func validPrecompileAddr(addrByte: byte, fork: EVMFork): bool =
let maxPrecompileAddr = getMaxPrecompileAddr(fork)
validPrecompileAddr(addrByte, maxPrecompileAddr.byte)
func getSignature(c: Computation): EvmResult[SigRes] =
# input is Hash, V, R, S
template data: untyped = c.msg.data
@ -719,16 +715,21 @@ func activePrecompilesList*(fork: EVMFork): seq[Address] =
for address in activePrecompiles(fork):
result.add address
proc execPrecompiles*(c: Computation, fork: EVMFork): bool =
proc getPrecompile*(fork: EVMFork, b: byte): Opt[PrecompileAddresses] =
let maxPrecompileAddr = getMaxPrecompileAddr(fork)
if validPrecompileAddr(b, maxPrecompileAddr.byte):
Opt.some(PrecompileAddresses(b))
else:
Opt.none(PrecompileAddresses)
proc getPrecompile*(fork: EVMFork, codeAddress: Address): Opt[PrecompileAddresses] =
for i in 0..18:
if c.msg.codeAddress.data[i] != 0:
return false
if codeAddress.data[i] != 0:
return Opt.none(PrecompileAddresses)
getPrecompile(fork, codeAddress.data[19])
let lb = c.msg.codeAddress.data[19]
if not validPrecompileAddr(lb, fork):
return false
let precompile = PrecompileAddresses(lb)
proc execPrecompile*(c: Computation, precompile: PrecompileAddresses) =
let fork = c.fork
let res = case precompile
of paEcRecover: ecRecover(c)
of paSha256: sha256(c)
@ -759,5 +760,3 @@ proc execPrecompiles*(c: Computation, fork: EVMFork): bool =
else:
# swallow any other precompiles errors
debug "execPrecompiles validation error", errCode = $res.error.code
true

View File

@ -8,182 +8,200 @@
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
# Type managing the EVM stack that comprises of 1024 256-bit words.
#
# The stack is a hot spot in EVM execution since it's used for practically every
# opcode. We use custom-allocated memory for several reasons, chiefly
# performance (at the time of writing, using a seq carried about 5% overhead on
# total EVM execution time):
#
# * no zeromem - the way the EVM uses the stack, it always writes full words
# meaning that whatever zeroing was done gets overwritten anyway - compilers
# are typically not smart enough to get rid of all of this
# * no reallocation - since we can allocate memory without zeroing, we can
# allocate the full stack length on creation and never grow / reallocate
# * less redundant range checking - we have to perform range checks manually and
# the compiler is not able to remove them consistently even though we range
# check manually
# * 32-byte alignment helps vector instruction optimization
#
# After calling `init`, the stack must be freed manually using `dispose`!
{.push raises: [].}
import
system/ansi_c,
stew/[assign2, ptrops],
stint,
eth/common/[base, addresses, hashes],
std/[macros],
std/typetraits,
./evm_errors,
./interpreter/utils/utils_numeric
const evmStackSize = 1024
## https://ethereum.org/en/developers/docs/evm/#evm-instructions
type
EvmStack* = ref object
values: seq[EvmStackElement]
values: ptr EvmStackElement
memory: pointer
len*: int
EvmStackElement = object
data {.align: 32.}: UInt256
EvmStackElement = UInt256
EvmStackInts = uint64 | uint | int | GasInt
EvmStackBytes32 = array[32, byte]
func len*(stack: EvmStack): int {.inline.} =
len(stack.values)
static:
# A few sanity checks because we skip the GC / parts of the nim type system:
doAssert sizeof(UInt256) == 32, "no padding etc"
doAssert supportsCopyMem(EvmStackElement), "byte-based ops must work sanely"
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
template toStackElem(v: UInt256, elem: EvmStackElement) =
template `[]`*(s: EvmStack, i: int): EvmStackElement =
s.values.offset(i)[]
template `[]`*(s: EvmStack, i: BackwardsIndex): EvmStackElement =
s.values.offset(s.len - int(i))[]
template `[]=`*(s: EvmStack, i: int, v: EvmStackElement) =
assign(s[i], v)
template `[]=`*(s: EvmStack, i: BackwardsIndex, v: EvmStackElement) =
assign(s[i], v)
template toStackElem(v: EvmStackElement, elem: EvmStackElement) =
elem = v
template toStackElem(v: UInt256, elem: EvmStackElement) =
elem.data = v
template toStackElem(v: EvmStackInts, elem: EvmStackElement) =
elem = v.u256
elem.data = v.u256
template toStackElem(v: Address, elem: EvmStackElement) =
elem.initFromBytesBE(v.data)
elem.data.initFromBytesBE(v.data)
template toStackElem(v: Hash32, elem: EvmStackElement) =
elem.initFromBytesBE(v.data)
elem.data.initFromBytesBE(v.data)
template toStackElem(v: openArray[byte], elem: EvmStackElement) =
doAssert(v.len <= 32)
elem.initFromBytesBE(v)
elem.data.initFromBytesBE(v)
template fromStackElem(elem: EvmStackElement, _: type UInt256): UInt256 =
elem
elem.data
func fromStackElem(elem: EvmStackElement, _: type Address): Address =
elem.to(Bytes32).to(Address)
elem.data.to(Bytes32).to(Address)
template fromStackElem(elem: EvmStackElement, _: type Hash32): Hash32 =
Hash32(elem.toBytesBE())
Hash32(elem.data.toBytesBE())
template fromStackElem(elem: EvmStackElement, _: type EvmStackBytes32): EvmStackBytes32 =
elem.toBytesBE()
func pushAux[T](stack: var EvmStack, value: T): EvmResultVoid =
if len(stack.values) > 1023:
return err(stackErr(StackFull))
stack.values.setLen(stack.values.len + 1)
toStackElem(value, stack.values[^1])
ok()
template fromStackElem(elem: EvmStackElement, _: type Bytes32): Bytes32 =
elem.data.toBytesBE().to(Bytes32)
func ensurePop(stack: EvmStack, expected: int): EvmResultVoid =
if stack.values.len < expected:
if stack.len < expected:
return err(stackErr(StackInsufficient))
ok()
func popAux(stack: var EvmStack, T: type): EvmResult[T] =
func popAux(stack: EvmStack, T: type): EvmResult[T] =
? ensurePop(stack, 1)
result = ok(fromStackElem(stack.values[^1], T))
stack.values.setLen(stack.values.len - 1)
func internalPopTuple(stack: var EvmStack, T: type, tupleLen: static[int]): EvmResult[T] =
? ensurePop(stack, tupleLen)
var
i = 0
v: T
let sz = stack.values.high
for f in fields(v):
f = fromStackElem(stack.values[sz - i], UInt256)
inc i
stack.values.setLen(sz - tupleLen + 1)
ok(v)
macro genTupleType(len: static[int], elemType: untyped): untyped =
result = nnkTupleConstr.newNimNode()
for i in 0 ..< len: result.add(elemType)
stack.len -= 1
ok(fromStackElem(stack[stack.len], T))
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
func push*(stack: var EvmStack,
value: EvmStackInts | UInt256 | Address | Hash32): EvmResultVoid =
pushAux(stack, value)
func push*(stack: EvmStack,
value: EvmStackElement | EvmStackInts | UInt256 | Address | Hash32): EvmResultVoid =
let len = stack.len
if len > 1023:
return err(stackErr(StackFull))
toStackElem(value, stack[len])
stack.len = len + 1
ok()
func push*(stack: var EvmStack, value: openArray[byte]): EvmResultVoid =
pushAux(stack, value)
func popInt*(stack: var EvmStack): EvmResult[UInt256] =
func popInt*(stack: EvmStack): EvmResult[UInt256] =
popAux(stack, UInt256)
func popSafeInt*(stack: var EvmStack): EvmResult[int] =
? ensurePop(stack, 1)
result = ok(fromStackElem(stack.values[^1], UInt256).safeInt)
stack.values.setLen(stack.values.len - 1)
func popMemRef*(stack: var EvmStack): EvmResult[int] =
? ensurePop(stack, 1)
result = ok(fromStackElem(stack.values[^1], UInt256).cleanMemRef)
stack.values.setLen(stack.values.len - 1)
func popInt*(stack: var EvmStack, numItems: static[int]): auto =
type T = genTupleType(numItems, UInt256)
stack.internalPopTuple(T, numItems)
func popAddress*(stack: var EvmStack): EvmResult[Address] =
func popAddress*(stack: EvmStack): EvmResult[Address] =
popAux(stack, Address)
func popTopic*(stack: var EvmStack): EvmResult[EvmStackBytes32] =
popAux(stack, EvmStackBytes32)
proc init*(_: type EvmStack): EvmStack =
let memory = c_malloc(evmStackSize * sizeof(EvmStackElement) + 31)
func init*(_: type EvmStack): EvmStack =
EvmStack(
values: newSeqOfCap[EvmStackElement](128)
values: cast[ptr EvmStackElement](((cast[uint](memory) + 31) div 32) * 32) ,
memory: memory, # Need to free the same pointer that we got from malloc
len: 0,
)
func swap*(stack: var EvmStack, position: int): EvmResultVoid =
## Perform a SWAP operation on the stack
let idx = position + 1
if idx < stack.values.len + 1:
(stack.values[^1], stack.values[^idx]) = (stack.values[^idx], stack.values[^1])
proc dispose*(stack: EvmStack) =
if stack[].memory != nil:
c_free(stack[].memory)
stack[].reset()
func swap*(stack: EvmStack, position: static int): EvmResultVoid =
## Swap the `top` and `top - position` items
let
idx = position + 1 # locals help compiler reason about overflows
len = stack.len
if stack.len >= idx:
let
l1 = len - 1
li = len - idx
let tmp {.noinit.} = stack[l1]
stack[l1] = stack[li]
stack[li] = tmp
ok()
else:
err(stackErr(StackInsufficient))
func dup*(stack: var EvmStack, position: int): EvmResultVoid =
## Perform a DUP operation on the stack
func dup*(stack: EvmStack, position: int): EvmResultVoid =
## Push copy of item at `top - position`
if position in 1 .. stack.len:
stack.push(stack.values[^position])
stack.push(stack[^position])
else:
err(stackErr(StackInsufficient))
func peek*(stack: EvmStack): EvmResult[UInt256] =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
ok(fromStackElem(stack.values[^1], UInt256))
? ensurePop(stack, 1)
ok(fromStackElem(stack[^1], UInt256))
func peekSafeInt*(stack: EvmStack): EvmResult[int] =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
ok(fromStackElem(stack.values[^1], UInt256).safeInt)
? ensurePop(stack, 1)
ok(fromStackElem(stack[^1], UInt256).safeInt)
func `[]`*(stack: EvmStack, i: BackwardsIndex, T: typedesc): EvmResult[T] =
? ensurePop(stack, int(i))
ok(fromStackElem(stack.values[i], T))
ok(fromStackElem(stack[i], T))
func peekInt*(stack: EvmStack): EvmResult[UInt256] =
? ensurePop(stack, 1)
ok(fromStackElem(stack.values[^1], UInt256))
ok(fromStackElem(stack[^1], UInt256))
func peekAddress*(stack: EvmStack): EvmResult[Address] =
? ensurePop(stack, 1)
ok(fromStackElem(stack.values[^1], Address))
ok(fromStackElem(stack[^1], Address))
func top*(stack: EvmStack,
value: EvmStackInts | UInt256 | Address | Hash32): EvmResultVoid =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
toStackElem(value, stack.values[^1])
? ensurePop(stack, 1)
toStackElem(value, stack[^1])
ok()
iterator items*(stack: EvmStack): UInt256 =
for v in stack.values:
yield v
for i in 0..<stack.len:
yield stack[i].data
iterator pairs*(stack: EvmStack): (int, UInt256) =
for i, v in stack.values:
yield (i, v)
for i in 0..<stack.len:
yield (i, stack[i].data)
# ------------------------------------------------------------------------------
# Public functions with less safety
@ -194,63 +212,77 @@ template lsCheck*(stack: EvmStack, expected: int): EvmResultVoid =
func lsTop*(stack: EvmStack,
value: EvmStackInts | UInt256 | Address | Hash32) =
toStackElem(value, stack.values[^1])
toStackElem(value, stack[^1])
func lsTop*(stack: var EvmStack, value: openArray[byte]) =
toStackElem(value, stack.values[^1])
func lsTop*(stack: EvmStack, value: openArray[byte]) =
toStackElem(value, stack[^1])
func lsPeekInt*(stack: EvmStack, i: BackwardsIndex): UInt256 =
fromStackElem(stack.values[i], UInt256)
fromStackElem(stack[i], UInt256)
func lsPeekAddress*(stack: EvmStack, i: BackwardsIndex): Address =
fromStackElem(stack.values[i], Address)
fromStackElem(stack[i], Address)
func lsPeekMemRef*(stack: EvmStack, i: BackwardsIndex): int =
fromStackElem(stack.values[i], UInt256).cleanMemRef
fromStackElem(stack[i], UInt256).cleanMemRef
func lsPeekSafeInt*(stack: EvmStack, i: BackwardsIndex): int =
fromStackElem(stack.values[i], UInt256).safeInt
fromStackElem(stack[i], UInt256).safeInt
func lsPeekTopic*(stack: EvmStack, i: BackwardsIndex): EvmStackBytes32 =
fromStackElem(stack.values[i], EvmStackBytes32)
func lsPeekTopic*(stack: EvmStack, i: BackwardsIndex): Bytes32 =
fromStackElem(stack[i], Bytes32)
func lsShrink*(stack: EvmStack, x: int) =
stack.values.setLen(stack.values.len - x)
stack.len -= x
template binaryOp*(stack: EvmStack, binOp): EvmResultVoid =
if stack.values.len >= 2:
stack.values[^2] = binOp(stack.values[^1], stack.values[^2])
stack.values.setLen(stack.values.len - 1)
let len = stack.len
if len >= 2:
let
l1 = len - 1
l2 = len - 2
stack[l2].data = binOp(stack[l1].data, stack[l2].data)
stack.len = l1
EvmResultVoid.ok()
else:
EvmResultVoid.err(stackErr(StackInsufficient))
template unaryOp*(stack: EvmStack, unOp): EvmResultVoid =
if stack.values.len >= 1:
stack.values[^1] = unOp(stack.values[^1])
let len = stack.len
if len >= 1:
let l1 = len - 1
stack[l1].data = unOp(stack[l1].data)
EvmResultVoid.ok()
else:
EvmResultVoid.err(stackErr(StackInsufficient))
template binaryWithTop*(stack: EvmStack, binOp): EvmResultVoid =
if stack.values.len >= 2:
binOp(stack.values[^2], stack.values[^1], stack.values[^2])
stack.values.setLen(stack.values.len - 1)
let len = stack.len
if len >= 2:
let
l1 = len - 1
l2 = len - 2
binOp(stack[l2].data, stack[l1].data, stack[l2].data)
stack.len = l1
EvmResultVoid.ok()
else:
EvmResultVoid.err(stackErr(StackInsufficient))
template unaryWithTop*(stack: EvmStack, unOp): EvmResultVoid =
if stack.values.len >= 1:
unOp(stack.values[^1], stack.values[^1], toStackElem)
let len = stack.len
if len >= 1:
let l1 = len - 1
unOp(stack[l1], stack[l1].data, toStackElem)
EvmResultVoid.ok()
else:
EvmResultVoid.err(stackErr(StackInsufficient))
template unaryAddress*(stack: EvmStack, unOp): EvmResultVoid =
if stack.values.len >= 1:
let address = fromStackElem(stack.values[^1], Address)
toStackElem(unOp(address), stack.values[^1])
let len = stack.len
if len >= 1:
let l1 = len - 1
let address = fromStackElem(stack[l1], Address)
toStackElem(unOp(address), stack[l1])
EvmResultVoid.ok()
else:
EvmResultVoid.err(stackErr(StackInsufficient))

View File

@ -14,6 +14,8 @@ import
../db/ledger,
../common/[common, evmforks]
export stack, memory
# this import not guarded by `when defined(evmc_enabled)`
# because we want to use evmc types such as evmc_call_kind
# and evmc_flags
@ -76,7 +78,6 @@ type
msg*: Message
memory*: EvmMemory
stack*: EvmStack
returnStack*: seq[int]
gasMeter*: GasMeter
code*: CodeStream
output*: seq[byte]
@ -93,6 +94,8 @@ type
parent*, child*: Computation
continuation*: proc(): EvmResultVoid {.gcsafe, raises: [].}
sysCall*: bool
keepStack*: bool
finalStack*: seq[UInt256]
Error* = ref object
evmcStatus*: evmc_status_code

View File

@ -132,7 +132,7 @@ proc preExecComputation(vmState: BaseVMState, call: CallParams): int64 =
gasRefund
proc setupHost(call: CallParams): TransactionHost =
proc setupHost(call: CallParams, keepStack: bool): TransactionHost =
let vmState = call.vmState
vmState.txCtx = TxContext(
origin : call.origin.get(call.sender),
@ -162,6 +162,8 @@ proc setupHost(call: CallParams): TransactionHost =
let gasRefund = if call.sysCall: 0
else: preExecComputation(vmState, call)
let isPrecompile =
not call.isCreate and vmState.fork.getPrecompile(host.msg.code_address.fromEvmc).isSome()
# Generate new contract address, prepare code, and update message `recipient`
# with the contract address. This differs from the previous Nimbus EVM API.
@ -179,7 +181,9 @@ proc setupHost(call: CallParams): TransactionHost =
else:
# TODO: Share the underlying data, but only after checking this does not
# cause problems with the database.
if host.vmState.fork >= FkPrague:
if isPrecompile:
code = nil
elif host.vmState.fork >= FkPrague:
code = host.vmState.readOnlyStateDB.resolveCode(host.msg.code_address.fromEvmc)
else:
code = host.vmState.readOnlyStateDB.getCode(host.msg.code_address.fromEvmc)
@ -190,8 +194,10 @@ proc setupHost(call: CallParams): TransactionHost =
host.input = call.input
host.msg.input_data = host.input[0].addr
let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, call.sysCall, cMsg, code)
let
cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(
vmState, call.sysCall, cMsg, code, isPrecompile = isPrecompile, keepStack = keepStack)
host.code = code
else:
@ -202,8 +208,10 @@ proc setupHost(call: CallParams): TransactionHost =
host.input = call.input
host.msg.input_data = host.input[0].addr
let cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(vmState, call.sysCall, cMsg)
let
cMsg = hostToComputationMessage(host.msg)
host.computation = newComputation(
vmState, call.sysCall, cMsg, isPrecompile = isPrecompile, keepStack = keepStack)
host.computation.gasMeter.refundGas(gasRefund)
vmState.captureStart(host.computation, call.sender, call.to,
@ -290,7 +298,7 @@ proc finishRunningComputation(
let gasUsed = host.msg.gas.GasInt - gasRemaining
host.vmState.captureEnd(c, c.output, gasUsed, c.errorOpt)
when T is CallResult:
when T is CallResult|DebugCallResult:
# Collecting the result can be unnecessarily expensive when (re)-processing
# transactions
if c.isError:
@ -299,8 +307,10 @@ proc finishRunningComputation(
result.output = system.move(c.output)
result.contractAddress = if call.isCreate: c.msg.contractAddress
else: default(HostAddress)
result.stack = move(c.stack)
result.memory = move(c.memory)
when T is DebugCallResult:
result.stack = move(c.finalStack)
result.memory = move(c.memory)
elif T is GasInt:
result = call.gasLimit - gasRemaining
elif T is string:
@ -312,7 +322,7 @@ proc finishRunningComputation(
{.error: "Unknown computation output".}
proc runComputation*(call: CallParams, T: type): T =
let host = setupHost(call)
let host = setupHost(call, keepStack = T is DebugCallResult)
prepareToRunComputation(host, call)
when defined(evmc_enabled):

View File

@ -204,6 +204,6 @@ proc txCallEvm*(tx: Transaction,
proc testCallEvm*(tx: Transaction,
sender: Address,
vmState: BaseVMState): CallResult =
vmState: BaseVMState): DebugCallResult =
let call = callParamsForTest(tx, sender, vmState)
runComputation(call, CallResult)
runComputation(call, DebugCallResult)

View File

@ -17,6 +17,8 @@ import
../core/eip7702,
./host_types
export types
type
# Standard call parameters.
CallParams* = object
@ -40,12 +42,14 @@ type
# Standard call result. (Some fields are beyond what EVMC can return,
# and must only be used from tests because they will not always be set).
CallResult* = object
CallResult* = object of RootObj
error*: string # Something if the call failed.
gasUsed*: GasInt # Gas used by the call.
contractAddress*: Address # Created account (when `isCreate`).
output*: seq[byte] # Output data.
stack*: EvmStack # EVM stack on return (for test only).
DebugCallResult* = object of CallResult
stack*: seq[UInt256] # EVM stack on return (for test only).
memory*: EvmMemory # EVM memory on return (for test only).
template isCreate(tx: Transaction): bool =

View File

@ -129,9 +129,9 @@ proc evmcExecComputation*(host: TransactionHost): EvmcResult =
result = vm.execute(vm, hostInterface.unsafeAddr, hostContext,
evmc_revision(host.vmState.fork.ord), host.msg,
if host.code.len > 0: host.code.bytes[0].unsafeAddr
if host.code != nil and host.code.len > 0: host.code.bytes[0].unsafeAddr
else: nil,
host.code.len.csize_t)
if host.code != nil: host.code.len.csize_t else: 0)
host.showCallReturn(result)

View File

@ -13,7 +13,7 @@ import
stew/ptrops,
stew/saturation_arith,
stint,
../evm/types,
../evm/[types, precompiles],
../evm/interpreter_dispatch,
../utils/utils,
"."/[host_types, host_trace]
@ -34,7 +34,8 @@ proc beforeExecCreateEvmcNested(host: TransactionHost,
value: m.value.fromEvmc,
data: @(makeOpenArray(m.input_data, m.input_size.int))
)
return newComputation(host.vmState, false, childMsg,
return newComputation(host.vmState, false, childMsg, isPrecompile = false,
keepStack = false,
cast[ContractSalt](m.create2_salt))
proc afterExecCreateEvmcNested(host: TransactionHost, child: Computation,
@ -71,7 +72,8 @@ proc beforeExecCallEvmcNested(host: TransactionHost,
data: @(makeOpenArray(m.input_data, m.input_size.int)),
flags: m.flags,
)
return newComputation(host.vmState, false, childMsg)
let isPrecompile = getPrecompile(host.vmState.fork, childMsg.codeAddress).isSome()
newComputation(host.vmState, false, childMsg, isPrecompile = isPrecompile, keepStack = false)
proc afterExecCallEvmcNested(host: TransactionHost, child: Computation,
res: var EvmcResult) {.inline.} =

View File

@ -40,8 +40,7 @@ func calcRequestsHash*(requests: varargs[seq[byte]]): Hash32 =
var ctx: sha256
ctx.init()
for i, data in requests:
if data.len > 0:
ctx.update(calcHash(i.byte, data).data)
ctx.update(calcHash(i.byte, data).data)
ctx.finish(result.data)
ctx.clear()

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
"payload": {
"baseFeePerGas": "0x7",
"blobGasUsed": "0x40000",
"blockHash": "0x2ad74d1c5c12bcdedbdc821ae5d18f44e732b4096b6496a23bd9483d202003c5",
"blockHash": "0x187307d7dc9beb87af2a1d8340e9f17a3bbe4738963daeaf6d8e13b27b2d6a7f",
"blockNumber": "0x94b2",
"excessBlobGas": "0x0",
"extraData": "0xd883010e0c846765746888676f312e32332e32856c696e7578",

View File

@ -281,7 +281,7 @@ proc initVMEnv*(network: string): BaseVMState =
BaseVMState.new(parent, header, com)
proc verifyAsmResult(vmState: BaseVMState, boa: Assembler, asmResult: CallResult): bool =
proc verifyAsmResult(vmState: BaseVMState, boa: Assembler, asmResult: DebugCallResult): bool =
let com = vmState.com
if not asmResult.isError:
if boa.success == false:

View File

@ -26,24 +26,20 @@ import
template testPush(value: untyped, expected: untyped): untyped =
privateAccess(EvmStack)
var stack = EvmStack.init()
defer: stack.dispose()
check stack.push(value).isOk
check(stack.values == @[expected])
func toBytes(s: string): seq[byte] =
cast[seq[byte]](s)
func bigEndianToInt(value: openArray[byte]): UInt256 =
result.initFromBytesBE(value)
check(toSeq(stack.items()) == @[expected])
proc runStackTests() =
suite "Stack tests":
test "push only valid":
testPush(0'u, 0.u256)
testPush(UINT_256_MAX, UINT_256_MAX)
testPush("ves".toBytes, "ves".toBytes.bigEndianToInt)
# testPush("ves".toBytes, "ves".toBytes.bigEndianToInt)
test "push does not allow stack to exceed 1024":
var stack = EvmStack.init()
defer: stack.dispose()
for z in 0 ..< 1024:
check stack.push(z.uint).isOk
check(stack.len == 1024)
@ -51,6 +47,7 @@ proc runStackTests() =
test "dup does not allow stack to exceed 1024":
var stack = EvmStack.init()
defer: stack.dispose()
check stack.push(1.u256).isOk
for z in 0 ..< 1023:
check stack.dup(1).isOk
@ -59,6 +56,7 @@ proc runStackTests() =
test "pop returns latest stack item":
var stack = EvmStack.init()
defer: stack.dispose()
for element in @[1'u, 2'u, 3'u]:
check stack.push(element).isOk
check(stack.popInt.get == 3.u256)
@ -66,35 +64,40 @@ proc runStackTests() =
test "swap correct":
privateAccess(EvmStack)
var stack = EvmStack.init()
defer: stack.dispose()
for z in 0 ..< 5:
check stack.push(z.uint).isOk
check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256])
check(toSeq(stack.items()) == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256])
check stack.swap(3).isOk
check(stack.values == @[0.u256, 4.u256, 2.u256, 3.u256, 1.u256])
check(toSeq(stack.items()) == @[0.u256, 4.u256, 2.u256, 3.u256, 1.u256])
check stack.swap(1).isOk
check(stack.values == @[0.u256, 4.u256, 2.u256, 1.u256, 3.u256])
check(toSeq(stack.items()) == @[0.u256, 4.u256, 2.u256, 1.u256, 3.u256])
test "dup correct":
privateAccess(EvmStack)
var stack = EvmStack.init()
defer: stack.dispose()
for z in 0 ..< 5:
check stack.push(z.uint).isOk
check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256])
check(toSeq(stack.items()) == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256])
check stack.dup(1).isOk
check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256])
check(toSeq(stack.items()) == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256])
check stack.dup(5).isOk
check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256, 1.u256])
check(toSeq(stack.items()) == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256, 1.u256])
test "pop raises InsufficientStack appropriately":
var stack = EvmStack.init()
defer: stack.dispose()
check stack.popInt().error.code == EvmErrorCode.StackInsufficient
test "swap raises InsufficientStack appropriately":
var stack = EvmStack.init()
defer: stack.dispose()
check stack.swap(0).error.code == EvmErrorCode.StackInsufficient
test "dup raises InsufficientStack appropriately":
var stack = EvmStack.init()
defer: stack.dispose()
check stack.dup(0).error.code == EvmErrorCode.StackInsufficient
test "binary operations raises InsufficientStack appropriately":
@ -102,8 +105,9 @@ proc runStackTests() =
# ./tests/fixtures/VMTests/vmArithmeticTest/mulUnderFlow.json
var stack = EvmStack.init()
defer: stack.dispose()
check stack.push(123).isOk
check stack.popInt(2).error.code == EvmErrorCode.StackInsufficient
check stack.binaryOp(`+`).error.code == EvmErrorCode.StackInsufficient
proc memory32: EvmMemory =
result = EvmMemory.init(32)

View File

@ -12,8 +12,7 @@ import
std/[os],
unittest2,
../nimbus/config,
../nimbus/common/common,
../nimbus/utils/utils
../nimbus/common/common
const
baseDir = [".", "tests", ".."/"tests", $DirSep] # path containg repo
@ -133,21 +132,6 @@ proc customGenesisTest() =
check com.genesisHeader.blockHash == genesisHash
check com.chainId == 17000.ChainId
test "Prague genesis":
# pre Prague
var cg: NetworkParams
check loadNetworkParams("mekong.json".findFilePath, cg)
var com = CommonRef.new(newCoreDbRef DefaultDbMemory, params = cg)
check com.genesisHeader.requestsHash.isNone
# post prague
const EmptyRequestsHash = hash32"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
check loadNetworkParams("prague.json".findFilePath, cg)
com = CommonRef.new(newCoreDbRef DefaultDbMemory, params = cg)
check com.genesisHeader.requestsHash.isSome
check com.genesisHeader.requestsHash.get == EmptyRequestsHash
check calcRequestsHash(default(seq[byte]), default(seq[byte]), default(seq[byte])) == EmptyRequestsHash
proc genesisMain*() =
genesisTest()
customGenesisTest()

View File

@ -18,7 +18,7 @@
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"currentExcessBlobGas": "0x2b80000",
"blobGasUsed": "0x0",
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"requestsHash": "0x6036c41849da9c076ed79654d434017387a88fb833c2856b32e18218b3341c5f",
"requests": [
"0x",
"0x",

View File

@ -34,7 +34,7 @@
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"currentExcessBlobGas": "0x2b80000",
"blobGasUsed": "0x0",
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"requestsHash": "0x6036c41849da9c076ed79654d434017387a88fb833c2856b32e18218b3341c5f",
"requests": [
"0x",
"0x",

View File

@ -34,7 +34,7 @@
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"currentExcessBlobGas": "0x2b80000",
"blobGasUsed": "0x0",
"requestsHash": "0xf34c7b46f404c6c6f91b540c098b6dfe3f59b7e50ad155807c8c7d2221e52241",
"requestsHash": "0xa5c19ed76c01fe25a67b7ef8c227caa34951e8c7e74168408b5be0861dac686d",
"requests": [
"0x81521c60874daf5b425c21e44caf045c4d475e8b33a557a28cee3c46ef9cf9bd95b4c75a0bb629981b40d0102452dd4c020000000000000000000000332e43696a505ef45b9319973785f837ce5267b9000065cd1d0000008c8f2647f342d2c3e8fd07c6b3b9b16383ac11c4be6a6962c7fc18a789daee5fac20ee0bbe4a10383759aaffacacb72b0d67f998730cdf4995fe73afe434dfce2803b343606f67fc4995597c0af9e0fe9ed00006e5889bec29171f670e7d9be20000000000000000",
"0x",

View File

@ -60,7 +60,7 @@
"gasUsed": "0x15fa9",
"currentBaseFee": "0x7",
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"requestsHash": "0x6036c41849da9c076ed79654d434017387a88fb833c2856b32e18218b3341c5f",
"requests": [
"0x",
"0x",