nimbus-eth1/nimbus/evm/stack.nim

257 lines
8.1 KiB
Nim

# Nimbus
# Copyright (c) 2018-2024 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.
{.push raises: [].}
import
std/[macros],
stew/assign2,
eth/common,
./evm_errors,
./interpreter/utils/utils_numeric
type
EvmStack* = ref object
values: seq[EvmStackElement]
EvmStackElement = UInt256
EvmStackInts = uint64 | uint | int | GasInt
EvmStackBytes32 = array[32, byte]
func len*(stack: EvmStack): int {.inline.} =
len(stack.values)
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
template toStackElem(v: UInt256, elem: EvmStackElement) =
elem = v
template toStackElem(v: EvmStackInts, elem: EvmStackElement) =
elem = v.u256
template toStackElem(v: EthAddress, elem: EvmStackElement) =
elem.initFromBytesBE(v.data)
template toStackElem(v: Hash256, elem: EvmStackElement) =
elem.initFromBytesBE(v.data)
template toStackElem(v: openArray[byte], elem: EvmStackElement) =
doAssert(v.len <= 32)
elem.initFromBytesBE(v)
template fromStackElem(elem: EvmStackElement, _: type UInt256): UInt256 =
elem
func fromStackElem(elem: EvmStackElement, _: type EthAddress): EthAddress =
elem.to(Bytes32).to(EthAddress)
template fromStackElem(elem: EvmStackElement, _: type Hash256): Hash256 =
Hash32(elem.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()
func ensurePop(stack: EvmStack, expected: int): EvmResultVoid =
if stack.values.len < expected:
return err(stackErr(StackInsufficient))
ok()
func popAux(stack: var 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)
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
func push*(stack: var EvmStack,
value: EvmStackInts | UInt256 | EthAddress | Hash256): EvmResultVoid =
pushAux(stack, value)
func push*(stack: var EvmStack, value: openArray[byte]): EvmResultVoid =
pushAux(stack, value)
func popInt*(stack: var 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[EthAddress] =
popAux(stack, EthAddress)
func popTopic*(stack: var EvmStack): EvmResult[EvmStackBytes32] =
popAux(stack, EvmStackBytes32)
func init*(_: type EvmStack): EvmStack =
EvmStack(
values: newSeqOfCap[EvmStackElement](128)
)
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])
ok()
else:
err(stackErr(StackInsufficient))
func dup*(stack: var EvmStack, position: int): EvmResultVoid =
## Perform a DUP operation on the stack
if position in 1 .. stack.len:
stack.push(stack.values[^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))
func peekSafeInt*(stack: EvmStack): EvmResult[int] =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
ok(fromStackElem(stack.values[^1], UInt256).safeInt)
func `[]`*(stack: EvmStack, i: BackwardsIndex, T: typedesc): EvmResult[T] =
? ensurePop(stack, int(i))
ok(fromStackElem(stack.values[i], T))
func peekInt*(stack: EvmStack): EvmResult[UInt256] =
? ensurePop(stack, 1)
ok(fromStackElem(stack.values[^1], UInt256))
func peekAddress*(stack: EvmStack): EvmResult[EthAddress] =
? ensurePop(stack, 1)
ok(fromStackElem(stack.values[^1], EthAddress))
func top*(stack: EvmStack,
value: EvmStackInts | UInt256 | EthAddress | Hash256): EvmResultVoid =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
toStackElem(value, stack.values[^1])
ok()
iterator items*(stack: EvmStack): UInt256 =
for v in stack.values:
yield v
iterator pairs*(stack: EvmStack): (int, UInt256) =
for i, v in stack.values:
yield (i, v)
# ------------------------------------------------------------------------------
# Public functions with less safety
# ------------------------------------------------------------------------------
template lsCheck*(stack: EvmStack, expected: int): EvmResultVoid =
ensurePop(stack, expected)
func lsTop*(stack: EvmStack,
value: EvmStackInts | UInt256 | EthAddress | Hash256) =
toStackElem(value, stack.values[^1])
func lsTop*(stack: var EvmStack, value: openArray[byte]) =
toStackElem(value, stack.values[^1])
func lsPeekInt*(stack: EvmStack, i: BackwardsIndex): UInt256 =
fromStackElem(stack.values[i], UInt256)
func lsPeekAddress*(stack: EvmStack, i: BackwardsIndex): EthAddress =
fromStackElem(stack.values[i], EthAddress)
func lsPeekMemRef*(stack: EvmStack, i: BackwardsIndex): int =
fromStackElem(stack.values[i], UInt256).cleanMemRef
func lsPeekSafeInt*(stack: EvmStack, i: BackwardsIndex): int =
fromStackElem(stack.values[i], UInt256).safeInt
func lsPeekTopic*(stack: EvmStack, i: BackwardsIndex): EvmStackBytes32 =
fromStackElem(stack.values[i], EvmStackBytes32)
func lsShrink*(stack: EvmStack, x: int) =
stack.values.setLen(stack.values.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)
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])
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)
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)
EvmResultVoid.ok()
else:
EvmResultVoid.err(stackErr(StackInsufficient))
template unaryAddress*(stack: EvmStack, unOp): EvmResultVoid =
if stack.values.len >= 1:
let address = fromStackElem(stack.values[^1], EthAddress)
toStackElem(unOp(address), stack.values[^1])
EvmResultVoid.ok()
else:
EvmResultVoid.err(stackErr(StackInsufficient))