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:
andri lim 2024-07-18 18:59:53 +07:00 committed by GitHub
parent f9956eba59
commit ee323d5ff8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 488 additions and 350 deletions

View File

@ -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 twos complement signed integer. ## Extend length of twos 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

View File

@ -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.

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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 contracts code ## 0x3f, Returns the keccak256 hash of a contracts 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 contracts code ## 0x3f, EIP2929: Returns the keccak256 hash of a contracts 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

View File

@ -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

View File

@ -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

View File

@ -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()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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"