nimbus-eth1/nimbus/evm/interpreter/op_handlers/oph_arithmetic.nim

496 lines
13 KiB
Nim
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
## EVM Opcode Handlers: Arithmetic and Logic Operators
## ===================================================
##
{.push raises: [].}
import
std/options,
../../../constants,
../../computation,
../../evm_errors,
../../stack,
../../types,
../op_codes,
../gas_costs,
../utils/utils_numeric,
./oph_defs,
eth/common
func slt(x, y: UInt256): bool =
type SignedWord = signedWordType(UInt256)
let x_neg = cast[SignedWord](x.mostSignificantWord) < 0
let y_neg = cast[SignedWord](y.mostSignificantWord) < 0
if x_neg xor y_neg: x_neg else: x < y
# ------------------------------------------------------------------------------
# Private, op handlers implementation
# ------------------------------------------------------------------------------
proc addOp (k: var VmCtx): EvmResultVoid =
## 0x01, Addition
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push(lhs + rhs)
proc mulOp(k: var VmCtx): EvmResultVoid =
## 0x02, Multiplication
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push(lhs * rhs)
proc subOp(k: var VmCtx): EvmResultVoid =
## 0x03, Substraction
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push(lhs - rhs)
proc divideOp(k: var VmCtx): EvmResultVoid =
## 0x04, Division
let
(lhs, rhs) = ? k.cpt.stack.popInt(2)
value = if rhs.isZero:
# EVM special casing of div by 0
zero(UInt256)
else:
lhs div rhs
k.cpt.stack.push value
proc sdivOp(k: var VmCtx): EvmResultVoid =
## 0x05, Signed division
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
var r: UInt256
if rhs.isZero.not:
var a = lhs
var b = rhs
var signA, signB: bool
extractSign(a, signA)
extractSign(b, signB)
r = a div b
setSign(r, signA xor signB)
k.cpt.stack.push(r)
proc moduloOp(k: var VmCtx): EvmResultVoid =
## 0x06, Modulo
let
(lhs, rhs) = ? k.cpt.stack.popInt(2)
value = if rhs.isZero:
zero(UInt256)
else:
lhs mod rhs
k.cpt.stack.push value
proc smodOp(k: var VmCtx): EvmResultVoid =
## 0x07, Signed modulo
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
var r: UInt256
if rhs.isZero.not:
var sign: bool
var v = lhs
var m = rhs
extractSign(m, sign)
extractSign(v, sign)
r = v mod m
setSign(r, sign)
k.cpt.stack.push(r)
proc addmodOp(k: var VmCtx): EvmResultVoid =
## 0x08, Modulo addition
## Intermediate computations do not roll over at 2^256
let
(lhs, rhs, modulus) = ? k.cpt.stack.popInt(3)
value = if modulus.isZero:
zero(UInt256)
else:
addmod(lhs, rhs, modulus)
k.cpt.stack.push value
proc mulmodOp(k: var VmCtx): EvmResultVoid =
## 0x09, Modulo multiplication
## Intermediate computations do not roll over at 2^256
let
(lhs, rhs, modulus) = ? k.cpt.stack.popInt(3)
value = if modulus.isZero:
zero(UInt256)
else:
mulmod(lhs, rhs, modulus)
k.cpt.stack.push value
proc expOp(k: var VmCtx): EvmResultVoid =
## 0x0A, Exponentiation
let (base, exponent) = ? k.cpt.stack.popInt(2)
? k.cpt.opcodeGastCost(Exp,
k.cpt.gasCosts[Exp].d_handler(exponent),
reason = "EXP: exponent bytes")
let value = if not base.isZero:
base.pow(exponent)
elif exponent.isZero:
# https://github.com/ethereum/yellowpaper/issues/257
# https://github.com/ethereum/tests/pull/460
# https://github.com/ewasm/evm2wasm/issues/137
1.u256
else:
zero(UInt256)
k.cpt.stack.push value
proc signExtendOp(k: var VmCtx): EvmResultVoid =
## 0x0B, Sign extend
## Extend length of twos complement signed integer.
let (bits, value) = ? k.cpt.stack.popInt(2)
var res: UInt256
if bits <= 31.u256:
let
one = 1.u256
testBit = bits.truncate(int) * 8 + 7
bitPos = one shl testBit
mask = bitPos - one
if not isZero(value and bitPos):
res = value or (not mask)
else:
res = value and mask
else:
res = value
k.cpt.stack.push res
proc ltOp(k: var VmCtx): EvmResultVoid =
## 0x10, Less-than comparison
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push((lhs < rhs).uint.u256)
proc gtOp(k: var VmCtx): EvmResultVoid =
## 0x11, Greater-than comparison
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push((lhs > rhs).uint.u256)
proc sltOp(k: var VmCtx): EvmResultVoid =
## 0x12, Signed less-than comparison
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push(slt(lhs, rhs).uint.u256)
proc sgtOp(k: var VmCtx): EvmResultVoid =
## 0x13, Signed greater-than comparison
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
# Arguments are swapped and SLT is used.
k.cpt.stack.push(slt(rhs, lhs).uint.u256)
proc eqOp(k: var VmCtx): EvmResultVoid =
## 0x14, Equality comparison
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push((lhs == rhs).uint.u256)
proc isZeroOp(k: var VmCtx): EvmResultVoid =
## 0x15, Check if zero
let value = ? k.cpt.stack.popInt()
k.cpt.stack.push(value.isZero.uint.u256)
proc andOp(k: var VmCtx): EvmResultVoid =
## 0x16, Bitwise AND
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push(lhs and rhs)
proc orOp(k: var VmCtx): EvmResultVoid =
## 0x17, Bitwise OR
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push(lhs or rhs)
proc xorOp(k: var VmCtx): EvmResultVoid =
## 0x18, Bitwise XOR
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
k.cpt.stack.push(lhs xor rhs)
proc notOp(k: var VmCtx): EvmResultVoid =
## 0x19, Check if zero
let value = ? k.cpt.stack.popInt()
k.cpt.stack.push(value.not)
proc byteOp(k: var VmCtx): EvmResultVoid =
## 0x20, Retrieve single byte from word.
let
(position, value) = ? k.cpt.stack.popInt(2)
val = if position >= 32.u256:
zero(UInt256)
else:
let pos = position.truncate(int)
when system.cpuEndian == bigEndian:
cast[array[32, byte]](value)[pos].u256
else:
cast[array[32, byte]](value)[31 - pos].u256
k.cpt.stack.push val
# Constantinople's new opcodes
proc shlOp(k: var VmCtx): EvmResultVoid =
let (shift, num) = ? k.cpt.stack.popInt(2)
let shiftLen = shift.safeInt
if shiftLen >= 256:
k.cpt.stack.push 0
else:
k.cpt.stack.push(num shl shiftLen)
proc shrOp(k: var VmCtx): EvmResultVoid =
let (shift, num) = ? k.cpt.stack.popInt(2)
let shiftLen = shift.safeInt
if shiftLen >= 256:
k.cpt.stack.push 0
else:
# uint version of `shr`
k.cpt.stack.push(num shr shiftLen)
proc sarOp(k: var VmCtx): EvmResultVoid =
let
shiftLen = ? k.cpt.stack.popSafeInt()
num256 = ? k.cpt.stack.popInt()
num = cast[Int256](num256)
if shiftLen >= 256:
if num.isNegative:
k.cpt.stack.push(cast[UInt256]((-1).i256))
else:
k.cpt.stack. push 0
else:
# int version of `shr` then force the result
# into uint256
k.cpt.stack.push(cast[UInt256](num shr shiftLen))
# ------------------------------------------------------------------------------
# Public, op exec table entries
# ------------------------------------------------------------------------------
const
VmOpExecArithmetic*: seq[VmOpExec] = @[
(opCode: Add, ## 0x01, Addition
forks: VmOpAllForks,
name: "add",
info: "Addition operation",
exec: (prep: VmOpIgnore,
run: VmOpFn addOp,
post: VmOpIgnore)),
(opCode: Mul, ## 0x02, Multiplication
forks: VmOpAllForks,
name: "mul",
info: "Multiplication operation",
exec: (prep: VmOpIgnore,
run: mulOp,
post: VmOpIgnore)),
(opCode: Sub, ## 0x03, Subtraction
forks: VmOpAllForks,
name: "sub",
info: "Subtraction operation",
exec: (prep: VmOpIgnore,
run: subOp,
post: VmOpIgnore)),
(opCode: Div, ## 0x04, Division
forks: VmOpAllForks,
name: "divide",
info: "Integer division operation",
exec: (prep: VmOpIgnore,
run: divideOp,
post: VmOpIgnore)),
(opCode: Sdiv, ## 0x05, Signed division
forks: VmOpAllForks,
name: "sdiv",
info: "Signed integer division operation (truncated)",
exec: (prep: VmOpIgnore,
run: sdivOp,
post: VmOpIgnore)),
(opCode: Mod, ## 0x06, Modulo
forks: VmOpAllForks,
name: "modulo",
info: "Modulo remainder operation",
exec: (prep: VmOpIgnore,
run: moduloOp,
post: VmOpIgnore)),
(opCode: Smod, ## 0x07, Signed modulo
forks: VmOpAllForks,
name: "smod",
info: "Signed modulo remainder operation",
exec: (prep: VmOpIgnore,
run: smodOp,
post: VmOpIgnore)),
(opCode: Addmod, ## 0x08, Modulo addition, Intermediate
## computations do not roll over at 2^256
forks: VmOpAllForks,
name: "addmod",
info: "Modulo addition operation",
exec: (prep: VmOpIgnore,
run: addmodOp,
post: VmOpIgnore)),
(opCode: Mulmod, ## 0x09, Modulo multiplication, Intermediate
## computations do not roll over at 2^256
forks: VmOpAllForks,
name: "mulmod",
info: "Modulo multiplication operation",
exec: (prep: VmOpIgnore,
run: mulmodOp,
post: VmOpIgnore)),
(opCode: Exp, ## 0x0a, Exponentiation
forks: VmOpAllForks,
name: "exp",
info: "Exponentiation operation",
exec: (prep: VmOpIgnore,
run: expOp,
post: VmOpIgnore)),
(opCode: SignExtend, ## 0x0b, Extend 2's complemet length
forks: VmOpAllForks,
name: "signExtend",
info: "Extend length of twos complement signed integer",
exec: (prep: VmOpIgnore,
run: signExtendOp,
post: VmOpIgnore)),
(opCode: Lt, ## 0x10, Less-than
forks: VmOpAllForks,
name: "lt",
info: "Less-than comparison",
exec: (prep: VmOpIgnore,
run: ltOp,
post: VmOpIgnore)),
(opCode: Gt, ## 0x11, Greater-than
forks: VmOpAllForks,
name: "gt",
info: "Greater-than comparison",
exec: (prep: VmOpIgnore,
run: gtOp,
post: VmOpIgnore)),
(opCode: Slt, ## 0x12, Signed less-than
forks: VmOpAllForks,
name: "slt",
info: "Signed less-than comparison",
exec: (prep: VmOpIgnore,
run: sltOp,
post: VmOpIgnore)),
(opCode: Sgt, ## 0x13, Signed greater-than
forks: VmOpAllForks,
name: "sgt",
info: "Signed greater-than comparison",
exec: (prep: VmOpIgnore,
run: sgtOp,
post: VmOpIgnore)),
(opCode: Eq, ## 0x14, Equality
forks: VmOpAllForks,
name: "eq",
info: "Equality comparison",
exec: (prep: VmOpIgnore,
run: eqOp,
post: VmOpIgnore)),
(opCode: IsZero, ## 0x15, Not operator
forks: VmOpAllForks,
name: "isZero",
info: "Simple not operator (Note: real Yellow Paper description)",
exec: (prep: VmOpIgnore,
run: isZeroOp,
post: VmOpIgnore)),
(opCode: And, ## 0x16, AND
forks: VmOpAllForks,
name: "andOp",
info: "Bitwise AND operation",
exec: (prep: VmOpIgnore,
run: andOp,
post: VmOpIgnore)),
(opCode: Or, ## 0x17, OR
forks: VmOpAllForks,
name: "orOp",
info: "Bitwise OR operation",
exec: (prep: VmOpIgnore,
run: orOp,
post: VmOpIgnore)),
(opCode: Xor, ## 0x18, XOR
forks: VmOpAllForks,
name: "xorOp",
info: "Bitwise XOR operation",
exec: (prep: VmOpIgnore,
run: xorOp,
post: VmOpIgnore)),
(opCode: Not, ## 0x19, NOT
forks: VmOpAllForks,
name: "notOp",
info: "Bitwise NOT operation",
exec: (prep: VmOpIgnore,
run: notOp,
post: VmOpIgnore)),
(opCode: Byte, ## 0x1a, Retrieve byte
forks: VmOpAllForks,
name: "byteOp",
info: "Retrieve single byte from word",
exec: (prep: VmOpIgnore,
run: byteOp,
post: VmOpIgnore)),
# Constantinople's new opcodes
(opCode: Shl, ## 0x1b, Shift left
forks: VmOpConstantinopleAndLater,
name: "shlOp",
info: "Shift left",
exec: (prep: VmOpIgnore,
run: shlOp,
post: VmOpIgnore)),
(opCode: Shr, ## 0x1c, Shift right logical
forks: VmOpConstantinopleAndLater,
name: "shrOp",
info: "Logical shift right",
exec: (prep: VmOpIgnore,
run: shrOp,
post: VmOpIgnore)),
(opCode: Sar, ## 0x1d, Shift right arithmetic
forks: VmOpConstantinopleAndLater,
name: "sarOp",
info: "Arithmetic shift right",
exec: (prep: VmOpIgnore,
run: sarOp,
post: VmOpIgnore))]
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------