Optimize EVM stack usage (#2502)
* EVM: Optimize CALL family stack usage * EVM: Optimize CREATE family stack usage * EVM: Optimize arith stack usage * EVM: Optimize stack usage in the rest of opcodes * Fix test_op_env and clean up unused imports * EVM: Optimize arithmetic binary ops
This commit is contained in:
parent
f9956eba59
commit
ee323d5ff8
|
@ -39,245 +39,243 @@ func slt(x, y: UInt256): bool =
|
||||||
|
|
||||||
proc addOp (k: var VmCtx): EvmResultVoid =
|
proc addOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x01, Addition
|
## 0x01, Addition
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
k.cpt.stack.binaryOp(`+`)
|
||||||
k.cpt.stack.push(lhs + rhs)
|
|
||||||
|
|
||||||
proc mulOp(k: var VmCtx): EvmResultVoid =
|
proc mulOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x02, Multiplication
|
## 0x02, Multiplication
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
k.cpt.stack.binaryOp(`*`)
|
||||||
k.cpt.stack.push(lhs * rhs)
|
|
||||||
|
|
||||||
proc subOp(k: var VmCtx): EvmResultVoid =
|
proc subOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x03, Substraction
|
## 0x03, Substraction
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
k.cpt.stack.binaryOp(`-`)
|
||||||
k.cpt.stack.push(lhs - rhs)
|
|
||||||
|
|
||||||
proc divideOp(k: var VmCtx): EvmResultVoid =
|
proc divideOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x04, Division
|
## 0x04, Division
|
||||||
let
|
template div256(top, lhs, rhs) =
|
||||||
(lhs, rhs) = ? k.cpt.stack.popInt(2)
|
if rhs.isZero:
|
||||||
value = if rhs.isZero:
|
# EVM special casing of div by 0
|
||||||
# EVM special casing of div by 0
|
top = zero(UInt256)
|
||||||
zero(UInt256)
|
else:
|
||||||
else:
|
top = lhs div rhs
|
||||||
lhs div rhs
|
|
||||||
|
|
||||||
k.cpt.stack.push value
|
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(div256)
|
||||||
|
|
||||||
proc sdivOp(k: var VmCtx): EvmResultVoid =
|
proc sdivOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x05, Signed division
|
## 0x05, Signed division
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
template sdiv256(top, lhs, rhs) =
|
||||||
|
if rhs.isZero.not:
|
||||||
var r: UInt256
|
var signA, signB: bool
|
||||||
if rhs.isZero.not:
|
extractSign(lhs, signA)
|
||||||
var a = lhs
|
extractSign(rhs, signB)
|
||||||
var b = rhs
|
top = lhs div rhs
|
||||||
var signA, signB: bool
|
setSign(top, signA xor signB)
|
||||||
extractSign(a, signA)
|
|
||||||
extractSign(b, signB)
|
|
||||||
r = a div b
|
|
||||||
setSign(r, signA xor signB)
|
|
||||||
k.cpt.stack.push(r)
|
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(sdiv256)
|
||||||
|
|
||||||
proc moduloOp(k: var VmCtx): EvmResultVoid =
|
proc moduloOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x06, Modulo
|
## 0x06, Modulo
|
||||||
let
|
template mod256(top, lhs, rhs) =
|
||||||
(lhs, rhs) = ? k.cpt.stack.popInt(2)
|
if rhs.isZero:
|
||||||
value = if rhs.isZero:
|
top = zero(UInt256)
|
||||||
zero(UInt256)
|
else:
|
||||||
else:
|
top = lhs mod rhs
|
||||||
lhs mod rhs
|
|
||||||
|
|
||||||
k.cpt.stack.push value
|
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(mod256)
|
||||||
|
|
||||||
proc smodOp(k: var VmCtx): EvmResultVoid =
|
proc smodOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x07, Signed modulo
|
## 0x07, Signed modulo
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
template smod256(top, lhs, rhs) =
|
||||||
|
if rhs.isZero.not:
|
||||||
var r: UInt256
|
var sign: bool
|
||||||
if rhs.isZero.not:
|
extractSign(rhs, sign)
|
||||||
var sign: bool
|
extractSign(lhs, sign)
|
||||||
var v = lhs
|
top = lhs mod rhs
|
||||||
var m = rhs
|
setSign(top, sign)
|
||||||
extractSign(m, sign)
|
|
||||||
extractSign(v, sign)
|
|
||||||
r = v mod m
|
|
||||||
setSign(r, sign)
|
|
||||||
k.cpt.stack.push(r)
|
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(smod256)
|
||||||
|
|
||||||
proc addmodOp(k: var VmCtx): EvmResultVoid =
|
proc addmodOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x08, Modulo addition
|
## 0x08, Modulo addition
|
||||||
## Intermediate computations do not roll over at 2^256
|
## Intermediate computations do not roll over at 2^256
|
||||||
|
? k.cpt.stack.lsCheck(3)
|
||||||
let
|
let
|
||||||
(lhs, rhs, modulus) = ? k.cpt.stack.popInt(3)
|
lhs = k.cpt.stack.lsPeekInt(^1)
|
||||||
|
rhs = k.cpt.stack.lsPeekInt(^2)
|
||||||
|
modulus = k.cpt.stack.lsPeekInt(^3)
|
||||||
value = if modulus.isZero:
|
value = if modulus.isZero:
|
||||||
zero(UInt256)
|
zero(UInt256)
|
||||||
else:
|
else:
|
||||||
addmod(lhs, rhs, modulus)
|
addmod(lhs, rhs, modulus)
|
||||||
|
|
||||||
k.cpt.stack.push value
|
k.cpt.stack.lsShrink(2)
|
||||||
|
k.cpt.stack.lsTop value
|
||||||
|
ok()
|
||||||
|
|
||||||
proc mulmodOp(k: var VmCtx): EvmResultVoid =
|
proc mulmodOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x09, Modulo multiplication
|
## 0x09, Modulo multiplication
|
||||||
## Intermediate computations do not roll over at 2^256
|
## Intermediate computations do not roll over at 2^256
|
||||||
|
? k.cpt.stack.lsCheck(3)
|
||||||
let
|
let
|
||||||
(lhs, rhs, modulus) = ? k.cpt.stack.popInt(3)
|
lhs = k.cpt.stack.lsPeekInt(^1)
|
||||||
|
rhs = k.cpt.stack.lsPeekInt(^2)
|
||||||
|
modulus = k.cpt.stack.lsPeekInt(^3)
|
||||||
value = if modulus.isZero:
|
value = if modulus.isZero:
|
||||||
zero(UInt256)
|
zero(UInt256)
|
||||||
else:
|
else:
|
||||||
mulmod(lhs, rhs, modulus)
|
mulmod(lhs, rhs, modulus)
|
||||||
|
|
||||||
k.cpt.stack.push value
|
k.cpt.stack.lsShrink(2)
|
||||||
|
k.cpt.stack.lsTop value
|
||||||
|
ok()
|
||||||
|
|
||||||
proc expOp(k: var VmCtx): EvmResultVoid =
|
proc expOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x0A, Exponentiation
|
## 0x0A, Exponentiation
|
||||||
let (base, exponent) = ? k.cpt.stack.popInt(2)
|
template exp256(top, base, exponent) =
|
||||||
|
? k.cpt.opcodeGasCost(Exp,
|
||||||
|
k.cpt.gasCosts[Exp].d_handler(exponent),
|
||||||
|
reason = "EXP: exponent bytes")
|
||||||
|
|
||||||
? k.cpt.opcodeGasCost(Exp,
|
if not base.isZero:
|
||||||
k.cpt.gasCosts[Exp].d_handler(exponent),
|
top = base.pow(exponent)
|
||||||
reason = "EXP: exponent bytes")
|
elif exponent.isZero:
|
||||||
|
# https://github.com/ethereum/yellowpaper/issues/257
|
||||||
let value = if not base.isZero:
|
# https://github.com/ethereum/tests/pull/460
|
||||||
base.pow(exponent)
|
# https://github.com/ewasm/evm2wasm/issues/137
|
||||||
elif exponent.isZero:
|
top = 1.u256
|
||||||
# https://github.com/ethereum/yellowpaper/issues/257
|
else:
|
||||||
# https://github.com/ethereum/tests/pull/460
|
top = zero(UInt256)
|
||||||
# https://github.com/ewasm/evm2wasm/issues/137
|
|
||||||
1.u256
|
|
||||||
else:
|
|
||||||
zero(UInt256)
|
|
||||||
|
|
||||||
k.cpt.stack.push value
|
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(exp256)
|
||||||
|
|
||||||
proc signExtendOp(k: var VmCtx): EvmResultVoid =
|
proc signExtendOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x0B, Sign extend
|
## 0x0B, Sign extend
|
||||||
## Extend length of two’s complement signed integer.
|
## Extend length of two’s complement signed integer.
|
||||||
let (bits, value) = ? k.cpt.stack.popInt(2)
|
template se256(top, bits, value) =
|
||||||
|
const one = 1.u256
|
||||||
var res: UInt256
|
if bits <= 31.u256:
|
||||||
if bits <= 31.u256:
|
let
|
||||||
let
|
testBit = bits.truncate(int) * 8 + 7
|
||||||
one = 1.u256
|
bitPos = one shl testBit
|
||||||
testBit = bits.truncate(int) * 8 + 7
|
mask = bitPos - one
|
||||||
bitPos = one shl testBit
|
if not isZero(value and bitPos):
|
||||||
mask = bitPos - one
|
top = value or (not mask)
|
||||||
if not isZero(value and bitPos):
|
else:
|
||||||
res = value or (not mask)
|
top = value and mask
|
||||||
else:
|
else:
|
||||||
res = value and mask
|
top = value
|
||||||
else:
|
|
||||||
res = value
|
|
||||||
k.cpt.stack.push res
|
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(se256)
|
||||||
|
|
||||||
proc ltOp(k: var VmCtx): EvmResultVoid =
|
proc ltOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x10, Less-than comparison
|
## 0x10, Less-than comparison
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
template lt256(lhs, rhs): auto =
|
||||||
k.cpt.stack.push((lhs < rhs).uint.u256)
|
(lhs < rhs).uint.u256
|
||||||
|
k.cpt.stack.binaryOp(lt256)
|
||||||
|
|
||||||
proc gtOp(k: var VmCtx): EvmResultVoid =
|
proc gtOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x11, Greater-than comparison
|
## 0x11, Greater-than comparison
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
template gt256(lhs, rhs): auto =
|
||||||
k.cpt.stack.push((lhs > rhs).uint.u256)
|
(lhs > rhs).uint.u256
|
||||||
|
k.cpt.stack.binaryOp(gt256)
|
||||||
|
|
||||||
proc sltOp(k: var VmCtx): EvmResultVoid =
|
proc sltOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x12, Signed less-than comparison
|
## 0x12, Signed less-than comparison
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
template slt256(lhs, rhs): auto =
|
||||||
k.cpt.stack.push(slt(lhs, rhs).uint.u256)
|
slt(lhs, rhs).uint.u256
|
||||||
|
k.cpt.stack.binaryOp(slt256)
|
||||||
|
|
||||||
proc sgtOp(k: var VmCtx): EvmResultVoid =
|
proc sgtOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x13, Signed greater-than comparison
|
## 0x13, Signed greater-than comparison
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
|
||||||
# Arguments are swapped and SLT is used.
|
# Arguments are swapped and SLT is used.
|
||||||
k.cpt.stack.push(slt(rhs, lhs).uint.u256)
|
template sgt256(lhs, rhs): auto =
|
||||||
|
slt(rhs, lhs).uint.u256
|
||||||
|
k.cpt.stack.binaryOp(sgt256)
|
||||||
|
|
||||||
proc eqOp(k: var VmCtx): EvmResultVoid =
|
proc eqOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x14, Equality comparison
|
## 0x14, Equality comparison
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
template eq256(lhs, rhs): auto =
|
||||||
k.cpt.stack.push((lhs == rhs).uint.u256)
|
(lhs == rhs).uint.u256
|
||||||
|
k.cpt.stack.binaryOp(eq256)
|
||||||
|
|
||||||
proc isZeroOp(k: var VmCtx): EvmResultVoid =
|
proc isZeroOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x15, Check if zero
|
## 0x15, Check if zero
|
||||||
let value = ? k.cpt.stack.popInt()
|
template zero256(value): auto =
|
||||||
k.cpt.stack.push(value.isZero.uint.u256)
|
value.isZero.uint.u256
|
||||||
|
k.cpt.stack.unaryOp(zero256)
|
||||||
|
|
||||||
proc andOp(k: var VmCtx): EvmResultVoid =
|
proc andOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x16, Bitwise AND
|
## 0x16, Bitwise AND
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
k.cpt.stack.binaryOp(`and`)
|
||||||
k.cpt.stack.push(lhs and rhs)
|
|
||||||
|
|
||||||
proc orOp(k: var VmCtx): EvmResultVoid =
|
proc orOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x17, Bitwise OR
|
## 0x17, Bitwise OR
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
k.cpt.stack.binaryOp(`or`)
|
||||||
k.cpt.stack.push(lhs or rhs)
|
|
||||||
|
|
||||||
proc xorOp(k: var VmCtx): EvmResultVoid =
|
proc xorOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x18, Bitwise XOR
|
## 0x18, Bitwise XOR
|
||||||
let (lhs, rhs) = ? k.cpt.stack.popInt(2)
|
k.cpt.stack.binaryOp(`xor`)
|
||||||
k.cpt.stack.push(lhs xor rhs)
|
|
||||||
|
|
||||||
proc notOp(k: var VmCtx): EvmResultVoid =
|
proc notOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x19, Check if zero
|
## 0x19, Check if zero
|
||||||
let value = ? k.cpt.stack.popInt()
|
k.cpt.stack.unaryOp(`not`)
|
||||||
k.cpt.stack.push(value.not)
|
|
||||||
|
|
||||||
proc byteOp(k: var VmCtx): EvmResultVoid =
|
proc byteOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x20, Retrieve single byte from word.
|
## 0x20, Retrieve single byte from word.
|
||||||
let
|
template byte256(top, position, value) =
|
||||||
(position, value) = ? k.cpt.stack.popInt(2)
|
if position >= 32.u256:
|
||||||
val = if position >= 32.u256:
|
top = zero(UInt256)
|
||||||
zero(UInt256)
|
else:
|
||||||
else:
|
let pos = position.truncate(int)
|
||||||
let pos = position.truncate(int)
|
when system.cpuEndian == bigEndian:
|
||||||
when system.cpuEndian == bigEndian:
|
top = cast[array[32, byte]](value)[pos].u256
|
||||||
cast[array[32, byte]](value)[pos].u256
|
else:
|
||||||
else:
|
top = cast[array[32, byte]](value)[31 - pos].u256
|
||||||
cast[array[32, byte]](value)[31 - pos].u256
|
|
||||||
|
|
||||||
k.cpt.stack.push val
|
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(byte256)
|
||||||
|
|
||||||
# Constantinople's new opcodes
|
# Constantinople's new opcodes
|
||||||
|
|
||||||
proc shlOp(k: var VmCtx): EvmResultVoid =
|
proc shlOp(k: var VmCtx): EvmResultVoid =
|
||||||
let (shift, num) = ? k.cpt.stack.popInt(2)
|
## 0x1b, Shift left
|
||||||
let shiftLen = shift.safeInt
|
template shl256(top, lhs, num) =
|
||||||
if shiftLen >= 256:
|
let shiftLen = lhs.safeInt
|
||||||
k.cpt.stack.push 0
|
if shiftLen >= 256:
|
||||||
else:
|
top = 0.u256
|
||||||
k.cpt.stack.push(num shl shiftLen)
|
else:
|
||||||
|
top = num shl shiftLen
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(shl256)
|
||||||
|
|
||||||
proc shrOp(k: var VmCtx): EvmResultVoid =
|
proc shrOp(k: var VmCtx): EvmResultVoid =
|
||||||
let (shift, num) = ? k.cpt.stack.popInt(2)
|
## 0x1c, Shift right logical
|
||||||
let shiftLen = shift.safeInt
|
template shr256(top, lhs, num) =
|
||||||
if shiftLen >= 256:
|
let shiftLen = lhs.safeInt
|
||||||
k.cpt.stack.push 0
|
if shiftLen >= 256:
|
||||||
else:
|
top = 0.u256
|
||||||
# uint version of `shr`
|
else:
|
||||||
k.cpt.stack.push(num shr shiftLen)
|
# uint version of `shr`
|
||||||
|
top = num shr shiftLen
|
||||||
|
|
||||||
|
k.cpt.stack.binaryWithTop(shr256)
|
||||||
|
|
||||||
proc sarOp(k: var VmCtx): EvmResultVoid =
|
proc sarOp(k: var VmCtx): EvmResultVoid =
|
||||||
let
|
## 0x1d, Shift right arithmetic
|
||||||
shiftLen = ? k.cpt.stack.popSafeInt()
|
template sar256(top, lhs, num256) =
|
||||||
num256 = ? k.cpt.stack.popInt()
|
let
|
||||||
num = cast[Int256](num256)
|
shiftLen = lhs.safeInt
|
||||||
|
num = cast[Int256](num256)
|
||||||
|
|
||||||
if shiftLen >= 256:
|
if shiftLen >= 256:
|
||||||
if num.isNegative:
|
if num.isNegative:
|
||||||
k.cpt.stack.push(cast[UInt256]((-1).i256))
|
top = cast[UInt256]((-1).i256)
|
||||||
|
else:
|
||||||
|
top = 0.u256
|
||||||
else:
|
else:
|
||||||
k.cpt.stack. push 0
|
# int version of `shr` then force the result
|
||||||
else:
|
# into uint256
|
||||||
# int version of `shr` then force the result
|
top = cast[UInt256](num shr shiftLen)
|
||||||
# into uint256
|
|
||||||
k.cpt.stack.push(cast[UInt256](num shr shiftLen))
|
k.cpt.stack.binaryWithTop(sar256)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public, op exec table entries
|
# Public, op exec table entries
|
||||||
|
|
|
@ -19,6 +19,7 @@ import
|
||||||
../../computation,
|
../../computation,
|
||||||
../../stack,
|
../../stack,
|
||||||
../../evm_errors,
|
../../evm_errors,
|
||||||
|
../utils/utils_numeric,
|
||||||
../op_codes,
|
../op_codes,
|
||||||
./oph_defs
|
./oph_defs
|
||||||
|
|
||||||
|
@ -31,14 +32,13 @@ when not defined(evmc_enabled):
|
||||||
|
|
||||||
proc blockhashOp (k: var VmCtx): EvmResultVoid =
|
proc blockhashOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x40, Get the hash of one of the 256 most recent complete blocks.
|
## 0x40, Get the hash of one of the 256 most recent complete blocks.
|
||||||
let
|
template block256(top, number, conv) =
|
||||||
cpt = k.cpt
|
if number > high(BlockNumber).u256:
|
||||||
blockNumber = ? cpt.stack.popInt()
|
top = zero(UInt256)
|
||||||
|
else:
|
||||||
|
conv(k.cpt.getBlockHash(number.truncate(BlockNumber)), top)
|
||||||
|
|
||||||
if blockNumber > high(BlockNumber).u256:
|
k.cpt.stack.unaryWithTop(block256)
|
||||||
cpt.stack.push Hash256()
|
|
||||||
else:
|
|
||||||
cpt.stack.push cpt.getBlockHash(blockNumber.truncate(BlockNumber))
|
|
||||||
|
|
||||||
proc coinBaseOp (k: var VmCtx): EvmResultVoid =
|
proc coinBaseOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x41, Get the block's beneficiary address.
|
## 0x41, Get the block's beneficiary address.
|
||||||
|
@ -75,14 +75,17 @@ proc baseFeeOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc blobHashOp (k: var VmCtx): EvmResultVoid =
|
proc blobHashOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x49, Get current transaction's EIP-4844 versioned hash.
|
## 0x49, Get current transaction's EIP-4844 versioned hash.
|
||||||
let
|
template blob256(top, number, conv) =
|
||||||
index = ? k.cpt.stack.popSafeInt()
|
let
|
||||||
len = k.cpt.getVersionedHashesLen
|
index = number.safeInt
|
||||||
|
len = k.cpt.getVersionedHashesLen
|
||||||
|
|
||||||
if index < len:
|
if index < len:
|
||||||
k.cpt.stack.push k.cpt.getVersionedHash(index)
|
conv(k.cpt.getVersionedHash(index), top)
|
||||||
else:
|
else:
|
||||||
k.cpt.stack.push 0
|
top = zero(UInt256)
|
||||||
|
|
||||||
|
k.cpt.stack.unaryWithTop(blob256)
|
||||||
|
|
||||||
proc blobBaseFeeOp (k: var VmCtx): EvmResultVoid =
|
proc blobBaseFeeOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x4a, Get the block's base fee.
|
## 0x4a, Get the block's base fee.
|
||||||
|
|
|
@ -62,8 +62,8 @@ type
|
||||||
gasCallEIP2929: GasInt
|
gasCallEIP2929: GasInt
|
||||||
|
|
||||||
|
|
||||||
proc updateStackAndParams(q: var LocalParams; c: Computation): EvmResultVoid =
|
proc updateStackAndParams(q: var LocalParams; c: Computation) =
|
||||||
? c.stack.push(0)
|
c.stack.lsTop(0)
|
||||||
|
|
||||||
let
|
let
|
||||||
outLen = calcMemSize(q.memOutPos, q.memOutLen)
|
outLen = calcMemSize(q.memOutPos, q.memOutLen)
|
||||||
|
@ -92,24 +92,27 @@ proc updateStackAndParams(q: var LocalParams; c: Computation): EvmResultVoid =
|
||||||
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
||||||
# the form of a constant `gasCall`
|
# the form of a constant `gasCall`
|
||||||
q.gasCallEIP2929 = ColdAccountAccessCost - WarmStorageReadCost
|
q.gasCallEIP2929 = ColdAccountAccessCost - WarmStorageReadCost
|
||||||
ok()
|
|
||||||
|
|
||||||
proc callParams(c: Computation): EvmResult[LocalParams] =
|
proc callParams(c: Computation): EvmResult[LocalParams] =
|
||||||
## Helper for callOp()
|
## Helper for callOp()
|
||||||
|
|
||||||
|
? c.stack.lsCheck(7)
|
||||||
|
|
||||||
var res = LocalParams(
|
var res = LocalParams(
|
||||||
gas : ? c.stack.popInt(),
|
gas : c.stack.lsPeekInt(^1),
|
||||||
codeAddress : ? c.stack.popAddress(),
|
codeAddress : c.stack.lsPeekAddress(^2),
|
||||||
value : ? c.stack.popInt(),
|
value : c.stack.lsPeekInt(^3),
|
||||||
memInPos : ? c.stack.popMemRef(),
|
memInPos : c.stack.lsPeekMemRef(^4),
|
||||||
memInLen : ? c.stack.popMemRef(),
|
memInLen : c.stack.lsPeekMemRef(^5),
|
||||||
memOutPos : ? c.stack.popMemRef(),
|
memOutPos : c.stack.lsPeekMemRef(^6),
|
||||||
memOutLen : ? c.stack.popMemRef(),
|
memOutLen : c.stack.lsPeekMemRef(^7),
|
||||||
sender : c.msg.contractAddress,
|
sender : c.msg.contractAddress,
|
||||||
flags : c.msg.flags,
|
flags : c.msg.flags,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
c.stack.lsShrink(6)
|
||||||
res.contractAddress = res.codeAddress
|
res.contractAddress = res.codeAddress
|
||||||
? res.updateStackAndParams(c)
|
res.updateStackAndParams(c)
|
||||||
ok(res)
|
ok(res)
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,38 +125,45 @@ proc callCodeParams(c: Computation): EvmResult[LocalParams] =
|
||||||
|
|
||||||
proc delegateCallParams(c: Computation): EvmResult[LocalParams] =
|
proc delegateCallParams(c: Computation): EvmResult[LocalParams] =
|
||||||
## Helper for delegateCall()
|
## Helper for delegateCall()
|
||||||
|
|
||||||
|
? c.stack.lsCheck(6)
|
||||||
var res = LocalParams(
|
var res = LocalParams(
|
||||||
gas : ? c.stack.popInt(),
|
gas : c.stack.lsPeekInt(^1),
|
||||||
codeAddress : ? c.stack.popAddress(),
|
codeAddress : c.stack.lsPeekAddress(^2),
|
||||||
memInPos : ? c.stack.popMemRef(),
|
memInPos : c.stack.lsPeekMemRef(^3),
|
||||||
memInLen : ? c.stack.popMemRef(),
|
memInLen : c.stack.lsPeekMemRef(^4),
|
||||||
memOutPos : ? c.stack.popMemRef(),
|
memOutPos : c.stack.lsPeekMemRef(^5),
|
||||||
memOutLen : ? c.stack.popMemRef(),
|
memOutLen : c.stack.lsPeekMemRef(^6),
|
||||||
value : c.msg.value,
|
value : c.msg.value,
|
||||||
sender : c.msg.sender,
|
sender : c.msg.sender,
|
||||||
flags : c.msg.flags,
|
flags : c.msg.flags,
|
||||||
contractAddress: c.msg.contractAddress,
|
contractAddress: c.msg.contractAddress,
|
||||||
)
|
)
|
||||||
? res.updateStackAndParams(c)
|
|
||||||
|
c.stack.lsShrink(5)
|
||||||
|
res.updateStackAndParams(c)
|
||||||
ok(res)
|
ok(res)
|
||||||
|
|
||||||
|
|
||||||
proc staticCallParams(c: Computation): EvmResult[LocalParams] =
|
proc staticCallParams(c: Computation): EvmResult[LocalParams] =
|
||||||
## Helper for staticCall()
|
## Helper for staticCall()
|
||||||
|
|
||||||
|
? c.stack.lsCheck(6)
|
||||||
var res = LocalParams(
|
var res = LocalParams(
|
||||||
gas : ? c.stack.popInt(),
|
gas : c.stack.lsPeekInt(^1),
|
||||||
codeAddress : ? c.stack.popAddress(),
|
codeAddress : c.stack.lsPeekAddress(^2),
|
||||||
memInPos : ? c.stack.popMemRef(),
|
memInPos : c.stack.lsPeekMemRef(^3),
|
||||||
memInLen : ? c.stack.popMemRef(),
|
memInLen : c.stack.lsPeekMemRef(^4),
|
||||||
memOutPos : ? c.stack.popMemRef(),
|
memOutPos : c.stack.lsPeekMemRef(^5),
|
||||||
memOutLen : ? c.stack.popMemRef(),
|
memOutLen : c.stack.lsPeekMemRef(^6),
|
||||||
value : 0.u256,
|
value : 0.u256,
|
||||||
sender : c.msg.contractAddress,
|
sender : c.msg.contractAddress,
|
||||||
flags : {EVMC_STATIC},
|
flags : {EVMC_STATIC},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
c.stack.lsShrink(5)
|
||||||
res.contractAddress = res.codeAddress
|
res.contractAddress = res.codeAddress
|
||||||
? res.updateStackAndParams(c)
|
res.updateStackAndParams(c)
|
||||||
ok(res)
|
ok(res)
|
||||||
|
|
||||||
when evmc_enabled:
|
when evmc_enabled:
|
||||||
|
@ -170,7 +180,7 @@ when evmc_enabled:
|
||||||
c.gasMeter.refundGas(c.res.gas_refund)
|
c.gasMeter.refundGas(c.res.gas_refund)
|
||||||
|
|
||||||
if c.res.status_code == EVMC_SUCCESS:
|
if c.res.status_code == EVMC_SUCCESS:
|
||||||
? c.stack.top(1)
|
c.stack.lsTop(1)
|
||||||
|
|
||||||
if not c.res.release.isNil:
|
if not c.res.release.isNil:
|
||||||
c.res.release(c.res)
|
c.res.release(c.res)
|
||||||
|
@ -191,7 +201,7 @@ else:
|
||||||
|
|
||||||
if child.isSuccess:
|
if child.isSuccess:
|
||||||
c.gasMeter.refundGas(child.gasMeter.gasRefunded)
|
c.gasMeter.refundGas(child.gasMeter.gasRefunded)
|
||||||
? c.stack.top(1)
|
c.stack.lsTop(1)
|
||||||
|
|
||||||
c.returnData = child.output
|
c.returnData = child.output
|
||||||
let actualOutputSize = min(memLen, child.output.len)
|
let actualOutputSize = min(memLen, child.output.len)
|
||||||
|
|
|
@ -52,7 +52,7 @@ when evmc_enabled:
|
||||||
c.gasMeter.returnGas(GasInt c.res.gas_left)
|
c.gasMeter.returnGas(GasInt c.res.gas_left)
|
||||||
c.gasMeter.refundGas(c.res.gas_refund)
|
c.gasMeter.refundGas(c.res.gas_refund)
|
||||||
if c.res.status_code == EVMC_SUCCESS:
|
if c.res.status_code == EVMC_SUCCESS:
|
||||||
? c.stack.top(c.res.create_address)
|
c.stack.lsTop(c.res.create_address)
|
||||||
elif c.res.status_code == EVMC_REVERT:
|
elif c.res.status_code == EVMC_REVERT:
|
||||||
# From create, only use `outputData` if child returned with `REVERT`.
|
# From create, only use `outputData` if child returned with `REVERT`.
|
||||||
assign(c.returnData, makeOpenArray(c.res.output_data, c.res.output_size.int))
|
assign(c.returnData, makeOpenArray(c.res.output_data, c.res.output_size.int))
|
||||||
|
@ -75,7 +75,7 @@ else:
|
||||||
|
|
||||||
if child.isSuccess:
|
if child.isSuccess:
|
||||||
c.gasMeter.refundGas(child.gasMeter.gasRefunded)
|
c.gasMeter.refundGas(child.gasMeter.gasRefunded)
|
||||||
? c.stack.top child.msg.contractAddress
|
c.stack.lsTop child.msg.contractAddress
|
||||||
elif not child.error.burnsGas: # Means return was `REVERT`.
|
elif not child.error.burnsGas: # Means return was `REVERT`.
|
||||||
# From create, only use `outputData` if child returned with `REVERT`.
|
# From create, only use `outputData` if child returned with `REVERT`.
|
||||||
c.returnData = child.output
|
c.returnData = child.output
|
||||||
|
@ -90,14 +90,16 @@ else:
|
||||||
proc createOp(k: var VmCtx): EvmResultVoid =
|
proc createOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0xf0, Create a new account with associated code
|
## 0xf0, Create a new account with associated code
|
||||||
? checkInStaticContext(k.cpt)
|
? checkInStaticContext(k.cpt)
|
||||||
|
? k.cpt.stack.lsCheck(3)
|
||||||
|
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
endowment = ? cpt.stack.popInt()
|
endowment = cpt.stack.lsPeekInt(^1)
|
||||||
memPos = ? cpt.stack.popSafeInt()
|
memPos = cpt.stack.lsPeekSafeInt(^2)
|
||||||
memLen = ? cpt.stack.peekSafeInt()
|
memLen = cpt.stack.lsPeekSafeInt(^3)
|
||||||
|
|
||||||
? cpt.stack.top(0)
|
cpt.stack.lsShrink(2)
|
||||||
|
cpt.stack.lsTop(0)
|
||||||
|
|
||||||
# EIP-3860
|
# EIP-3860
|
||||||
if cpt.fork >= FkShanghai and memLen > EIP3860_MAX_INITCODE_SIZE:
|
if cpt.fork >= FkShanghai and memLen > EIP3860_MAX_INITCODE_SIZE:
|
||||||
|
@ -168,16 +170,18 @@ proc createOp(k: var VmCtx): EvmResultVoid =
|
||||||
proc create2Op(k: var VmCtx): EvmResultVoid =
|
proc create2Op(k: var VmCtx): EvmResultVoid =
|
||||||
## 0xf5, Behaves identically to CREATE, except using keccak256
|
## 0xf5, Behaves identically to CREATE, except using keccak256
|
||||||
? checkInStaticContext(k.cpt)
|
? checkInStaticContext(k.cpt)
|
||||||
|
? k.cpt.stack.lsCheck(4)
|
||||||
|
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
endowment = ? cpt.stack.popInt()
|
endowment = cpt.stack.lsPeekInt(^1)
|
||||||
memPos = ? cpt.stack.popSafeInt()
|
memPos = cpt.stack.lsPeekSafeInt(^2)
|
||||||
memLen = ? cpt.stack.popSafeInt()
|
memLen = cpt.stack.lsPeekSafeInt(^3)
|
||||||
salt256 = ? cpt.stack.peekInt()
|
salt256 = cpt.stack.lsPeekInt(^4)
|
||||||
salt = ContractSalt(bytes: salt256.toBytesBE)
|
salt = ContractSalt(bytes: salt256.toBytesBE)
|
||||||
|
|
||||||
? cpt.stack.top(0)
|
cpt.stack.lsShrink(3)
|
||||||
|
cpt.stack.lsTop(0)
|
||||||
|
|
||||||
# EIP-3860
|
# EIP-3860
|
||||||
if cpt.fork >= FkShanghai and memLen > EIP3860_MAX_INITCODE_SIZE:
|
if cpt.fork >= FkShanghai and memLen > EIP3860_MAX_INITCODE_SIZE:
|
||||||
|
|
|
@ -41,7 +41,7 @@ proc fnInfo(n: int): string {.compileTime.} =
|
||||||
"Duplicate " & blurb & " item in the stack"
|
"Duplicate " & blurb & " item in the stack"
|
||||||
|
|
||||||
|
|
||||||
proc dupImpl(k: var VmCtx; n: int): EvmResultVoid =
|
template dupImpl(k: var VmCtx; n: int): EvmResultVoid =
|
||||||
k.cpt.stack.dup(n)
|
k.cpt.stack.dup(n)
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
@ -22,10 +22,10 @@ import
|
||||||
../../stack,
|
../../stack,
|
||||||
../gas_costs,
|
../gas_costs,
|
||||||
../op_codes,
|
../op_codes,
|
||||||
../utils/utils_numeric,
|
|
||||||
./oph_defs,
|
./oph_defs,
|
||||||
./oph_helpers,
|
./oph_helpers,
|
||||||
eth/common,
|
eth/common,
|
||||||
|
stew/assign2,
|
||||||
stint
|
stint
|
||||||
|
|
||||||
when not defined(evmc_enabled):
|
when not defined(evmc_enabled):
|
||||||
|
@ -43,20 +43,17 @@ proc addressOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc balanceOp (k: var VmCtx): EvmResultVoid =
|
proc balanceOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x31, Get balance of the given account.
|
## 0x31, Get balance of the given account.
|
||||||
let
|
template balance256(address): auto =
|
||||||
cpt = k.cpt
|
k.cpt.getBalance(address)
|
||||||
address = ? cpt.stack.popAddress
|
k.cpt.stack.unaryAddress(balance256)
|
||||||
cpt.stack.push cpt.getBalance(address)
|
|
||||||
|
|
||||||
proc balanceEIP2929Op (k: var VmCtx): EvmResultVoid =
|
proc balanceEIP2929Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x31, EIP292: Get balance of the given account for Berlin and later
|
## 0x31, EIP292: Get balance of the given account for Berlin and later
|
||||||
let
|
template balanceEIP2929(address): auto =
|
||||||
cpt = k.cpt
|
let gasCost = k.cpt.gasEip2929AccountCheck(address)
|
||||||
address = ? cpt.stack.popAddress()
|
? k.cpt.opcodeGasCost(Balance, gasCost, reason = "Balance EIP2929")
|
||||||
gasCost = cpt.gasEip2929AccountCheck(address)
|
k.cpt.getBalance(address)
|
||||||
|
k.cpt.stack.unaryAddress(balanceEIP2929)
|
||||||
? cpt.opcodeGasCost(Balance, gasCost, reason = "Balance EIP2929")
|
|
||||||
cpt.stack.push cpt.getBalance(address)
|
|
||||||
|
|
||||||
# ------------------
|
# ------------------
|
||||||
|
|
||||||
|
@ -75,12 +72,12 @@ proc callValueOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc callDataLoadOp (k: var VmCtx): EvmResultVoid =
|
proc callDataLoadOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x35, Get input data of current environment
|
## 0x35, Get input data of current environment
|
||||||
let
|
? k.cpt.stack.lsCheck(1)
|
||||||
startPos = ? k.cpt.stack.popInt()
|
let start = k.cpt.stack.lsPeekMemRef(^1)
|
||||||
start = startPos.cleanMemRef
|
|
||||||
|
|
||||||
if start >= k.cpt.msg.data.len:
|
if start >= k.cpt.msg.data.len:
|
||||||
return k.cpt.stack.push 0
|
k.cpt.stack.lsTop 0
|
||||||
|
return ok()
|
||||||
|
|
||||||
# If the data does not take 32 bytes, pad with zeros
|
# If the data does not take 32 bytes, pad with zeros
|
||||||
let
|
let
|
||||||
|
@ -89,9 +86,9 @@ proc callDataLoadOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
# We rely on value being initialized with 0 by default
|
# We rely on value being initialized with 0 by default
|
||||||
var value: array[32, byte]
|
var value: array[32, byte]
|
||||||
value[0 .. presentBytes] = k.cpt.msg.data.toOpenArray(start, endRange)
|
assign(value.toOpenArray(0, presentBytes), k.cpt.msg.data.toOpenArray(start, endRange))
|
||||||
k.cpt.stack.push value
|
k.cpt.stack.lsTop value
|
||||||
|
ok()
|
||||||
|
|
||||||
proc callDataSizeOp (k: var VmCtx): EvmResultVoid =
|
proc callDataSizeOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x36, Get size of input data in current environment.
|
## 0x36, Get size of input data in current environment.
|
||||||
|
@ -100,17 +97,19 @@ proc callDataSizeOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc callDataCopyOp (k: var VmCtx): EvmResultVoid =
|
proc callDataCopyOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x37, Copy input data in current environment to memory.
|
## 0x37, Copy input data in current environment to memory.
|
||||||
let (memStartPos, copyStartPos, size) = ? k.cpt.stack.popInt(3)
|
? k.cpt.stack.lsCheck(3)
|
||||||
|
let
|
||||||
|
cpt = k.cpt
|
||||||
|
memPos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
copyPos = cpt.stack.lsPeekMemRef(^2)
|
||||||
|
len = cpt.stack.lsPeekMemRef(^3)
|
||||||
|
|
||||||
# TODO tests: https://github.com/status-im/nimbus/issues/67
|
cpt.stack.lsShrink(3)
|
||||||
let (memPos, copyPos, len) =
|
? cpt.opcodeGasCost(CallDataCopy,
|
||||||
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
|
cpt.gasCosts[CallDataCopy].m_handler(cpt.memory.len, memPos, len),
|
||||||
|
|
||||||
? k.cpt.opcodeGasCost(CallDataCopy,
|
|
||||||
k.cpt.gasCosts[CallDataCopy].m_handler(k.cpt.memory.len, memPos, len),
|
|
||||||
reason = "CallDataCopy fee")
|
reason = "CallDataCopy fee")
|
||||||
|
|
||||||
k.cpt.memory.writePadded(k.cpt.msg.data, memPos, copyPos, len)
|
cpt.memory.writePadded(cpt.msg.data, memPos, copyPos, len)
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,14 +121,14 @@ proc codeSizeOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc codeCopyOp (k: var VmCtx): EvmResultVoid =
|
proc codeCopyOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x39, Copy code running in current environment to memory.
|
## 0x39, Copy code running in current environment to memory.
|
||||||
|
? k.cpt.stack.lsCheck(3)
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
(memStartPos, copyStartPos, size) = ? cpt.stack.popInt(3)
|
memPos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
copyPos = cpt.stack.lsPeekMemRef(^2)
|
||||||
# TODO tests: https://github.com/status-im/nimbus/issues/67
|
len = cpt.stack.lsPeekMemRef(^3)
|
||||||
let (memPos, copyPos, len) =
|
|
||||||
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
|
|
||||||
|
|
||||||
|
cpt.stack.lsShrink(3)
|
||||||
? cpt.opcodeGasCost(CodeCopy,
|
? cpt.opcodeGasCost(CodeCopy,
|
||||||
cpt.gasCosts[CodeCopy].m_handler(cpt.memory.len, memPos, len),
|
cpt.gasCosts[CodeCopy].m_handler(cpt.memory.len, memPos, len),
|
||||||
reason = "CodeCopy fee")
|
reason = "CodeCopy fee")
|
||||||
|
@ -145,33 +144,32 @@ proc gasPriceOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc extCodeSizeOp (k: var VmCtx): EvmResultVoid =
|
proc extCodeSizeOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x3b, Get size of an account's code
|
## 0x3b, Get size of an account's code
|
||||||
let
|
template ecs256(address): auto =
|
||||||
cpt = k.cpt
|
k.cpt.getCodeSize(address)
|
||||||
address = ? k.cpt.stack.popAddress()
|
k.cpt.stack.unaryAddress(ecs256)
|
||||||
|
|
||||||
cpt.stack.push cpt.getCodeSize(address)
|
|
||||||
|
|
||||||
proc extCodeSizeEIP2929Op (k: var VmCtx): EvmResultVoid =
|
proc extCodeSizeEIP2929Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x3b, Get size of an account's code
|
## 0x3b, Get size of an account's code
|
||||||
let
|
template ecsEIP2929(address): auto =
|
||||||
cpt = k.cpt
|
let gasCost = k.cpt.gasEip2929AccountCheck(address)
|
||||||
address = ? cpt.stack.popAddress()
|
? k.cpt.opcodeGasCost(ExtCodeSize, gasCost, reason = "ExtCodeSize EIP2929")
|
||||||
gasCost = cpt.gasEip2929AccountCheck(address)
|
k.cpt.getCodeSize(address)
|
||||||
|
|
||||||
? cpt.opcodeGasCost(ExtCodeSize, gasCost, reason = "ExtCodeSize EIP2929")
|
k.cpt.stack.unaryAddress(ecsEIP2929)
|
||||||
cpt.stack.push cpt.getCodeSize(address)
|
|
||||||
|
|
||||||
# -----------
|
# -----------
|
||||||
|
|
||||||
proc extCodeCopyOp (k: var VmCtx): EvmResultVoid =
|
proc extCodeCopyOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x3c, Copy an account's code to memory.
|
## 0x3c, Copy an account's code to memory.
|
||||||
|
? k.cpt.stack.lsCheck(4)
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
address = ? cpt.stack.popAddress()
|
address = cpt.stack.lsPeekAddress(^1)
|
||||||
(memStartPos, codeStartPos, size) = ? cpt.stack.popInt(3)
|
memPos = cpt.stack.lsPeekMemRef(^2)
|
||||||
(memPos, codePos, len) =
|
codePos = cpt.stack.lsPeekMemRef(^3)
|
||||||
(memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef)
|
len = cpt.stack.lsPeekMemRef(^4)
|
||||||
|
|
||||||
|
cpt.stack.lsShrink(4)
|
||||||
? cpt.opcodeGasCost(ExtCodeCopy,
|
? cpt.opcodeGasCost(ExtCodeCopy,
|
||||||
cpt.gasCosts[ExtCodeCopy].m_handler(cpt.memory.len, memPos, len),
|
cpt.gasCosts[ExtCodeCopy].m_handler(cpt.memory.len, memPos, len),
|
||||||
reason = "ExtCodeCopy fee")
|
reason = "ExtCodeCopy fee")
|
||||||
|
@ -183,15 +181,17 @@ proc extCodeCopyOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc extCodeCopyEIP2929Op (k: var VmCtx): EvmResultVoid =
|
proc extCodeCopyEIP2929Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x3c, Copy an account's code to memory.
|
## 0x3c, Copy an account's code to memory.
|
||||||
|
? k.cpt.stack.lsCheck(4)
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
address = ? cpt.stack.popAddress()
|
address = cpt.stack.lsPeekAddress(^1)
|
||||||
(memStartPos, codeStartPos, size) = ? cpt.stack.popInt(3)
|
memPos = cpt.stack.lsPeekMemRef(^2)
|
||||||
(memPos, codePos, len) = (memStartPos.cleanMemRef,
|
codePos = cpt.stack.lsPeekMemRef(^3)
|
||||||
codeStartPos.cleanMemRef, size.cleanMemRef)
|
len = cpt.stack.lsPeekMemRef(^4)
|
||||||
|
|
||||||
gasCost = cpt.gasCosts[ExtCodeCopy].m_handler(cpt.memory.len, memPos, len) +
|
gasCost = cpt.gasCosts[ExtCodeCopy].m_handler(cpt.memory.len, memPos, len) +
|
||||||
cpt.gasEip2929AccountCheck(address)
|
cpt.gasEip2929AccountCheck(address)
|
||||||
|
|
||||||
|
cpt.stack.lsShrink(4)
|
||||||
? cpt.opcodeGasCost(ExtCodeCopy, gasCost, reason = "ExtCodeCopy EIP2929")
|
? cpt.opcodeGasCost(ExtCodeCopy, gasCost, reason = "ExtCodeCopy EIP2929")
|
||||||
|
|
||||||
let code = cpt.getCode(address)
|
let code = cpt.getCode(address)
|
||||||
|
@ -208,39 +208,38 @@ proc returnDataSizeOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc returnDataCopyOp (k: var VmCtx): EvmResultVoid =
|
proc returnDataCopyOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x3e, Copy output data from the previous call to memory.
|
## 0x3e, Copy output data from the previous call to memory.
|
||||||
|
? k.cpt.stack.lsCheck(3)
|
||||||
let
|
let
|
||||||
(memStartPos, copyStartPos, size) = ? k.cpt.stack.popInt(3)
|
cpt = k.cpt
|
||||||
(memPos, copyPos, len) =
|
memPos = cpt.stack.lsPeekMemRef(^1)
|
||||||
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
|
copyPos = cpt.stack.lsPeekMemRef(^2)
|
||||||
gasCost = k.cpt.gasCosts[ReturnDataCopy].m_handler(
|
len = cpt.stack.lsPeekMemRef(^3)
|
||||||
k.cpt.memory.len, memPos, len)
|
gasCost = cpt.gasCosts[ReturnDataCopy].m_handler(
|
||||||
|
cpt.memory.len, memPos, len)
|
||||||
|
|
||||||
? k.cpt.opcodeGasCost(ReturnDataCopy, gasCost, reason = "returnDataCopy fee")
|
cpt.stack.lsShrink(3)
|
||||||
|
? cpt.opcodeGasCost(ReturnDataCopy, gasCost, reason = "returnDataCopy fee")
|
||||||
|
|
||||||
if copyPos + len > k.cpt.returnData.len:
|
if copyPos + len > cpt.returnData.len:
|
||||||
return err(opErr(OutOfBounds))
|
return err(opErr(OutOfBounds))
|
||||||
k.cpt.memory.writePadded(k.cpt.returnData, memPos, copyPos, len)
|
cpt.memory.writePadded(cpt.returnData, memPos, copyPos, len)
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
||||||
proc extCodeHashOp (k: var VmCtx): EvmResultVoid =
|
proc extCodeHashOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x3f, Returns the keccak256 hash of a contract’s code
|
## 0x3f, Returns the keccak256 hash of a contract’s code
|
||||||
let
|
template ech256(address): auto =
|
||||||
cpt = k.cpt
|
k.cpt.getCodeHash(address)
|
||||||
address = ? k.cpt.stack.popAddress()
|
k.cpt.stack.unaryAddress(ech256)
|
||||||
|
|
||||||
cpt.stack.push cpt.getCodeHash(address)
|
|
||||||
|
|
||||||
proc extCodeHashEIP2929Op (k: var VmCtx): EvmResultVoid =
|
proc extCodeHashEIP2929Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x3f, EIP2929: Returns the keccak256 hash of a contract’s code
|
## 0x3f, EIP2929: Returns the keccak256 hash of a contract’s code
|
||||||
let
|
template echEIP2929(address): auto =
|
||||||
cpt = k.cpt
|
let gasCost = k.cpt.gasEip2929AccountCheck(address)
|
||||||
address = ? k.cpt.stack.popAddress()
|
? k.cpt.opcodeGasCost(ExtCodeHash, gasCost, reason = "ExtCodeHash EIP2929")
|
||||||
gasCost = cpt.gasEip2929AccountCheck(address)
|
k.cpt.getCodeHash(address)
|
||||||
|
k.cpt.stack.unaryAddress(echEIP2929)
|
||||||
? cpt.opcodeGasCost(ExtCodeHash, gasCost, reason = "ExtCodeHash EIP2929")
|
|
||||||
cpt.stack.push cpt.getCodeHash(address)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public, op exec table entries
|
# Public, op exec table entries
|
||||||
|
|
|
@ -22,7 +22,6 @@ import
|
||||||
../../stack,
|
../../stack,
|
||||||
../gas_costs,
|
../gas_costs,
|
||||||
../op_codes,
|
../op_codes,
|
||||||
../utils/utils_numeric,
|
|
||||||
./oph_defs,
|
./oph_defs,
|
||||||
eth/common
|
eth/common
|
||||||
|
|
||||||
|
@ -32,24 +31,28 @@ import
|
||||||
|
|
||||||
proc sha3Op(k: var VmCtx): EvmResultVoid =
|
proc sha3Op(k: var VmCtx): EvmResultVoid =
|
||||||
## 0x20, Compute Keccak-256 hash.
|
## 0x20, Compute Keccak-256 hash.
|
||||||
|
? k.cpt.stack.lsCheck(2)
|
||||||
let
|
let
|
||||||
(startPos, length) = ? k.cpt.stack.popInt(2)
|
cpt = k.cpt
|
||||||
(pos, len) = (startPos.safeInt, length.safeInt)
|
pos = cpt.stack.lsPeekSafeInt(^1)
|
||||||
|
len = cpt.stack.lsPeekSafeInt(^2)
|
||||||
|
cpt.stack.lsShrink(1)
|
||||||
|
|
||||||
if pos < 0 or len < 0 or pos > 2147483648'i64:
|
if pos < 0 or len < 0 or pos > 2147483648'i64:
|
||||||
return err(opErr(OutOfBounds))
|
return err(opErr(OutOfBounds))
|
||||||
|
|
||||||
? k.cpt.opcodeGasCost(Op.Sha3,
|
? cpt.opcodeGasCost(Op.Sha3,
|
||||||
k.cpt.gasCosts[Op.Sha3].m_handler(k.cpt.memory.len, pos, len),
|
cpt.gasCosts[Op.Sha3].m_handler(cpt.memory.len, pos, len),
|
||||||
reason = "SHA3: word gas cost")
|
reason = "SHA3: word gas cost")
|
||||||
|
|
||||||
k.cpt.memory.extend(pos, len)
|
cpt.memory.extend(pos, len)
|
||||||
|
|
||||||
let endRange = min(pos + len, k.cpt.memory.len) - 1
|
let endRange = min(pos + len, cpt.memory.len) - 1
|
||||||
if endRange == -1 or pos >= k.cpt.memory.len:
|
if endRange == -1 or pos >= cpt.memory.len:
|
||||||
k.cpt.stack.push(EMPTY_SHA3)
|
cpt.stack.lsTop(EMPTY_SHA3)
|
||||||
else:
|
else:
|
||||||
k.cpt.stack.push keccakHash k.cpt.memory.bytes.toOpenArray(pos, endRange)
|
cpt.stack.lsTop keccakHash k.cpt.memory.bytes.toOpenArray(pos, endRange)
|
||||||
|
ok()
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public, op exec table entries
|
# Public, op exec table entries
|
||||||
|
|
|
@ -24,7 +24,6 @@ import
|
||||||
../../types,
|
../../types,
|
||||||
../gas_costs,
|
../gas_costs,
|
||||||
../op_codes,
|
../op_codes,
|
||||||
../utils/utils_numeric,
|
|
||||||
./oph_defs,
|
./oph_defs,
|
||||||
./oph_gen_handlers,
|
./oph_gen_handlers,
|
||||||
./oph_helpers,
|
./oph_helpers,
|
||||||
|
@ -52,8 +51,11 @@ proc fnInfo(n: int): string {.compileTime.} =
|
||||||
proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid =
|
proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid =
|
||||||
static: doAssert(topicCount in 0 .. 4)
|
static: doAssert(topicCount in 0 .. 4)
|
||||||
? checkInStaticContext(c)
|
? checkInStaticContext(c)
|
||||||
let (memStartPosition, size) = ? c.stack.popInt(2)
|
const stackSize = 2 + topicCount
|
||||||
let (memPos, len) = (memStartPosition.cleanMemRef, size.cleanMemRef)
|
? c.stack.lsCheck(stackSize)
|
||||||
|
let
|
||||||
|
memPos = c.stack.lsPeekMemRef(^1)
|
||||||
|
len = c.stack.lsPeekMemRef(^2)
|
||||||
|
|
||||||
if memPos < 0 or len < 0:
|
if memPos < 0 or len < 0:
|
||||||
return err(opErr(OutOfBounds))
|
return err(opErr(OutOfBounds))
|
||||||
|
@ -66,7 +68,7 @@ proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid
|
||||||
when evmc_enabled:
|
when evmc_enabled:
|
||||||
var topics: array[4, evmc_bytes32]
|
var topics: array[4, evmc_bytes32]
|
||||||
for i in 0 ..< topicCount:
|
for i in 0 ..< topicCount:
|
||||||
topics[i].bytes = ? c.stack.popTopic()
|
topics[i].bytes = c.stack.lsPeekTopic(^(i+3))
|
||||||
|
|
||||||
c.host.emitLog(c.msg.contractAddress,
|
c.host.emitLog(c.msg.contractAddress,
|
||||||
c.memory.read(memPos, len),
|
c.memory.read(memPos, len),
|
||||||
|
@ -75,13 +77,13 @@ proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid
|
||||||
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:
|
||||||
let topic = ? c.stack.popTopic()
|
log.topics.add c.stack.lsPeekTopic(^(i+3))
|
||||||
log.topics.add topic
|
|
||||||
|
|
||||||
assign(log.data, c.memory.read(memPos, len))
|
assign(log.data, c.memory.read(memPos, len))
|
||||||
log.address = c.msg.contractAddress
|
log.address = c.msg.contractAddress
|
||||||
c.addLogEntry(log)
|
c.addLogEntry(log)
|
||||||
|
|
||||||
|
c.stack.lsShrink(stackSize)
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
@ -24,7 +24,6 @@ import
|
||||||
../gas_meter,
|
../gas_meter,
|
||||||
../gas_costs,
|
../gas_costs,
|
||||||
../op_codes,
|
../op_codes,
|
||||||
../utils/utils_numeric,
|
|
||||||
./oph_defs,
|
./oph_defs,
|
||||||
./oph_helpers,
|
./oph_helpers,
|
||||||
eth/common,
|
eth/common,
|
||||||
|
@ -129,68 +128,81 @@ proc popOp(k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc mloadOp (k: var VmCtx): EvmResultVoid =
|
proc mloadOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x51, Load word from memory
|
## 0x51, Load word from memory
|
||||||
let memStartPos = ? k.cpt.stack.popInt()
|
|
||||||
|
|
||||||
let memPos = memStartPos.cleanMemRef
|
? k.cpt.stack.lsCheck(1)
|
||||||
? k.cpt.opcodeGasCost(Mload,
|
let
|
||||||
k.cpt.gasCosts[Mload].m_handler(k.cpt.memory.len, memPos, 32),
|
cpt = k.cpt
|
||||||
|
memPos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
|
||||||
|
? cpt.opcodeGasCost(Mload,
|
||||||
|
cpt.gasCosts[Mload].m_handler(cpt.memory.len, memPos, 32),
|
||||||
reason = "MLOAD: GasVeryLow + memory expansion")
|
reason = "MLOAD: GasVeryLow + memory expansion")
|
||||||
|
|
||||||
k.cpt.memory.extend(memPos, 32)
|
cpt.memory.extend(memPos, 32)
|
||||||
k.cpt.stack.push k.cpt.memory.read32Bytes(memPos)
|
cpt.stack.lsTop cpt.memory.read32Bytes(memPos)
|
||||||
|
ok()
|
||||||
|
|
||||||
|
|
||||||
proc mstoreOp (k: var VmCtx): EvmResultVoid =
|
proc mstoreOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x52, Save word to memory
|
## 0x52, Save word to memory
|
||||||
let (memStartPos, value) = ? k.cpt.stack.popInt(2)
|
? k.cpt.stack.lsCheck(2)
|
||||||
|
let
|
||||||
|
cpt = k.cpt
|
||||||
|
memPos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
value = cpt.stack.lsPeekInt(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
let memPos = memStartPos.cleanMemRef
|
? cpt.opcodeGasCost(Mstore,
|
||||||
? k.cpt.opcodeGasCost(Mstore,
|
cpt.gasCosts[Mstore].m_handler(cpt.memory.len, memPos, 32),
|
||||||
k.cpt.gasCosts[Mstore].m_handler(k.cpt.memory.len, memPos, 32),
|
|
||||||
reason = "MSTORE: GasVeryLow + memory expansion")
|
reason = "MSTORE: GasVeryLow + memory expansion")
|
||||||
|
|
||||||
k.cpt.memory.extend(memPos, 32)
|
cpt.memory.extend(memPos, 32)
|
||||||
k.cpt.memory.write(memPos, value.toBytesBE)
|
cpt.memory.write(memPos, value.toBytesBE)
|
||||||
|
|
||||||
|
|
||||||
proc mstore8Op (k: var VmCtx): EvmResultVoid =
|
proc mstore8Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x53, Save byte to memory
|
## 0x53, Save byte to memory
|
||||||
let (memStartPos, value) = ? k.cpt.stack.popInt(2)
|
? k.cpt.stack.lsCheck(2)
|
||||||
|
let
|
||||||
|
cpt = k.cpt
|
||||||
|
memPos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
value = cpt.stack.lsPeekInt(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
let memPos = memStartPos.cleanMemRef
|
? cpt.opcodeGasCost(Mstore8,
|
||||||
? k.cpt.opcodeGasCost(Mstore8,
|
cpt.gasCosts[Mstore8].m_handler(cpt.memory.len, memPos, 1),
|
||||||
k.cpt.gasCosts[Mstore8].m_handler(k.cpt.memory.len, memPos, 1),
|
|
||||||
reason = "MSTORE8: GasVeryLow + memory expansion")
|
reason = "MSTORE8: GasVeryLow + memory expansion")
|
||||||
|
|
||||||
k.cpt.memory.extend(memPos, 1)
|
cpt.memory.extend(memPos, 1)
|
||||||
k.cpt.memory.write(memPos, value.toByteArrayBE[31])
|
cpt.memory.write(memPos, value.toByteArrayBE[31])
|
||||||
|
|
||||||
|
|
||||||
# -------
|
# -------
|
||||||
|
|
||||||
proc sloadOp (k: var VmCtx): EvmResultVoid =
|
proc sloadOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x54, Load word from storage.
|
## 0x54, Load word from storage.
|
||||||
let
|
template sload256(top, slot, conv) =
|
||||||
cpt = k.cpt
|
top = k.cpt.getStorage(slot)
|
||||||
slot = ? cpt.stack.popInt()
|
k.cpt.stack.unaryWithTop(sload256)
|
||||||
cpt.stack.push cpt.getStorage(slot)
|
|
||||||
|
|
||||||
proc sloadEIP2929Op (k: var VmCtx): EvmResultVoid =
|
proc sloadEIP2929Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x54, EIP2929: Load word from storage for Berlin and later
|
## 0x54, EIP2929: Load word from storage for Berlin and later
|
||||||
let
|
template sloadEIP2929(top, slot, conv) =
|
||||||
cpt = k.cpt
|
let gasCost = k.cpt.gasEip2929AccountCheck(k.cpt.msg.contractAddress, slot)
|
||||||
slot = ? cpt.stack.popInt()
|
? k.cpt.opcodeGasCost(Sload, gasCost, reason = "sloadEIP2929")
|
||||||
gasCost = cpt.gasEip2929AccountCheck(cpt.msg.contractAddress, slot)
|
top = k.cpt.getStorage(slot)
|
||||||
? cpt.opcodeGasCost(Sload, gasCost, reason = "sloadEIP2929")
|
k.cpt.stack.unaryWithTop(sloadEIP2929)
|
||||||
cpt.stack.push cpt.getStorage(slot)
|
|
||||||
|
|
||||||
# -------
|
# -------
|
||||||
|
|
||||||
proc sstoreOp (k: var VmCtx): EvmResultVoid =
|
proc sstoreOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x55, Save word to storage.
|
## 0x55, Save word to storage.
|
||||||
|
? k.cpt.stack.lsCheck(2)
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
(slot, newValue) = ? cpt.stack.popInt(2)
|
slot = cpt.stack.lsPeekInt(^1)
|
||||||
|
newValue = cpt.stack.lsPeekInt(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
? checkInStaticContext(cpt)
|
? checkInStaticContext(cpt)
|
||||||
sstoreEvmcOrSstore(cpt, slot, newValue)
|
sstoreEvmcOrSstore(cpt, slot, newValue)
|
||||||
|
@ -198,9 +210,12 @@ proc sstoreOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc sstoreEIP1283Op (k: var VmCtx): EvmResultVoid =
|
proc sstoreEIP1283Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x55, EIP1283: sstore for Constantinople and later
|
## 0x55, EIP1283: sstore for Constantinople and later
|
||||||
|
? k.cpt.stack.lsCheck(2)
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
(slot, newValue) = ? cpt.stack.popInt(2)
|
slot = cpt.stack.lsPeekInt(^1)
|
||||||
|
newValue = cpt.stack.lsPeekInt(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
? checkInStaticContext(cpt)
|
? checkInStaticContext(cpt)
|
||||||
sstoreEvmcOrNetGasMetering(cpt, slot, newValue)
|
sstoreEvmcOrNetGasMetering(cpt, slot, newValue)
|
||||||
|
@ -208,9 +223,12 @@ proc sstoreEIP1283Op (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc sstoreEIP2200Op (k: var VmCtx): EvmResultVoid =
|
proc sstoreEIP2200Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x55, EIP2200: sstore for Istanbul and later
|
## 0x55, EIP2200: sstore for Istanbul and later
|
||||||
|
? k.cpt.stack.lsCheck(2)
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
(slot, newValue) = ? cpt.stack.popInt(2)
|
slot = cpt.stack.lsPeekInt(^1)
|
||||||
|
newValue = cpt.stack.lsPeekInt(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
? checkInStaticContext(cpt)
|
? checkInStaticContext(cpt)
|
||||||
const SentryGasEIP2200 = 2300
|
const SentryGasEIP2200 = 2300
|
||||||
|
@ -223,9 +241,12 @@ proc sstoreEIP2200Op (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc sstoreEIP2929Op (k: var VmCtx): EvmResultVoid =
|
proc sstoreEIP2929Op (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x55, EIP2929: sstore for Berlin and later
|
## 0x55, EIP2929: sstore for Berlin and later
|
||||||
|
? k.cpt.stack.lsCheck(2)
|
||||||
let
|
let
|
||||||
cpt = k.cpt
|
cpt = k.cpt
|
||||||
(slot, newValue) = ? cpt.stack.popInt(2)
|
slot = cpt.stack.lsPeekInt(^1)
|
||||||
|
newValue = cpt.stack.lsPeekInt(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
? checkInStaticContext(cpt)
|
? checkInStaticContext(cpt)
|
||||||
|
|
||||||
|
@ -257,7 +278,12 @@ proc jumpOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc jumpIOp (k: var VmCtx): EvmResultVoid =
|
proc jumpIOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x57, Conditionally alter the program counter.
|
## 0x57, Conditionally alter the program counter.
|
||||||
let (jumpTarget, testedValue) = ? k.cpt.stack.popInt(2)
|
? k.cpt.stack.lsCheck(2)
|
||||||
|
let
|
||||||
|
jumpTarget = k.cpt.stack.lsPeekInt(^1)
|
||||||
|
testedValue = k.cpt.stack.lsPeekInt(^2)
|
||||||
|
k.cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
if testedValue.isZero:
|
if testedValue.isZero:
|
||||||
return ok()
|
return ok()
|
||||||
jumpImpl(k.cpt, jumpTarget)
|
jumpImpl(k.cpt, jumpTarget)
|
||||||
|
@ -283,33 +309,43 @@ proc jumpDestOp (k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc tloadOp (k: var VmCtx): EvmResultVoid =
|
proc tloadOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x5c, Load word from transient storage.
|
## 0x5c, Load word from transient storage.
|
||||||
|
? k.cpt.stack.lsCheck(1)
|
||||||
let
|
let
|
||||||
slot = ? k.cpt.stack.popInt()
|
cpt = k.cpt
|
||||||
val = k.cpt.getTransientStorage(slot)
|
slot = cpt.stack.lsPeekInt(^1)
|
||||||
k.cpt.stack.push val
|
val = cpt.getTransientStorage(slot)
|
||||||
|
cpt.stack.lsTop val
|
||||||
|
ok()
|
||||||
|
|
||||||
proc tstoreOp (k: var VmCtx): EvmResultVoid =
|
proc tstoreOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x5d, Save word to transient storage.
|
## 0x5d, Save word to transient storage.
|
||||||
? checkInStaticContext(k.cpt)
|
? checkInStaticContext(k.cpt)
|
||||||
|
|
||||||
|
? k.cpt.stack.lsCheck(2)
|
||||||
let
|
let
|
||||||
slot = ? k.cpt.stack.popInt()
|
cpt = k.cpt
|
||||||
val = ? k.cpt.stack.popInt()
|
slot = cpt.stack.lsPeekInt(^1)
|
||||||
k.cpt.setTransientStorage(slot, val)
|
val = cpt.stack.lsPeekInt(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
|
cpt.setTransientStorage(slot, val)
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
proc mCopyOp (k: var VmCtx): EvmResultVoid =
|
proc mCopyOp (k: var VmCtx): EvmResultVoid =
|
||||||
## 0x5e, Copy memory
|
## 0x5e, Copy memory
|
||||||
let (dst, src, size) = ? k.cpt.stack.popInt(3)
|
? k.cpt.stack.lsCheck(3)
|
||||||
|
let
|
||||||
|
cpt = k.cpt
|
||||||
|
dstPos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
srcPos = cpt.stack.lsPeekMemRef(^2)
|
||||||
|
len = cpt.stack.lsPeekMemRef(^3)
|
||||||
|
cpt.stack.lsShrink(3)
|
||||||
|
|
||||||
let (dstPos, srcPos, len) =
|
? cpt.opcodeGasCost(Mcopy,
|
||||||
(dst.cleanMemRef, src.cleanMemRef, size.cleanMemRef)
|
cpt.gasCosts[Mcopy].m_handler(cpt.memory.len, max(dstPos, srcPos), len),
|
||||||
|
|
||||||
? k.cpt.opcodeGasCost(Mcopy,
|
|
||||||
k.cpt.gasCosts[Mcopy].m_handler(k.cpt.memory.len, max(dstPos, srcPos), len),
|
|
||||||
reason = "Mcopy fee")
|
reason = "Mcopy fee")
|
||||||
|
|
||||||
k.cpt.memory.copy(dstPos, srcPos, len)
|
cpt.memory.copy(dstPos, srcPos, len)
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -40,7 +40,7 @@ proc fnInfo(n: int): string {.compileTime.} =
|
||||||
"Push " & blurb & " on the stack"
|
"Push " & blurb & " on the stack"
|
||||||
|
|
||||||
|
|
||||||
proc pushImpl(k: var VmCtx; n: static int): EvmResultVoid =
|
template pushImpl(k: var VmCtx; n: static int): EvmResultVoid =
|
||||||
k.cpt.stack.push k.cpt.code.readVmWord(n)
|
k.cpt.stack.push k.cpt.code.readVmWord(n)
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
@ -41,7 +41,7 @@ proc fnInfo(n: int): string {.compileTime.} =
|
||||||
"Exchange first and " & blurb & " stack items"
|
"Exchange first and " & blurb & " stack items"
|
||||||
|
|
||||||
|
|
||||||
func swapImpl(k: var VmCtx; n: static int): EvmResultVoid =
|
template swapImpl(k: var VmCtx; n: static int): EvmResultVoid =
|
||||||
k.cpt.stack.swap(n)
|
k.cpt.stack.swap(n)
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
@ -23,7 +23,6 @@ import
|
||||||
../../types,
|
../../types,
|
||||||
../gas_costs,
|
../gas_costs,
|
||||||
../op_codes,
|
../op_codes,
|
||||||
../utils/utils_numeric,
|
|
||||||
./oph_defs,
|
./oph_defs,
|
||||||
./oph_helpers,
|
./oph_helpers,
|
||||||
eth/common,
|
eth/common,
|
||||||
|
@ -40,31 +39,40 @@ when not defined(evmc_enabled):
|
||||||
|
|
||||||
proc returnOp(k: var VmCtx): EvmResultVoid =
|
proc returnOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0xf3, Halt execution returning output data.
|
## 0xf3, Halt execution returning output data.
|
||||||
let (startPos, size) = ? k.cpt.stack.popInt(2)
|
? k.cpt.stack.lsCheck(2)
|
||||||
|
let
|
||||||
|
cpt = k.cpt
|
||||||
|
pos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
len = cpt.stack.lsPeekMemRef(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
|
? cpt.opcodeGasCost(Return,
|
||||||
? k.cpt.opcodeGasCost(Return,
|
cpt.gasCosts[Return].m_handler(cpt.memory.len, pos, len),
|
||||||
k.cpt.gasCosts[Return].m_handler(k.cpt.memory.len, pos, len),
|
|
||||||
reason = "RETURN")
|
reason = "RETURN")
|
||||||
k.cpt.memory.extend(pos, len)
|
|
||||||
assign(k.cpt.output, k.cpt.memory.read(pos, len))
|
cpt.memory.extend(pos, len)
|
||||||
|
assign(cpt.output, cpt.memory.read(pos, len))
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
|
|
||||||
proc revertOp(k: var VmCtx): EvmResultVoid =
|
proc revertOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0xfd, Halt execution reverting state changes but returning data
|
## 0xfd, Halt execution reverting state changes but returning data
|
||||||
## and remaining gas.
|
## and remaining gas.
|
||||||
let (startPos, size) = ? k.cpt.stack.popInt(2)
|
? k.cpt.stack.lsCheck(2)
|
||||||
|
let
|
||||||
|
cpt = k.cpt
|
||||||
|
pos = cpt.stack.lsPeekMemRef(^1)
|
||||||
|
len = cpt.stack.lsPeekMemRef(^2)
|
||||||
|
cpt.stack.lsShrink(2)
|
||||||
|
|
||||||
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
|
? cpt.opcodeGasCost(Revert,
|
||||||
? k.cpt.opcodeGasCost(Revert,
|
cpt.gasCosts[Revert].m_handler(cpt.memory.len, pos, len),
|
||||||
k.cpt.gasCosts[Revert].m_handler(k.cpt.memory.len, pos, len),
|
|
||||||
reason = "REVERT")
|
reason = "REVERT")
|
||||||
|
|
||||||
k.cpt.memory.extend(pos, len)
|
cpt.memory.extend(pos, len)
|
||||||
assign(k.cpt.output, k.cpt.memory.read(pos, len))
|
assign(cpt.output, cpt.memory.read(pos, len))
|
||||||
# setError(msg, false) will signal cheap revert
|
# setError(msg, false) will signal cheap revert
|
||||||
k.cpt.setError(EVMC_REVERT, "REVERT opcode executed", false)
|
cpt.setError(EVMC_REVERT, "REVERT opcode executed", false)
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
proc invalidOp(k: var VmCtx): EvmResultVoid =
|
proc invalidOp(k: var VmCtx): EvmResultVoid =
|
||||||
|
@ -74,8 +82,10 @@ proc invalidOp(k: var VmCtx): EvmResultVoid =
|
||||||
|
|
||||||
proc selfDestructOp(k: var VmCtx): EvmResultVoid =
|
proc selfDestructOp(k: var VmCtx): EvmResultVoid =
|
||||||
## 0xff, Halt execution and register account for later deletion.
|
## 0xff, Halt execution and register account for later deletion.
|
||||||
let cpt = k.cpt
|
let
|
||||||
let beneficiary = ? cpt.stack.popAddress()
|
cpt = k.cpt
|
||||||
|
beneficiary = ? cpt.stack.popAddress()
|
||||||
|
|
||||||
when defined(evmc_enabled):
|
when defined(evmc_enabled):
|
||||||
block:
|
block:
|
||||||
cpt.selfDestruct(beneficiary)
|
cpt.selfDestruct(beneficiary)
|
||||||
|
|
|
@ -164,7 +164,7 @@ func `[]`*(stack: EvmStack, i: BackwardsIndex, T: typedesc): EvmResult[T] =
|
||||||
|
|
||||||
func peekInt*(stack: EvmStack): EvmResult[UInt256] =
|
func peekInt*(stack: EvmStack): EvmResult[UInt256] =
|
||||||
? ensurePop(stack, 1)
|
? ensurePop(stack, 1)
|
||||||
ok(fromStackElem(stack.values[^1], Uint256))
|
ok(fromStackElem(stack.values[^1], UInt256))
|
||||||
|
|
||||||
func peekAddress*(stack: EvmStack): EvmResult[EthAddress] =
|
func peekAddress*(stack: EvmStack): EvmResult[EthAddress] =
|
||||||
? ensurePop(stack, 1)
|
? ensurePop(stack, 1)
|
||||||
|
@ -184,3 +184,74 @@ iterator items*(stack: EvmStack): UInt256 =
|
||||||
iterator pairs*(stack: EvmStack): (int, UInt256) =
|
iterator pairs*(stack: EvmStack): (int, UInt256) =
|
||||||
for i, v in stack.values:
|
for i, v in stack.values:
|
||||||
yield (i, v)
|
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))
|
||||||
|
|
|
@ -30,6 +30,7 @@ proc opEnvMain*() =
|
||||||
fork: London
|
fork: London
|
||||||
success: false
|
success: false
|
||||||
memory: "0x0000000000000000000000000000000000000000000000000000000000000000"
|
memory: "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
stack: "0x00"
|
||||||
|
|
||||||
assembler: # CodeCopy OP
|
assembler: # CodeCopy OP
|
||||||
title: "CODECOPY_1"
|
title: "CODECOPY_1"
|
||||||
|
@ -310,6 +311,7 @@ proc opEnvMain*() =
|
||||||
stack:
|
stack:
|
||||||
"0x5E"
|
"0x5E"
|
||||||
"0x07"
|
"0x07"
|
||||||
|
"0x471FD3AD3E9EEADEEC4608B92D16CE6B500704CC"
|
||||||
|
|
||||||
assembler: # CODESIZE OP
|
assembler: # CODESIZE OP
|
||||||
title: "CODESIZE_1"
|
title: "CODESIZE_1"
|
||||||
|
|
Loading…
Reference in New Issue