468 lines
13 KiB
Nim
468 lines
13 KiB
Nim
|
# Nimbus
|
|||
|
# Copyright (c) 2018 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
|
|||
|
## ===================================================
|
|||
|
##
|
|||
|
|
|||
|
const
|
|||
|
kludge {.intdefine.}: int = 0
|
|||
|
breakCircularDependency {.used.} = kludge > 0
|
|||
|
|
|||
|
import
|
|||
|
./oph_defs,
|
|||
|
stint
|
|||
|
|
|||
|
# ------------------------------------------------------------------------------
|
|||
|
# Kludge BEGIN
|
|||
|
# ------------------------------------------------------------------------------
|
|||
|
|
|||
|
when not breakCircularDependency:
|
|||
|
import
|
|||
|
../../../constants,
|
|||
|
../../stack,
|
|||
|
../../v2computation,
|
|||
|
../../v2types,
|
|||
|
../gas_meter,
|
|||
|
../utils/v2utils_numeric,
|
|||
|
../v2gas_costs,
|
|||
|
chronicles,
|
|||
|
eth/common,
|
|||
|
options,
|
|||
|
sets
|
|||
|
|
|||
|
else:
|
|||
|
import macros
|
|||
|
|
|||
|
# copied from stack.nim
|
|||
|
macro genTupleType(len: static[int], elemType: untyped): untyped =
|
|||
|
result = nnkTupleConstr.newNimNode()
|
|||
|
for i in 0 ..< len: result.add(elemType)
|
|||
|
|
|||
|
# function stubs from stack.nim (to satisfy compiler logic)
|
|||
|
proc push[T](x: Stack; n: T) = discard
|
|||
|
proc popInt(x: var Stack): UInt256 = discard
|
|||
|
proc popInt(x: var Stack, n: static[int]): auto =
|
|||
|
var rc: genTupleType(n, UInt256)
|
|||
|
return rc
|
|||
|
|
|||
|
# function stubs from v2utils_numeric.nim
|
|||
|
proc extractSign(v: var UInt256, sign: var bool) = discard
|
|||
|
proc setSign(v: var UInt256, sign: bool) = discard
|
|||
|
func safeInt(x: Uint256): int = discard
|
|||
|
|
|||
|
# ------------------------------------------------------------------------------
|
|||
|
# Kludge END
|
|||
|
# ------------------------------------------------------------------------------
|
|||
|
|
|||
|
# ------------------------------------------------------------------------------
|
|||
|
# Private, op handlers implementation
|
|||
|
# ------------------------------------------------------------------------------
|
|||
|
|
|||
|
const
|
|||
|
addOp: Vm2OpFn = proc (k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
lhs + rhs
|
|||
|
|
|||
|
mulOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
lhs * rhs
|
|||
|
|
|||
|
subOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
lhs - rhs
|
|||
|
|
|||
|
divideOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
if rhs == 0:
|
|||
|
zero(Uint256)
|
|||
|
else:
|
|||
|
lhs div rhs
|
|||
|
|
|||
|
sdivOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
var r: UInt256
|
|||
|
if rhs != 0:
|
|||
|
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)
|
|||
|
|
|||
|
moduloOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
if rhs == 0:
|
|||
|
zero(Uint256)
|
|||
|
else:
|
|||
|
lhs mod rhs
|
|||
|
|
|||
|
smodOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
var r: UInt256
|
|||
|
if rhs != 0:
|
|||
|
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)
|
|||
|
|
|||
|
addmodOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs, modulus) = k.cpt.stack.popInt(3)
|
|||
|
k.cpt.stack.push:
|
|||
|
if modulus == 0:
|
|||
|
zero(UInt256)
|
|||
|
else:
|
|||
|
addmod(lhs, rhs, modulus)
|
|||
|
|
|||
|
mulmodOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs, modulus) = k.cpt.stack.popInt(3)
|
|||
|
k.cpt.stack.push:
|
|||
|
if modulus == 0:
|
|||
|
zero(UInt256)
|
|||
|
else:
|
|||
|
mulmod(lhs, rhs, modulus)
|
|||
|
|
|||
|
expOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (base, exponent) = k.cpt.stack.popInt(2)
|
|||
|
when not breakCircularDependency:
|
|||
|
k.cpt.gasMeter.consumeGas(
|
|||
|
k.cpt.gasCosts[Exp].d_handler(exponent),
|
|||
|
reason = "EXP: exponent bytes")
|
|||
|
k.cpt.stack.push:
|
|||
|
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)
|
|||
|
|
|||
|
signExtendOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
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
|
|||
|
|
|||
|
ltOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
(lhs < rhs).uint.u256
|
|||
|
|
|||
|
gtOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
(lhs > rhs).uint.u256
|
|||
|
|
|||
|
sltOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
(cast[Int256](lhs) < cast[Int256](rhs)).uint.u256
|
|||
|
|
|||
|
sgtOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
(cast[Int256](lhs) > cast[Int256](rhs)).uint.u256
|
|||
|
|
|||
|
eqOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
(lhs == rhs).uint.u256
|
|||
|
|
|||
|
isZeroOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (value) = k.cpt.stack.popInt(1)
|
|||
|
k.cpt.stack.push:
|
|||
|
value.isZero.uint.u256
|
|||
|
|
|||
|
andOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
lhs and rhs
|
|||
|
|
|||
|
orOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
lhs or rhs
|
|||
|
|
|||
|
xorOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (lhs, rhs) = k.cpt.stack.popInt(2)
|
|||
|
k.cpt.stack.push:
|
|||
|
lhs xor rhs
|
|||
|
|
|||
|
notOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (value) = k.cpt.stack.popInt(1)
|
|||
|
k.cpt.stack.push:
|
|||
|
value.not
|
|||
|
|
|||
|
byteOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let (position, value) = k.cpt.stack.popInt(2)
|
|||
|
let pos = position.truncate(int)
|
|||
|
k.cpt.stack.push:
|
|||
|
if pos >= 32 or pos < 0:
|
|||
|
zero(Uint256)
|
|||
|
else:
|
|||
|
when system.cpuEndian == bigEndian:
|
|||
|
cast[array[32, byte]](value)[pos].u256
|
|||
|
else:
|
|||
|
cast[array[32, byte]](value)[31 - pos].u256
|
|||
|
|
|||
|
# Constantinople's new opcodes
|
|||
|
|
|||
|
shlOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
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
|
|||
|
|
|||
|
shrOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
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
|
|||
|
|
|||
|
sarOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|||
|
let shiftLen = k.cpt.stack.popInt().safeInt
|
|||
|
let num = cast[Int256](k.cpt.stack.popInt())
|
|||
|
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
|
|||
|
vm2OpExecArithmetic*: seq[Vm2OpExec] = @[
|
|||
|
|
|||
|
(opCode: Add, ## 0x01, Addition
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Addition operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: addOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Mul, ## 0x02, Multiplication
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Multiplication operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: mulOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Sub, ## 0x03, Subtraction
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Subtraction operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: subOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Div, ## 0x04, Division
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Integer division operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: divideOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Sdiv, ## 0x05, Signed division
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Signed integer division operation (truncated)",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: sdivOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Mod, ## 0x06, Modulo
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Modulo remainder operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: moduloOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Smod, ## 0x07, Signed modulo
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Signed modulo remainder operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: smodOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: AddMod, ## 0x08, Modulo addition, Intermediate
|
|||
|
## computations do not roll over at 2^256
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Modulo addition operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: addmodOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: MulMod, ## 0x09, Modulo multiplication, Intermediate
|
|||
|
## computations do not roll over at 2^256
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Modulo multiplication operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: mulmodOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Exp, ## 0x0a, Exponentiation
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Exponentiation operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: expOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: SignExtend, ## 0x0b, Extend 2's complemet length
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Extend length of two’s complement signed integer",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: signExtendOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Lt, ## 0x10, Less-than
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Less-than comparison",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: ltOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Gt, ## 0x11, Greater-than
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Greater-than comparison",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: gtOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Slt, ## 0x12, Signed less-than
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Signed less-than comparison",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: sltOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Sgt, ## 0x13, Signed greater-than
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Signed greater-than comparison",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: sgtOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Eq, ## 0x14, Equality
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Equality comparison",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: eqOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: IsZero, ## 0x15, Not operator
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Simple not operator (Note: real Yellow Paper description)",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: isZeroOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: And, ## 0x16, AND
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Bitwise AND operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: andOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Or, ## 0x17, OR
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Bitwise OR operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: orOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Xor, ## 0x18, XOR
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Bitwise XOR operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: xorOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Not, ## 0x19, NOT
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Bitwise NOT operation",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: notOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Byte, ## 0x1a, Retrieve byte
|
|||
|
forks: Vm2OpAllForks,
|
|||
|
info: "Retrieve single byte from word",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: byteOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
# Constantinople's new opcodes
|
|||
|
|
|||
|
(opCode: Shl, ## 0x1b, Shift left
|
|||
|
forks: Vm2OpConstantinopleAndLater,
|
|||
|
info: "Shift left",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: shlOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Shr, ## 0x1c, Shift right logical
|
|||
|
forks: Vm2OpConstantinopleAndLater,
|
|||
|
info: "Logical shift right",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: shrOp,
|
|||
|
post: vm2OpIgnore)),
|
|||
|
|
|||
|
(opCode: Sar, ## 0x1d, Shift right arithmetic
|
|||
|
forks: Vm2OpConstantinopleAndLater,
|
|||
|
info: "Arithmetic shift right",
|
|||
|
exec: (prep: vm2OpIgnore,
|
|||
|
run: sarOp,
|
|||
|
post: vm2OpIgnore))]
|
|||
|
|
|||
|
# ------------------------------------------------------------------------------
|
|||
|
# End
|
|||
|
# ------------------------------------------------------------------------------
|