From ee323d5ff80b8d0b3e6f1321c7a431e9c4a529ca Mon Sep 17 00:00:00 2001 From: andri lim Date: Thu, 18 Jul 2024 18:59:53 +0700 Subject: [PATCH] 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 --- .../op_handlers/oph_arithmetic.nim | 280 +++++++++--------- .../interpreter/op_handlers/oph_blockdata.nim | 31 +- .../evm/interpreter/op_handlers/oph_call.nim | 64 ++-- .../interpreter/op_handlers/oph_create.nim | 26 +- .../evm/interpreter/op_handlers/oph_dup.nim | 2 +- .../interpreter/op_handlers/oph_envinfo.nim | 145 +++++---- .../evm/interpreter/op_handlers/oph_hash.nim | 25 +- .../evm/interpreter/op_handlers/oph_log.nim | 14 +- .../interpreter/op_handlers/oph_memory.nim | 130 +++++--- .../evm/interpreter/op_handlers/oph_push.nim | 2 +- .../evm/interpreter/op_handlers/oph_swap.nim | 2 +- .../interpreter/op_handlers/oph_sysops.nim | 42 ++- nimbus/evm/stack.nim | 73 ++++- tests/test_op_env.nim | 2 + 14 files changed, 488 insertions(+), 350 deletions(-) diff --git a/nimbus/evm/interpreter/op_handlers/oph_arithmetic.nim b/nimbus/evm/interpreter/op_handlers/oph_arithmetic.nim index 5323c1a70..e84d6920c 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_arithmetic.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_arithmetic.nim @@ -39,245 +39,243 @@ func slt(x, y: UInt256): bool = proc addOp (k: var VmCtx): EvmResultVoid = ## 0x01, Addition - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push(lhs + rhs) + k.cpt.stack.binaryOp(`+`) proc mulOp(k: var VmCtx): EvmResultVoid = ## 0x02, Multiplication - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push(lhs * rhs) + k.cpt.stack.binaryOp(`*`) proc subOp(k: var VmCtx): EvmResultVoid = ## 0x03, Substraction - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push(lhs - rhs) + k.cpt.stack.binaryOp(`-`) proc divideOp(k: var VmCtx): EvmResultVoid = ## 0x04, Division - let - (lhs, rhs) = ? k.cpt.stack.popInt(2) - value = if rhs.isZero: - # EVM special casing of div by 0 - zero(UInt256) - else: - lhs div rhs - - k.cpt.stack.push value + template div256(top, lhs, rhs) = + if rhs.isZero: + # EVM special casing of div by 0 + top = zero(UInt256) + else: + top = lhs div rhs + k.cpt.stack.binaryWithTop(div256) proc sdivOp(k: var VmCtx): EvmResultVoid = ## 0x05, Signed division - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - - var r: UInt256 - if rhs.isZero.not: - var a = lhs - var b = rhs - var signA, signB: bool - extractSign(a, signA) - extractSign(b, signB) - r = a div b - setSign(r, signA xor signB) - k.cpt.stack.push(r) + template sdiv256(top, lhs, rhs) = + if rhs.isZero.not: + var signA, signB: bool + extractSign(lhs, signA) + extractSign(rhs, signB) + top = lhs div rhs + setSign(top, signA xor signB) + k.cpt.stack.binaryWithTop(sdiv256) proc moduloOp(k: var VmCtx): EvmResultVoid = ## 0x06, Modulo - let - (lhs, rhs) = ? k.cpt.stack.popInt(2) - value = if rhs.isZero: - zero(UInt256) - else: - lhs mod rhs - - k.cpt.stack.push value + template mod256(top, lhs, rhs) = + if rhs.isZero: + top = zero(UInt256) + else: + top = lhs mod rhs + k.cpt.stack.binaryWithTop(mod256) proc smodOp(k: var VmCtx): EvmResultVoid = ## 0x07, Signed modulo - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - - var r: UInt256 - if rhs.isZero.not: - var sign: bool - var v = lhs - var m = rhs - extractSign(m, sign) - extractSign(v, sign) - r = v mod m - setSign(r, sign) - k.cpt.stack.push(r) + template smod256(top, lhs, rhs) = + if rhs.isZero.not: + var sign: bool + extractSign(rhs, sign) + extractSign(lhs, sign) + top = lhs mod rhs + setSign(top, sign) + k.cpt.stack.binaryWithTop(smod256) proc addmodOp(k: var VmCtx): EvmResultVoid = ## 0x08, Modulo addition ## Intermediate computations do not roll over at 2^256 + ? k.cpt.stack.lsCheck(3) 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: zero(UInt256) else: 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 = ## 0x09, Modulo multiplication ## Intermediate computations do not roll over at 2^256 + ? k.cpt.stack.lsCheck(3) 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: zero(UInt256) else: 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 = ## 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, - k.cpt.gasCosts[Exp].d_handler(exponent), - reason = "EXP: exponent bytes") - - let value = if not base.isZero: - base.pow(exponent) - elif exponent.isZero: - # https://github.com/ethereum/yellowpaper/issues/257 - # https://github.com/ethereum/tests/pull/460 - # https://github.com/ewasm/evm2wasm/issues/137 - 1.u256 - else: - zero(UInt256) - - k.cpt.stack.push value + if not base.isZero: + top = base.pow(exponent) + elif exponent.isZero: + # https://github.com/ethereum/yellowpaper/issues/257 + # https://github.com/ethereum/tests/pull/460 + # https://github.com/ewasm/evm2wasm/issues/137 + top = 1.u256 + else: + top = zero(UInt256) + k.cpt.stack.binaryWithTop(exp256) proc signExtendOp(k: var VmCtx): EvmResultVoid = ## 0x0B, Sign extend ## Extend length of two’s complement signed integer. - let (bits, value) = ? k.cpt.stack.popInt(2) - - var res: UInt256 - if bits <= 31.u256: - let - one = 1.u256 - testBit = bits.truncate(int) * 8 + 7 - bitPos = one shl testBit - mask = bitPos - one - if not isZero(value and bitPos): - res = value or (not mask) + template se256(top, bits, value) = + const one = 1.u256 + if bits <= 31.u256: + let + testBit = bits.truncate(int) * 8 + 7 + bitPos = one shl testBit + mask = bitPos - one + if not isZero(value and bitPos): + top = value or (not mask) + else: + top = value and mask else: - res = value and mask - else: - res = value - k.cpt.stack.push res + top = value + k.cpt.stack.binaryWithTop(se256) proc ltOp(k: var VmCtx): EvmResultVoid = ## 0x10, Less-than comparison - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push((lhs < rhs).uint.u256) + template lt256(lhs, rhs): auto = + (lhs < rhs).uint.u256 + k.cpt.stack.binaryOp(lt256) proc gtOp(k: var VmCtx): EvmResultVoid = ## 0x11, Greater-than comparison - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push((lhs > rhs).uint.u256) + template gt256(lhs, rhs): auto = + (lhs > rhs).uint.u256 + k.cpt.stack.binaryOp(gt256) proc sltOp(k: var VmCtx): EvmResultVoid = ## 0x12, Signed less-than comparison - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push(slt(lhs, rhs).uint.u256) + template slt256(lhs, rhs): auto = + slt(lhs, rhs).uint.u256 + k.cpt.stack.binaryOp(slt256) proc sgtOp(k: var VmCtx): EvmResultVoid = ## 0x13, Signed greater-than comparison - let (lhs, rhs) = ? k.cpt.stack.popInt(2) # Arguments are swapped and SLT is used. - k.cpt.stack.push(slt(rhs, lhs).uint.u256) + template sgt256(lhs, rhs): auto = + slt(rhs, lhs).uint.u256 + k.cpt.stack.binaryOp(sgt256) proc eqOp(k: var VmCtx): EvmResultVoid = ## 0x14, Equality comparison - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push((lhs == rhs).uint.u256) + template eq256(lhs, rhs): auto = + (lhs == rhs).uint.u256 + k.cpt.stack.binaryOp(eq256) proc isZeroOp(k: var VmCtx): EvmResultVoid = ## 0x15, Check if zero - let value = ? k.cpt.stack.popInt() - k.cpt.stack.push(value.isZero.uint.u256) + template zero256(value): auto = + value.isZero.uint.u256 + k.cpt.stack.unaryOp(zero256) proc andOp(k: var VmCtx): EvmResultVoid = ## 0x16, Bitwise AND - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push(lhs and rhs) + k.cpt.stack.binaryOp(`and`) proc orOp(k: var VmCtx): EvmResultVoid = ## 0x17, Bitwise OR - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push(lhs or rhs) + k.cpt.stack.binaryOp(`or`) proc xorOp(k: var VmCtx): EvmResultVoid = ## 0x18, Bitwise XOR - let (lhs, rhs) = ? k.cpt.stack.popInt(2) - k.cpt.stack.push(lhs xor rhs) + k.cpt.stack.binaryOp(`xor`) proc notOp(k: var VmCtx): EvmResultVoid = ## 0x19, Check if zero - let value = ? k.cpt.stack.popInt() - k.cpt.stack.push(value.not) + k.cpt.stack.unaryOp(`not`) proc byteOp(k: var VmCtx): EvmResultVoid = ## 0x20, Retrieve single byte from word. - let - (position, value) = ? k.cpt.stack.popInt(2) - val = if position >= 32.u256: - zero(UInt256) - else: - let pos = position.truncate(int) - when system.cpuEndian == bigEndian: - cast[array[32, byte]](value)[pos].u256 - else: - cast[array[32, byte]](value)[31 - pos].u256 - - k.cpt.stack.push val + template byte256(top, position, value) = + if position >= 32.u256: + top = zero(UInt256) + else: + let pos = position.truncate(int) + when system.cpuEndian == bigEndian: + top = cast[array[32, byte]](value)[pos].u256 + else: + top = cast[array[32, byte]](value)[31 - pos].u256 + k.cpt.stack.binaryWithTop(byte256) # Constantinople's new opcodes proc shlOp(k: var VmCtx): EvmResultVoid = - let (shift, num) = ? k.cpt.stack.popInt(2) - let shiftLen = shift.safeInt - if shiftLen >= 256: - k.cpt.stack.push 0 - else: - k.cpt.stack.push(num shl shiftLen) + ## 0x1b, Shift left + template shl256(top, lhs, num) = + let shiftLen = lhs.safeInt + if shiftLen >= 256: + top = 0.u256 + else: + top = num shl shiftLen + + k.cpt.stack.binaryWithTop(shl256) proc shrOp(k: var VmCtx): EvmResultVoid = - let (shift, num) = ? k.cpt.stack.popInt(2) - let shiftLen = shift.safeInt - if shiftLen >= 256: - k.cpt.stack.push 0 - else: - # uint version of `shr` - k.cpt.stack.push(num shr shiftLen) + ## 0x1c, Shift right logical + template shr256(top, lhs, num) = + let shiftLen = lhs.safeInt + if shiftLen >= 256: + top = 0.u256 + else: + # uint version of `shr` + top = num shr shiftLen + + k.cpt.stack.binaryWithTop(shr256) proc sarOp(k: var VmCtx): EvmResultVoid = - let - shiftLen = ? k.cpt.stack.popSafeInt() - num256 = ? k.cpt.stack.popInt() - num = cast[Int256](num256) + ## 0x1d, Shift right arithmetic + template sar256(top, lhs, num256) = + let + shiftLen = lhs.safeInt + num = cast[Int256](num256) - if shiftLen >= 256: - if num.isNegative: - k.cpt.stack.push(cast[UInt256]((-1).i256)) + if shiftLen >= 256: + if num.isNegative: + top = cast[UInt256]((-1).i256) + else: + top = 0.u256 else: - k.cpt.stack. push 0 - else: - # int version of `shr` then force the result - # into uint256 - k.cpt.stack.push(cast[UInt256](num shr shiftLen)) + # int version of `shr` then force the result + # into uint256 + top = cast[UInt256](num shr shiftLen) + + k.cpt.stack.binaryWithTop(sar256) # ------------------------------------------------------------------------------ # Public, op exec table entries diff --git a/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim b/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim index abbf05202..7dd0d31b1 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim @@ -19,6 +19,7 @@ import ../../computation, ../../stack, ../../evm_errors, + ../utils/utils_numeric, ../op_codes, ./oph_defs @@ -31,14 +32,13 @@ when not defined(evmc_enabled): proc blockhashOp (k: var VmCtx): EvmResultVoid = ## 0x40, Get the hash of one of the 256 most recent complete blocks. - let - cpt = k.cpt - blockNumber = ? cpt.stack.popInt() + template block256(top, number, conv) = + if number > high(BlockNumber).u256: + top = zero(UInt256) + else: + conv(k.cpt.getBlockHash(number.truncate(BlockNumber)), top) - if blockNumber > high(BlockNumber).u256: - cpt.stack.push Hash256() - else: - cpt.stack.push cpt.getBlockHash(blockNumber.truncate(BlockNumber)) + k.cpt.stack.unaryWithTop(block256) proc coinBaseOp (k: var VmCtx): EvmResultVoid = ## 0x41, Get the block's beneficiary address. @@ -75,14 +75,17 @@ proc baseFeeOp (k: var VmCtx): EvmResultVoid = proc blobHashOp (k: var VmCtx): EvmResultVoid = ## 0x49, Get current transaction's EIP-4844 versioned hash. - let - index = ? k.cpt.stack.popSafeInt() - len = k.cpt.getVersionedHashesLen + template blob256(top, number, conv) = + let + index = number.safeInt + len = k.cpt.getVersionedHashesLen - if index < len: - k.cpt.stack.push k.cpt.getVersionedHash(index) - else: - k.cpt.stack.push 0 + if index < len: + conv(k.cpt.getVersionedHash(index), top) + else: + top = zero(UInt256) + + k.cpt.stack.unaryWithTop(blob256) proc blobBaseFeeOp (k: var VmCtx): EvmResultVoid = ## 0x4a, Get the block's base fee. diff --git a/nimbus/evm/interpreter/op_handlers/oph_call.nim b/nimbus/evm/interpreter/op_handlers/oph_call.nim index 74666c7aa..a88066282 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_call.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_call.nim @@ -62,8 +62,8 @@ type gasCallEIP2929: GasInt -proc updateStackAndParams(q: var LocalParams; c: Computation): EvmResultVoid = - ? c.stack.push(0) +proc updateStackAndParams(q: var LocalParams; c: Computation) = + c.stack.lsTop(0) let 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 form of a constant `gasCall` q.gasCallEIP2929 = ColdAccountAccessCost - WarmStorageReadCost - ok() proc callParams(c: Computation): EvmResult[LocalParams] = ## Helper for callOp() + + ? c.stack.lsCheck(7) + var res = LocalParams( - gas : ? c.stack.popInt(), - codeAddress : ? c.stack.popAddress(), - value : ? c.stack.popInt(), - memInPos : ? c.stack.popMemRef(), - memInLen : ? c.stack.popMemRef(), - memOutPos : ? c.stack.popMemRef(), - memOutLen : ? c.stack.popMemRef(), + gas : c.stack.lsPeekInt(^1), + codeAddress : c.stack.lsPeekAddress(^2), + value : c.stack.lsPeekInt(^3), + memInPos : c.stack.lsPeekMemRef(^4), + memInLen : c.stack.lsPeekMemRef(^5), + memOutPos : c.stack.lsPeekMemRef(^6), + memOutLen : c.stack.lsPeekMemRef(^7), sender : c.msg.contractAddress, flags : c.msg.flags, ) + c.stack.lsShrink(6) res.contractAddress = res.codeAddress - ? res.updateStackAndParams(c) + res.updateStackAndParams(c) ok(res) @@ -122,38 +125,45 @@ proc callCodeParams(c: Computation): EvmResult[LocalParams] = proc delegateCallParams(c: Computation): EvmResult[LocalParams] = ## Helper for delegateCall() + + ? c.stack.lsCheck(6) var res = LocalParams( - gas : ? c.stack.popInt(), - codeAddress : ? c.stack.popAddress(), - memInPos : ? c.stack.popMemRef(), - memInLen : ? c.stack.popMemRef(), - memOutPos : ? c.stack.popMemRef(), - memOutLen : ? c.stack.popMemRef(), + gas : c.stack.lsPeekInt(^1), + codeAddress : c.stack.lsPeekAddress(^2), + memInPos : c.stack.lsPeekMemRef(^3), + memInLen : c.stack.lsPeekMemRef(^4), + memOutPos : c.stack.lsPeekMemRef(^5), + memOutLen : c.stack.lsPeekMemRef(^6), value : c.msg.value, sender : c.msg.sender, flags : c.msg.flags, contractAddress: c.msg.contractAddress, ) - ? res.updateStackAndParams(c) + + c.stack.lsShrink(5) + res.updateStackAndParams(c) ok(res) proc staticCallParams(c: Computation): EvmResult[LocalParams] = ## Helper for staticCall() + + ? c.stack.lsCheck(6) var res = LocalParams( - gas : ? c.stack.popInt(), - codeAddress : ? c.stack.popAddress(), - memInPos : ? c.stack.popMemRef(), - memInLen : ? c.stack.popMemRef(), - memOutPos : ? c.stack.popMemRef(), - memOutLen : ? c.stack.popMemRef(), + gas : c.stack.lsPeekInt(^1), + codeAddress : c.stack.lsPeekAddress(^2), + memInPos : c.stack.lsPeekMemRef(^3), + memInLen : c.stack.lsPeekMemRef(^4), + memOutPos : c.stack.lsPeekMemRef(^5), + memOutLen : c.stack.lsPeekMemRef(^6), value : 0.u256, sender : c.msg.contractAddress, flags : {EVMC_STATIC}, ) + c.stack.lsShrink(5) res.contractAddress = res.codeAddress - ? res.updateStackAndParams(c) + res.updateStackAndParams(c) ok(res) when evmc_enabled: @@ -170,7 +180,7 @@ when evmc_enabled: c.gasMeter.refundGas(c.res.gas_refund) if c.res.status_code == EVMC_SUCCESS: - ? c.stack.top(1) + c.stack.lsTop(1) if not c.res.release.isNil: c.res.release(c.res) @@ -191,7 +201,7 @@ else: if child.isSuccess: c.gasMeter.refundGas(child.gasMeter.gasRefunded) - ? c.stack.top(1) + c.stack.lsTop(1) c.returnData = child.output let actualOutputSize = min(memLen, child.output.len) diff --git a/nimbus/evm/interpreter/op_handlers/oph_create.nim b/nimbus/evm/interpreter/op_handlers/oph_create.nim index 671620ff7..1b1b188b2 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_create.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_create.nim @@ -52,7 +52,7 @@ when evmc_enabled: c.gasMeter.returnGas(GasInt c.res.gas_left) c.gasMeter.refundGas(c.res.gas_refund) 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: # From create, only use `outputData` if child returned with `REVERT`. assign(c.returnData, makeOpenArray(c.res.output_data, c.res.output_size.int)) @@ -75,7 +75,7 @@ else: if child.isSuccess: 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`. # From create, only use `outputData` if child returned with `REVERT`. c.returnData = child.output @@ -90,14 +90,16 @@ else: proc createOp(k: var VmCtx): EvmResultVoid = ## 0xf0, Create a new account with associated code ? checkInStaticContext(k.cpt) + ? k.cpt.stack.lsCheck(3) let cpt = k.cpt - endowment = ? cpt.stack.popInt() - memPos = ? cpt.stack.popSafeInt() - memLen = ? cpt.stack.peekSafeInt() + endowment = cpt.stack.lsPeekInt(^1) + memPos = cpt.stack.lsPeekSafeInt(^2) + memLen = cpt.stack.lsPeekSafeInt(^3) - ? cpt.stack.top(0) + cpt.stack.lsShrink(2) + cpt.stack.lsTop(0) # EIP-3860 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 = ## 0xf5, Behaves identically to CREATE, except using keccak256 ? checkInStaticContext(k.cpt) + ? k.cpt.stack.lsCheck(4) let cpt = k.cpt - endowment = ? cpt.stack.popInt() - memPos = ? cpt.stack.popSafeInt() - memLen = ? cpt.stack.popSafeInt() - salt256 = ? cpt.stack.peekInt() + endowment = cpt.stack.lsPeekInt(^1) + memPos = cpt.stack.lsPeekSafeInt(^2) + memLen = cpt.stack.lsPeekSafeInt(^3) + salt256 = cpt.stack.lsPeekInt(^4) salt = ContractSalt(bytes: salt256.toBytesBE) - ? cpt.stack.top(0) + cpt.stack.lsShrink(3) + cpt.stack.lsTop(0) # EIP-3860 if cpt.fork >= FkShanghai and memLen > EIP3860_MAX_INITCODE_SIZE: diff --git a/nimbus/evm/interpreter/op_handlers/oph_dup.nim b/nimbus/evm/interpreter/op_handlers/oph_dup.nim index 6c6431d9a..db3ac63ee 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_dup.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_dup.nim @@ -41,7 +41,7 @@ proc fnInfo(n: int): string {.compileTime.} = "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) const diff --git a/nimbus/evm/interpreter/op_handlers/oph_envinfo.nim b/nimbus/evm/interpreter/op_handlers/oph_envinfo.nim index 104afb6f6..dd75e0871 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_envinfo.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_envinfo.nim @@ -22,10 +22,10 @@ import ../../stack, ../gas_costs, ../op_codes, - ../utils/utils_numeric, ./oph_defs, ./oph_helpers, eth/common, + stew/assign2, stint when not defined(evmc_enabled): @@ -43,20 +43,17 @@ proc addressOp (k: var VmCtx): EvmResultVoid = proc balanceOp (k: var VmCtx): EvmResultVoid = ## 0x31, Get balance of the given account. - let - cpt = k.cpt - address = ? cpt.stack.popAddress - cpt.stack.push cpt.getBalance(address) + template balance256(address): auto = + k.cpt.getBalance(address) + k.cpt.stack.unaryAddress(balance256) proc balanceEIP2929Op (k: var VmCtx): EvmResultVoid = ## 0x31, EIP292: Get balance of the given account for Berlin and later - let - cpt = k.cpt - address = ? cpt.stack.popAddress() - gasCost = cpt.gasEip2929AccountCheck(address) - - ? cpt.opcodeGasCost(Balance, gasCost, reason = "Balance EIP2929") - cpt.stack.push cpt.getBalance(address) + template balanceEIP2929(address): auto = + let gasCost = k.cpt.gasEip2929AccountCheck(address) + ? k.cpt.opcodeGasCost(Balance, gasCost, reason = "Balance EIP2929") + k.cpt.getBalance(address) + k.cpt.stack.unaryAddress(balanceEIP2929) # ------------------ @@ -75,12 +72,12 @@ proc callValueOp (k: var VmCtx): EvmResultVoid = proc callDataLoadOp (k: var VmCtx): EvmResultVoid = ## 0x35, Get input data of current environment - let - startPos = ? k.cpt.stack.popInt() - start = startPos.cleanMemRef + ? k.cpt.stack.lsCheck(1) + let start = k.cpt.stack.lsPeekMemRef(^1) 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 let @@ -89,9 +86,9 @@ proc callDataLoadOp (k: var VmCtx): EvmResultVoid = # We rely on value being initialized with 0 by default var value: array[32, byte] - value[0 .. presentBytes] = k.cpt.msg.data.toOpenArray(start, endRange) - k.cpt.stack.push value - + assign(value.toOpenArray(0, presentBytes), k.cpt.msg.data.toOpenArray(start, endRange)) + k.cpt.stack.lsTop value + ok() proc callDataSizeOp (k: var VmCtx): EvmResultVoid = ## 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 = ## 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 - let (memPos, copyPos, len) = - (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef) - - ? k.cpt.opcodeGasCost(CallDataCopy, - k.cpt.gasCosts[CallDataCopy].m_handler(k.cpt.memory.len, memPos, len), + cpt.stack.lsShrink(3) + ? cpt.opcodeGasCost(CallDataCopy, + cpt.gasCosts[CallDataCopy].m_handler(cpt.memory.len, memPos, len), reason = "CallDataCopy fee") - k.cpt.memory.writePadded(k.cpt.msg.data, memPos, copyPos, len) + cpt.memory.writePadded(cpt.msg.data, memPos, copyPos, len) ok() @@ -122,14 +121,14 @@ proc codeSizeOp (k: var VmCtx): EvmResultVoid = proc codeCopyOp (k: var VmCtx): EvmResultVoid = ## 0x39, Copy code running in current environment to memory. + ? k.cpt.stack.lsCheck(3) let cpt = k.cpt - (memStartPos, copyStartPos, size) = ? cpt.stack.popInt(3) - - # TODO tests: https://github.com/status-im/nimbus/issues/67 - let (memPos, copyPos, len) = - (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef) + memPos = cpt.stack.lsPeekMemRef(^1) + copyPos = cpt.stack.lsPeekMemRef(^2) + len = cpt.stack.lsPeekMemRef(^3) + cpt.stack.lsShrink(3) ? cpt.opcodeGasCost(CodeCopy, cpt.gasCosts[CodeCopy].m_handler(cpt.memory.len, memPos, len), reason = "CodeCopy fee") @@ -145,33 +144,32 @@ proc gasPriceOp (k: var VmCtx): EvmResultVoid = proc extCodeSizeOp (k: var VmCtx): EvmResultVoid = ## 0x3b, Get size of an account's code - let - cpt = k.cpt - address = ? k.cpt.stack.popAddress() - - cpt.stack.push cpt.getCodeSize(address) + template ecs256(address): auto = + k.cpt.getCodeSize(address) + k.cpt.stack.unaryAddress(ecs256) proc extCodeSizeEIP2929Op (k: var VmCtx): EvmResultVoid = ## 0x3b, Get size of an account's code - let - cpt = k.cpt - address = ? cpt.stack.popAddress() - gasCost = cpt.gasEip2929AccountCheck(address) + template ecsEIP2929(address): auto = + let gasCost = k.cpt.gasEip2929AccountCheck(address) + ? k.cpt.opcodeGasCost(ExtCodeSize, gasCost, reason = "ExtCodeSize EIP2929") + k.cpt.getCodeSize(address) - ? cpt.opcodeGasCost(ExtCodeSize, gasCost, reason = "ExtCodeSize EIP2929") - cpt.stack.push cpt.getCodeSize(address) + k.cpt.stack.unaryAddress(ecsEIP2929) # ----------- proc extCodeCopyOp (k: var VmCtx): EvmResultVoid = ## 0x3c, Copy an account's code to memory. + ? k.cpt.stack.lsCheck(4) let cpt = k.cpt - address = ? cpt.stack.popAddress() - (memStartPos, codeStartPos, size) = ? cpt.stack.popInt(3) - (memPos, codePos, len) = - (memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef) + address = cpt.stack.lsPeekAddress(^1) + memPos = cpt.stack.lsPeekMemRef(^2) + codePos = cpt.stack.lsPeekMemRef(^3) + len = cpt.stack.lsPeekMemRef(^4) + cpt.stack.lsShrink(4) ? cpt.opcodeGasCost(ExtCodeCopy, cpt.gasCosts[ExtCodeCopy].m_handler(cpt.memory.len, memPos, len), reason = "ExtCodeCopy fee") @@ -183,15 +181,17 @@ proc extCodeCopyOp (k: var VmCtx): EvmResultVoid = proc extCodeCopyEIP2929Op (k: var VmCtx): EvmResultVoid = ## 0x3c, Copy an account's code to memory. + ? k.cpt.stack.lsCheck(4) let cpt = k.cpt - address = ? cpt.stack.popAddress() - (memStartPos, codeStartPos, size) = ? cpt.stack.popInt(3) - (memPos, codePos, len) = (memStartPos.cleanMemRef, - codeStartPos.cleanMemRef, size.cleanMemRef) - + address = cpt.stack.lsPeekAddress(^1) + memPos = cpt.stack.lsPeekMemRef(^2) + codePos = cpt.stack.lsPeekMemRef(^3) + len = cpt.stack.lsPeekMemRef(^4) 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") let code = cpt.getCode(address) @@ -208,39 +208,38 @@ proc returnDataSizeOp (k: var VmCtx): EvmResultVoid = proc returnDataCopyOp (k: var VmCtx): EvmResultVoid = ## 0x3e, Copy output data from the previous call to memory. + ? k.cpt.stack.lsCheck(3) let - (memStartPos, copyStartPos, size) = ? k.cpt.stack.popInt(3) - (memPos, copyPos, len) = - (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef) - gasCost = k.cpt.gasCosts[ReturnDataCopy].m_handler( - k.cpt.memory.len, memPos, len) + cpt = k.cpt + memPos = cpt.stack.lsPeekMemRef(^1) + copyPos = cpt.stack.lsPeekMemRef(^2) + len = cpt.stack.lsPeekMemRef(^3) + 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)) - k.cpt.memory.writePadded(k.cpt.returnData, memPos, copyPos, len) + cpt.memory.writePadded(cpt.returnData, memPos, copyPos, len) ok() # --------------- proc extCodeHashOp (k: var VmCtx): EvmResultVoid = ## 0x3f, Returns the keccak256 hash of a contract’s code - let - cpt = k.cpt - address = ? k.cpt.stack.popAddress() - - cpt.stack.push cpt.getCodeHash(address) + template ech256(address): auto = + k.cpt.getCodeHash(address) + k.cpt.stack.unaryAddress(ech256) proc extCodeHashEIP2929Op (k: var VmCtx): EvmResultVoid = ## 0x3f, EIP2929: Returns the keccak256 hash of a contract’s code - let - cpt = k.cpt - address = ? k.cpt.stack.popAddress() - gasCost = cpt.gasEip2929AccountCheck(address) - - ? cpt.opcodeGasCost(ExtCodeHash, gasCost, reason = "ExtCodeHash EIP2929") - cpt.stack.push cpt.getCodeHash(address) + template echEIP2929(address): auto = + let gasCost = k.cpt.gasEip2929AccountCheck(address) + ? k.cpt.opcodeGasCost(ExtCodeHash, gasCost, reason = "ExtCodeHash EIP2929") + k.cpt.getCodeHash(address) + k.cpt.stack.unaryAddress(echEIP2929) # ------------------------------------------------------------------------------ # Public, op exec table entries diff --git a/nimbus/evm/interpreter/op_handlers/oph_hash.nim b/nimbus/evm/interpreter/op_handlers/oph_hash.nim index beaefabdd..4fa461198 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_hash.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_hash.nim @@ -22,7 +22,6 @@ import ../../stack, ../gas_costs, ../op_codes, - ../utils/utils_numeric, ./oph_defs, eth/common @@ -32,24 +31,28 @@ import proc sha3Op(k: var VmCtx): EvmResultVoid = ## 0x20, Compute Keccak-256 hash. + ? k.cpt.stack.lsCheck(2) let - (startPos, length) = ? k.cpt.stack.popInt(2) - (pos, len) = (startPos.safeInt, length.safeInt) + cpt = k.cpt + pos = cpt.stack.lsPeekSafeInt(^1) + len = cpt.stack.lsPeekSafeInt(^2) + cpt.stack.lsShrink(1) if pos < 0 or len < 0 or pos > 2147483648'i64: return err(opErr(OutOfBounds)) - ? k.cpt.opcodeGasCost(Op.Sha3, - k.cpt.gasCosts[Op.Sha3].m_handler(k.cpt.memory.len, pos, len), - reason = "SHA3: word gas cost") + ? cpt.opcodeGasCost(Op.Sha3, + cpt.gasCosts[Op.Sha3].m_handler(cpt.memory.len, pos, len), + 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 - if endRange == -1 or pos >= k.cpt.memory.len: - k.cpt.stack.push(EMPTY_SHA3) + let endRange = min(pos + len, cpt.memory.len) - 1 + if endRange == -1 or pos >= cpt.memory.len: + cpt.stack.lsTop(EMPTY_SHA3) 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 diff --git a/nimbus/evm/interpreter/op_handlers/oph_log.nim b/nimbus/evm/interpreter/op_handlers/oph_log.nim index bb7bd43a1..aa7a84b36 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_log.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_log.nim @@ -24,7 +24,6 @@ import ../../types, ../gas_costs, ../op_codes, - ../utils/utils_numeric, ./oph_defs, ./oph_gen_handlers, ./oph_helpers, @@ -52,8 +51,11 @@ proc fnInfo(n: int): string {.compileTime.} = proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid = static: doAssert(topicCount in 0 .. 4) ? checkInStaticContext(c) - let (memStartPosition, size) = ? c.stack.popInt(2) - let (memPos, len) = (memStartPosition.cleanMemRef, size.cleanMemRef) + const stackSize = 2 + topicCount + ? c.stack.lsCheck(stackSize) + let + memPos = c.stack.lsPeekMemRef(^1) + len = c.stack.lsPeekMemRef(^2) if memPos < 0 or len < 0: return err(opErr(OutOfBounds)) @@ -66,7 +68,7 @@ proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid when evmc_enabled: var topics: array[4, evmc_bytes32] 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.memory.read(memPos, len), @@ -75,13 +77,13 @@ proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid var log: Log log.topics = newSeqOfCap[Topic](topicCount) for i in 0 ..< topicCount: - let topic = ? c.stack.popTopic() - log.topics.add topic + log.topics.add c.stack.lsPeekTopic(^(i+3)) assign(log.data, c.memory.read(memPos, len)) log.address = c.msg.contractAddress c.addLogEntry(log) + c.stack.lsShrink(stackSize) ok() const diff --git a/nimbus/evm/interpreter/op_handlers/oph_memory.nim b/nimbus/evm/interpreter/op_handlers/oph_memory.nim index 531eac770..4d247c21b 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_memory.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_memory.nim @@ -24,7 +24,6 @@ import ../gas_meter, ../gas_costs, ../op_codes, - ../utils/utils_numeric, ./oph_defs, ./oph_helpers, eth/common, @@ -129,68 +128,81 @@ proc popOp(k: var VmCtx): EvmResultVoid = proc mloadOp (k: var VmCtx): EvmResultVoid = ## 0x51, Load word from memory - let memStartPos = ? k.cpt.stack.popInt() - let memPos = memStartPos.cleanMemRef - ? k.cpt.opcodeGasCost(Mload, - k.cpt.gasCosts[Mload].m_handler(k.cpt.memory.len, memPos, 32), + ? k.cpt.stack.lsCheck(1) + let + 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") - k.cpt.memory.extend(memPos, 32) - k.cpt.stack.push k.cpt.memory.read32Bytes(memPos) + cpt.memory.extend(memPos, 32) + cpt.stack.lsTop cpt.memory.read32Bytes(memPos) + ok() proc mstoreOp (k: var VmCtx): EvmResultVoid = ## 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 - ? k.cpt.opcodeGasCost(Mstore, - k.cpt.gasCosts[Mstore].m_handler(k.cpt.memory.len, memPos, 32), + ? cpt.opcodeGasCost(Mstore, + cpt.gasCosts[Mstore].m_handler(cpt.memory.len, memPos, 32), reason = "MSTORE: GasVeryLow + memory expansion") - k.cpt.memory.extend(memPos, 32) - k.cpt.memory.write(memPos, value.toBytesBE) + cpt.memory.extend(memPos, 32) + cpt.memory.write(memPos, value.toBytesBE) proc mstore8Op (k: var VmCtx): EvmResultVoid = ## 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 - ? k.cpt.opcodeGasCost(Mstore8, - k.cpt.gasCosts[Mstore8].m_handler(k.cpt.memory.len, memPos, 1), + ? cpt.opcodeGasCost(Mstore8, + cpt.gasCosts[Mstore8].m_handler(cpt.memory.len, memPos, 1), reason = "MSTORE8: GasVeryLow + memory expansion") - k.cpt.memory.extend(memPos, 1) - k.cpt.memory.write(memPos, value.toByteArrayBE[31]) + cpt.memory.extend(memPos, 1) + cpt.memory.write(memPos, value.toByteArrayBE[31]) # ------- proc sloadOp (k: var VmCtx): EvmResultVoid = ## 0x54, Load word from storage. - let - cpt = k.cpt - slot = ? cpt.stack.popInt() - cpt.stack.push cpt.getStorage(slot) + template sload256(top, slot, conv) = + top = k.cpt.getStorage(slot) + k.cpt.stack.unaryWithTop(sload256) proc sloadEIP2929Op (k: var VmCtx): EvmResultVoid = ## 0x54, EIP2929: Load word from storage for Berlin and later - let - cpt = k.cpt - slot = ? cpt.stack.popInt() - gasCost = cpt.gasEip2929AccountCheck(cpt.msg.contractAddress, slot) - ? cpt.opcodeGasCost(Sload, gasCost, reason = "sloadEIP2929") - cpt.stack.push cpt.getStorage(slot) + template sloadEIP2929(top, slot, conv) = + let gasCost = k.cpt.gasEip2929AccountCheck(k.cpt.msg.contractAddress, slot) + ? k.cpt.opcodeGasCost(Sload, gasCost, reason = "sloadEIP2929") + top = k.cpt.getStorage(slot) + k.cpt.stack.unaryWithTop(sloadEIP2929) # ------- proc sstoreOp (k: var VmCtx): EvmResultVoid = ## 0x55, Save word to storage. + ? k.cpt.stack.lsCheck(2) let 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) sstoreEvmcOrSstore(cpt, slot, newValue) @@ -198,9 +210,12 @@ proc sstoreOp (k: var VmCtx): EvmResultVoid = proc sstoreEIP1283Op (k: var VmCtx): EvmResultVoid = ## 0x55, EIP1283: sstore for Constantinople and later + ? k.cpt.stack.lsCheck(2) let 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) sstoreEvmcOrNetGasMetering(cpt, slot, newValue) @@ -208,9 +223,12 @@ proc sstoreEIP1283Op (k: var VmCtx): EvmResultVoid = proc sstoreEIP2200Op (k: var VmCtx): EvmResultVoid = ## 0x55, EIP2200: sstore for Istanbul and later + ? k.cpt.stack.lsCheck(2) let 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) const SentryGasEIP2200 = 2300 @@ -223,9 +241,12 @@ proc sstoreEIP2200Op (k: var VmCtx): EvmResultVoid = proc sstoreEIP2929Op (k: var VmCtx): EvmResultVoid = ## 0x55, EIP2929: sstore for Berlin and later + ? k.cpt.stack.lsCheck(2) let 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) @@ -257,7 +278,12 @@ proc jumpOp (k: var VmCtx): EvmResultVoid = proc jumpIOp (k: var VmCtx): EvmResultVoid = ## 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: return ok() jumpImpl(k.cpt, jumpTarget) @@ -283,33 +309,43 @@ proc jumpDestOp (k: var VmCtx): EvmResultVoid = proc tloadOp (k: var VmCtx): EvmResultVoid = ## 0x5c, Load word from transient storage. + ? k.cpt.stack.lsCheck(1) let - slot = ? k.cpt.stack.popInt() - val = k.cpt.getTransientStorage(slot) - k.cpt.stack.push val + cpt = k.cpt + slot = cpt.stack.lsPeekInt(^1) + val = cpt.getTransientStorage(slot) + cpt.stack.lsTop val + ok() proc tstoreOp (k: var VmCtx): EvmResultVoid = ## 0x5d, Save word to transient storage. ? checkInStaticContext(k.cpt) + ? k.cpt.stack.lsCheck(2) let - slot = ? k.cpt.stack.popInt() - val = ? k.cpt.stack.popInt() - k.cpt.setTransientStorage(slot, val) + cpt = k.cpt + slot = cpt.stack.lsPeekInt(^1) + val = cpt.stack.lsPeekInt(^2) + cpt.stack.lsShrink(2) + + cpt.setTransientStorage(slot, val) ok() proc mCopyOp (k: var VmCtx): EvmResultVoid = ## 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) = - (dst.cleanMemRef, src.cleanMemRef, size.cleanMemRef) - - ? k.cpt.opcodeGasCost(Mcopy, - k.cpt.gasCosts[Mcopy].m_handler(k.cpt.memory.len, max(dstPos, srcPos), len), + ? cpt.opcodeGasCost(Mcopy, + cpt.gasCosts[Mcopy].m_handler(cpt.memory.len, max(dstPos, srcPos), len), reason = "Mcopy fee") - k.cpt.memory.copy(dstPos, srcPos, len) + cpt.memory.copy(dstPos, srcPos, len) ok() # ------------------------------------------------------------------------------ diff --git a/nimbus/evm/interpreter/op_handlers/oph_push.nim b/nimbus/evm/interpreter/op_handlers/oph_push.nim index 05acb400b..e20e8185b 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_push.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_push.nim @@ -40,7 +40,7 @@ proc fnInfo(n: int): string {.compileTime.} = "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) const diff --git a/nimbus/evm/interpreter/op_handlers/oph_swap.nim b/nimbus/evm/interpreter/op_handlers/oph_swap.nim index 5c81f9d07..349d048b5 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_swap.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_swap.nim @@ -41,7 +41,7 @@ proc fnInfo(n: int): string {.compileTime.} = "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) const diff --git a/nimbus/evm/interpreter/op_handlers/oph_sysops.nim b/nimbus/evm/interpreter/op_handlers/oph_sysops.nim index f08e3e36a..6d8e8f958 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_sysops.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_sysops.nim @@ -23,7 +23,6 @@ import ../../types, ../gas_costs, ../op_codes, - ../utils/utils_numeric, ./oph_defs, ./oph_helpers, eth/common, @@ -40,31 +39,40 @@ when not defined(evmc_enabled): proc returnOp(k: var VmCtx): EvmResultVoid = ## 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) - ? k.cpt.opcodeGasCost(Return, - k.cpt.gasCosts[Return].m_handler(k.cpt.memory.len, pos, len), + ? cpt.opcodeGasCost(Return, + cpt.gasCosts[Return].m_handler(cpt.memory.len, pos, len), 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() proc revertOp(k: var VmCtx): EvmResultVoid = ## 0xfd, Halt execution reverting state changes but returning data ## 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) - ? k.cpt.opcodeGasCost(Revert, - k.cpt.gasCosts[Revert].m_handler(k.cpt.memory.len, pos, len), + ? cpt.opcodeGasCost(Revert, + cpt.gasCosts[Revert].m_handler(cpt.memory.len, pos, len), reason = "REVERT") - 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)) # 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() proc invalidOp(k: var VmCtx): EvmResultVoid = @@ -74,8 +82,10 @@ proc invalidOp(k: var VmCtx): EvmResultVoid = proc selfDestructOp(k: var VmCtx): EvmResultVoid = ## 0xff, Halt execution and register account for later deletion. - let cpt = k.cpt - let beneficiary = ? cpt.stack.popAddress() + let + cpt = k.cpt + beneficiary = ? cpt.stack.popAddress() + when defined(evmc_enabled): block: cpt.selfDestruct(beneficiary) diff --git a/nimbus/evm/stack.nim b/nimbus/evm/stack.nim index aed36dec8..8fe0d8944 100644 --- a/nimbus/evm/stack.nim +++ b/nimbus/evm/stack.nim @@ -164,7 +164,7 @@ func `[]`*(stack: EvmStack, i: BackwardsIndex, T: typedesc): EvmResult[T] = func peekInt*(stack: EvmStack): EvmResult[UInt256] = ? ensurePop(stack, 1) - ok(fromStackElem(stack.values[^1], Uint256)) + ok(fromStackElem(stack.values[^1], UInt256)) func peekAddress*(stack: EvmStack): EvmResult[EthAddress] = ? ensurePop(stack, 1) @@ -184,3 +184,74 @@ iterator items*(stack: EvmStack): UInt256 = iterator pairs*(stack: EvmStack): (int, UInt256) = for i, v in stack.values: yield (i, v) + +# ------------------------------------------------------------------------------ +# Public functions with less safety +# ------------------------------------------------------------------------------ + +template lsCheck*(stack: EvmStack, expected: int): EvmResultVoid = + ensurePop(stack, expected) + +func lsTop*(stack: EvmStack, + value: EvmStackInts | UInt256 | EthAddress | Hash256) = + toStackElem(value, stack.values[^1]) + +func lsTop*(stack: var EvmStack, value: openArray[byte]) = + toStackElem(value, stack.values[^1]) + +func lsPeekInt*(stack: EvmStack, i: BackwardsIndex): UInt256 = + fromStackElem(stack.values[i], UInt256) + +func lsPeekAddress*(stack: EvmStack, i: BackwardsIndex): EthAddress = + fromStackElem(stack.values[i], EthAddress) + +func lsPeekMemRef*(stack: EvmStack, i: BackwardsIndex): int = + fromStackElem(stack.values[i], UInt256).cleanMemRef + +func lsPeekSafeInt*(stack: EvmStack, i: BackwardsIndex): int = + fromStackElem(stack.values[i], UInt256).safeInt + +func lsPeekTopic*(stack: EvmStack, i: BackwardsIndex): EvmStackBytes32 = + fromStackElem(stack.values[i], EvmStackBytes32) + +func lsShrink*(stack: EvmStack, x: int) = + stack.values.setLen(stack.values.len - x) + +template binaryOp*(stack: EvmStack, binOp): EvmResultVoid = + if stack.values.len >= 2: + stack.values[^2] = binOp(stack.values[^1], stack.values[^2]) + stack.values.setLen(stack.values.len - 1) + EvmResultVoid.ok() + else: + EvmResultVoid.err(stackErr(StackInsufficient)) + +template unaryOp*(stack: EvmStack, unOp): EvmResultVoid = + if stack.values.len >= 1: + stack.values[^1] = unOp(stack.values[^1]) + EvmResultVoid.ok() + else: + EvmResultVoid.err(stackErr(StackInsufficient)) + +template binaryWithTop*(stack: EvmStack, binOp): EvmResultVoid = + if stack.values.len >= 2: + binOp(stack.values[^2], stack.values[^1], stack.values[^2]) + stack.values.setLen(stack.values.len - 1) + EvmResultVoid.ok() + else: + EvmResultVoid.err(stackErr(StackInsufficient)) + +template unaryWithTop*(stack: EvmStack, unOp): EvmResultVoid = + if stack.values.len >= 1: + unOp(stack.values[^1], stack.values[^1], toStackElem) + EvmResultVoid.ok() + else: + EvmResultVoid.err(stackErr(StackInsufficient)) + +template unaryAddress*(stack: EvmStack, unOp): EvmResultVoid = + if stack.values.len >= 1: + let address = fromStackElem(stack.values[^1], EthAddress) + toStackElem(unOp(address), stack.values[^1]) + EvmResultVoid.ok() + else: + EvmResultVoid.err(stackErr(StackInsufficient)) + \ No newline at end of file diff --git a/tests/test_op_env.nim b/tests/test_op_env.nim index 8bea4f30b..b6d139026 100644 --- a/tests/test_op_env.nim +++ b/tests/test_op_env.nim @@ -30,6 +30,7 @@ proc opEnvMain*() = fork: London success: false memory: "0x0000000000000000000000000000000000000000000000000000000000000000" + stack: "0x00" assembler: # CodeCopy OP title: "CODECOPY_1" @@ -310,6 +311,7 @@ proc opEnvMain*() = stack: "0x5E" "0x07" + "0x471FD3AD3E9EEADEEC4608B92D16CE6B500704CC" assembler: # CODESIZE OP title: "CODESIZE_1"