mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-12 05:14:14 +00:00
re-integrated call op handlers
This commit is contained in:
parent
1bdbfda37f
commit
4ac32d360b
@ -22,7 +22,7 @@ import
|
|||||||
./op_handlers/[oph_defs,
|
./op_handlers/[oph_defs,
|
||||||
oph_arithmetic, oph_hash, oph_envinfo, oph_blockdata,
|
oph_arithmetic, oph_hash, oph_envinfo, oph_blockdata,
|
||||||
oph_memory, oph_push, oph_dup, oph_swap, oph_log,
|
oph_memory, oph_push, oph_dup, oph_swap, oph_log,
|
||||||
oph_create, oph_sysops]
|
oph_create, oph_call, oph_sysops]
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Helper
|
# Helper
|
||||||
@ -52,7 +52,7 @@ proc mkOpTable(select: Fork): array[Op,Vm2OpExec] {.compileTime.} =
|
|||||||
result.importList(select, vm2OpExecSwap, "Swap")
|
result.importList(select, vm2OpExecSwap, "Swap")
|
||||||
result.importList(select, vm2OpExecLog, "Log")
|
result.importList(select, vm2OpExecLog, "Log")
|
||||||
result.importList(select, vm2OpExecCreate, "Create")
|
result.importList(select, vm2OpExecCreate, "Create")
|
||||||
#result.importList(select, vm2OpExecCall, "Call")
|
result.importList(select, vm2OpExecCall, "Call")
|
||||||
result.importList(select, vm2OpExecSysOp, "SysOp")
|
result.importList(select, vm2OpExecSysOp, "SysOp")
|
||||||
|
|
||||||
for op in Op:
|
for op in Op:
|
||||||
|
625
nimbus/vm2/interpreter/op_handlers/oph_call.nim
Normal file
625
nimbus/vm2/interpreter/op_handlers/oph_call.nim
Normal file
@ -0,0 +1,625 @@
|
|||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
# Licensed under either of
|
||||||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||||
|
# http://opensource.org/licenses/MIT)
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except
|
||||||
|
# according to those terms.
|
||||||
|
|
||||||
|
## EVM Opcode Handlers: Call Operations
|
||||||
|
## ====================================
|
||||||
|
##
|
||||||
|
|
||||||
|
const
|
||||||
|
kludge {.intdefine.}: int = 0
|
||||||
|
breakCircularDependency {.used.} = kludge > 0
|
||||||
|
|
||||||
|
import
|
||||||
|
../../../errors,
|
||||||
|
./oph_defs,
|
||||||
|
chronicles,
|
||||||
|
stint
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Kludge BEGIN
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
when not breakCircularDependency:
|
||||||
|
import
|
||||||
|
../../../db/accounts_cache,
|
||||||
|
../../../constants,
|
||||||
|
../../stack,
|
||||||
|
../../v2computation,
|
||||||
|
../../v2memory,
|
||||||
|
../../v2state,
|
||||||
|
../../v2types,
|
||||||
|
../gas_meter,
|
||||||
|
../utils/v2utils_numeric,
|
||||||
|
../v2gas_costs,
|
||||||
|
eth/common
|
||||||
|
|
||||||
|
else:
|
||||||
|
import macros
|
||||||
|
|
||||||
|
type
|
||||||
|
MsgFlags = int
|
||||||
|
GasResult = tuple[gasCost, gasRefund: GasInt]
|
||||||
|
const
|
||||||
|
evmcCall = 42
|
||||||
|
evmcDelegateCall = 43
|
||||||
|
evmcCallCode = 44
|
||||||
|
emvcStatic = 45
|
||||||
|
MaxCallDepth = 46
|
||||||
|
ColdAccountAccessCost = 47
|
||||||
|
WarmStorageReadCost = 48
|
||||||
|
|
||||||
|
# function stubs from stack.nim (to satisfy compiler logic)
|
||||||
|
proc `[]`(x: Stack, i: BackwardsIndex; T: typedesc): T = result
|
||||||
|
proc top[T](x: Stack, value: T) = discard
|
||||||
|
proc push[T](x: Stack; n: T) = discard
|
||||||
|
proc popAddress(x: var Stack): EthAddress = result
|
||||||
|
proc popInt(x: var Stack): UInt256 = result
|
||||||
|
|
||||||
|
# function stubs from v2computation.nim (to satisfy compiler logic)
|
||||||
|
proc gasCosts(c: Computation): array[Op,int] = result
|
||||||
|
proc getBalance[T](c: Computation, address: T): Uint256 = result
|
||||||
|
proc newComputation[A,B](v:A, m:B, salt = 0.u256): Computation = new result
|
||||||
|
func shouldBurnGas(c: Computation): bool = result
|
||||||
|
proc accountExists(c: Computation, address: EthAddress): bool = result
|
||||||
|
proc isSuccess(c: Computation): bool = result
|
||||||
|
proc merge(c, child: Computation) = discard
|
||||||
|
template chainTo(c, d: Computation, e: untyped) =
|
||||||
|
c.child = d; c.continuation = proc() = e
|
||||||
|
|
||||||
|
# function stubs from v2utils_numeric.nim
|
||||||
|
func calcMemSize*(offset, length: int): int = result
|
||||||
|
|
||||||
|
# function stubs from v2memory.nim
|
||||||
|
proc len(mem: Memory): int = result
|
||||||
|
proc extend(mem: var Memory; startPos: Natural; size: Natural) = discard
|
||||||
|
proc read(mem: var Memory, startPos: Natural, size: Natural): seq[byte] = @[]
|
||||||
|
proc write(mem: var Memory, startPos: Natural, val: openarray[byte]) = discard
|
||||||
|
|
||||||
|
# function stubs from v2state.nim
|
||||||
|
template mutateStateDB(vmState: BaseVMState, body: untyped) =
|
||||||
|
block:
|
||||||
|
var db {.inject.} = vmState.accountDb
|
||||||
|
body
|
||||||
|
|
||||||
|
# function stubs from gas_meter.nim
|
||||||
|
proc consumeGas(gasMeter: var GasMeter; amount: int; reason: string) = discard
|
||||||
|
proc returnGas(gasMeter: var GasMeter; amount: GasInt) = discard
|
||||||
|
|
||||||
|
# function stubs from v2utils_numeric.nim
|
||||||
|
func cleanMemRef(x: UInt256): int = result
|
||||||
|
|
||||||
|
# stubs from v2gas_costs.nim
|
||||||
|
type GasParams = object
|
||||||
|
case kind*: Op
|
||||||
|
of Call, CallCode, DelegateCall, StaticCall:
|
||||||
|
c_isNewAccount: bool
|
||||||
|
c_contractGas: Uint256
|
||||||
|
c_gasBalance, c_currentMemSize, c_memOffset, c_memLength: int64
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
proc c_handler(x: int; y: Uint256, z: GasParams): GasResult = result
|
||||||
|
|
||||||
|
# function stubs from accounts_cache.nim:
|
||||||
|
func inAccessList[A,B](ac: A; address: B): bool = result
|
||||||
|
proc accessList[A,B](ac: var A; address: B) = discard
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Kludge END
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc callParams(c: Computation): (UInt256, UInt256, EthAddress,
|
||||||
|
EthAddress, int, int, int,
|
||||||
|
int, MsgFlags) =
|
||||||
|
let gas = c.stack.popInt()
|
||||||
|
let destination = c.stack.popAddress()
|
||||||
|
let value = c.stack.popInt()
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
value,
|
||||||
|
destination,
|
||||||
|
c.msg.contractAddress, # sender
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.msg.flags)
|
||||||
|
|
||||||
|
c.stack.push(0)
|
||||||
|
|
||||||
|
|
||||||
|
proc callCodeParams(c: Computation): (UInt256, UInt256, EthAddress,
|
||||||
|
EthAddress, int, int, int,
|
||||||
|
int, MsgFlags) =
|
||||||
|
let gas = c.stack.popInt()
|
||||||
|
let destination = c.stack.popAddress()
|
||||||
|
let value = c.stack.popInt()
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
value,
|
||||||
|
destination,
|
||||||
|
c.msg.contractAddress, # sender
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.msg.flags)
|
||||||
|
|
||||||
|
c.stack.push(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc delegateCallParams(c: Computation): (UInt256, UInt256, EthAddress,
|
||||||
|
EthAddress, int, int, int,
|
||||||
|
int, MsgFlags) =
|
||||||
|
let gas = c.stack.popInt()
|
||||||
|
let destination = c.stack.popAddress()
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
c.msg.value, # value
|
||||||
|
destination,
|
||||||
|
c.msg.sender, # sender
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.msg.flags)
|
||||||
|
|
||||||
|
c.stack.push(0)
|
||||||
|
|
||||||
|
|
||||||
|
proc staticCallParams(c: Computation): (UInt256, UInt256, EthAddress,
|
||||||
|
EthAddress, int, int, int,
|
||||||
|
int, MsgFlags) =
|
||||||
|
let gas = c.stack.popInt()
|
||||||
|
let destination = c.stack.popAddress()
|
||||||
|
|
||||||
|
result = (gas,
|
||||||
|
0.u256, # value
|
||||||
|
destination,
|
||||||
|
c.msg.contractAddress, # sender
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
c.stack.popInt().cleanMemRef,
|
||||||
|
emvcStatic) # is_static
|
||||||
|
|
||||||
|
c.stack.push(0)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private, op handlers implementation
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const
|
||||||
|
callOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
||||||
|
## 0xf1, Message-Call into an account
|
||||||
|
if emvcStatic == k.cpt.msg.flags and k.cpt.stack[^3, UInt256] > 0.u256:
|
||||||
|
raise newException(
|
||||||
|
StaticContextError,
|
||||||
|
"Cannot modify state while inside of a STATICCALL context")
|
||||||
|
|
||||||
|
let (gas, value, destination, sender, memInPos, memInLen, memOutPos,
|
||||||
|
memOutLen, flags) = callParams(k.cpt)
|
||||||
|
|
||||||
|
let (memOffset, memLength) =
|
||||||
|
if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
|
||||||
|
(memInPos, memInLen)
|
||||||
|
else:
|
||||||
|
(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
# EIP2929
|
||||||
|
# This came before old gas calculator
|
||||||
|
# because it will affect `k.cpt.gasMeter.gasRemaining`
|
||||||
|
# and further `childGasLimit`
|
||||||
|
if k.cpt.fork >= FkBerlin:
|
||||||
|
k.cpt.vmState.mutateStateDB:
|
||||||
|
if not db.inAccessList(destination):
|
||||||
|
db.accessList(destination)
|
||||||
|
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
||||||
|
# the form of a constant `gasCall`
|
||||||
|
k.cpt.gasMeter.consumeGas(
|
||||||
|
ColdAccountAccessCost - WarmStorageReadCost,
|
||||||
|
reason = "EIP2929 gasCall")
|
||||||
|
|
||||||
|
let contractAddress = destination
|
||||||
|
|
||||||
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[Call].c_handler(
|
||||||
|
value,
|
||||||
|
GasParams(
|
||||||
|
kind: Call,
|
||||||
|
c_isNewAccount: not k.cpt.accountExists(contractAddress),
|
||||||
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
||||||
|
c_contractGas: gas,
|
||||||
|
c_currentMemSize: k.cpt.memory.len,
|
||||||
|
c_memOffset: memOffset,
|
||||||
|
c_memLength: memLength))
|
||||||
|
|
||||||
|
# EIP 2046: temporary disabled
|
||||||
|
# reduce gas fee for precompiles
|
||||||
|
# from 700 to 40
|
||||||
|
if gasCost >= 0:
|
||||||
|
k.cpt.gasMeter.consumeGas(gasCost, reason = $Call)
|
||||||
|
|
||||||
|
k.cpt.returnData.setLen(0)
|
||||||
|
|
||||||
|
if k.cpt.msg.depth >= MaxCallDepth:
|
||||||
|
debug "Computation Failure",
|
||||||
|
reason = "Stack too deep",
|
||||||
|
maximumDepth = MaxCallDepth,
|
||||||
|
depth = k.cpt.msg.depth
|
||||||
|
k.cpt.gasMeter.returnGas(childGasLimit)
|
||||||
|
return
|
||||||
|
|
||||||
|
if gasCost < 0 and childGasLimit <= 0:
|
||||||
|
raise newException(
|
||||||
|
OutOfGas, "Gas not enough to perform calculation (call)")
|
||||||
|
|
||||||
|
k.cpt.memory.extend(memInPos, memInLen)
|
||||||
|
k.cpt.memory.extend(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
let senderBalance = k.cpt.getBalance(sender)
|
||||||
|
if senderBalance < value:
|
||||||
|
debug "Insufficient funds",
|
||||||
|
available = senderBalance,
|
||||||
|
needed = k.cpt.msg.value
|
||||||
|
k.cpt.gasMeter.returnGas(childGasLimit)
|
||||||
|
return
|
||||||
|
|
||||||
|
let msg = Message(
|
||||||
|
kind: evmcCall,
|
||||||
|
depth: k.cpt.msg.depth + 1,
|
||||||
|
gas: childGasLimit,
|
||||||
|
sender: sender,
|
||||||
|
contractAddress: contractAddress,
|
||||||
|
codeAddress: destination,
|
||||||
|
value: value,
|
||||||
|
data: k.cpt.memory.read(memInPos, memInLen),
|
||||||
|
flags: flags)
|
||||||
|
|
||||||
|
var child = newComputation(k.cpt.vmState, msg)
|
||||||
|
k.cpt.chainTo(child):
|
||||||
|
if not child.shouldBurnGas:
|
||||||
|
k.cpt.gasMeter.returnGas(child.gasMeter.gasRemaining)
|
||||||
|
|
||||||
|
if child.isSuccess:
|
||||||
|
k.cpt.merge(child)
|
||||||
|
k.cpt.stack.top(1)
|
||||||
|
|
||||||
|
k.cpt.returnData = child.output
|
||||||
|
let actualOutputSize = min(memOutLen, child.output.len)
|
||||||
|
if actualOutputSize > 0:
|
||||||
|
k.cpt.memory.write(memOutPos,
|
||||||
|
child.output.toOpenArray(0, actualOutputSize - 1))
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
callCodeOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
||||||
|
## 0xf2, Message-call into this account with an alternative account's code.
|
||||||
|
let (gas, value, destination, sender, memInPos, memInLen, memOutPos,
|
||||||
|
memOutLen, flags) = callCodeParams(k.cpt)
|
||||||
|
|
||||||
|
let (memOffset, memLength) =
|
||||||
|
if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
|
||||||
|
(memInPos, memInLen)
|
||||||
|
else:
|
||||||
|
(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
# EIP2929
|
||||||
|
# This came before old gas calculator
|
||||||
|
# because it will affect `k.cpt.gasMeter.gasRemaining`
|
||||||
|
# and further `childGasLimit`
|
||||||
|
if k.cpt.fork >= FkBerlin:
|
||||||
|
k.cpt.vmState.mutateStateDB:
|
||||||
|
if not db.inAccessList(destination):
|
||||||
|
db.accessList(destination)
|
||||||
|
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
||||||
|
# the form of a constant `gasCall`
|
||||||
|
k.cpt.gasMeter.consumeGas(
|
||||||
|
ColdAccountAccessCost - WarmStorageReadCost,
|
||||||
|
reason = "EIP2929 gasCall")
|
||||||
|
|
||||||
|
let contractAddress = k.cpt.msg.contractAddress
|
||||||
|
|
||||||
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[CallCode].c_handler(
|
||||||
|
value,
|
||||||
|
GasParams(
|
||||||
|
kind: CallCode,
|
||||||
|
c_isNewAccount: not k.cpt.accountExists(contractAddress),
|
||||||
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
||||||
|
c_contractGas: gas,
|
||||||
|
c_currentMemSize: k.cpt.memory.len,
|
||||||
|
c_memOffset: memOffset,
|
||||||
|
c_memLength: memLength))
|
||||||
|
|
||||||
|
# EIP 2046: temporary disabled
|
||||||
|
# reduce gas fee for precompiles
|
||||||
|
# from 700 to 40
|
||||||
|
if gasCost >= 0:
|
||||||
|
k.cpt.gasMeter.consumeGas(gasCost, reason = $CallCode)
|
||||||
|
|
||||||
|
k.cpt.returnData.setLen(0)
|
||||||
|
|
||||||
|
if k.cpt.msg.depth >= MaxCallDepth:
|
||||||
|
debug "Computation Failure",
|
||||||
|
reason = "Stack too deep",
|
||||||
|
maximumDepth = MaxCallDepth,
|
||||||
|
depth = k.cpt.msg.depth
|
||||||
|
k.cpt.gasMeter.returnGas(childGasLimit)
|
||||||
|
return
|
||||||
|
|
||||||
|
# EIP 2046: temporary disabled
|
||||||
|
# reduce gas fee for precompiles
|
||||||
|
# from 700 to 40
|
||||||
|
if gasCost < 0 and childGasLimit <= 0:
|
||||||
|
raise newException(
|
||||||
|
OutOfGas, "Gas not enough to perform calculation (callCode)")
|
||||||
|
|
||||||
|
k.cpt.memory.extend(memInPos, memInLen)
|
||||||
|
k.cpt.memory.extend(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
let senderBalance = k.cpt.getBalance(sender)
|
||||||
|
if senderBalance < value:
|
||||||
|
debug "Insufficient funds",
|
||||||
|
available = senderBalance,
|
||||||
|
needed = k.cpt.msg.value
|
||||||
|
k.cpt.gasMeter.returnGas(childGasLimit)
|
||||||
|
return
|
||||||
|
|
||||||
|
let msg = Message(
|
||||||
|
kind: evmcCallCode,
|
||||||
|
depth: k.cpt.msg.depth + 1,
|
||||||
|
gas: childGasLimit,
|
||||||
|
sender: sender,
|
||||||
|
contractAddress: contractAddress,
|
||||||
|
codeAddress: destination,
|
||||||
|
value: value,
|
||||||
|
data: k.cpt.memory.read(memInPos, memInLen),
|
||||||
|
flags: flags)
|
||||||
|
|
||||||
|
var child = newComputation(k.cpt.vmState, msg)
|
||||||
|
k.cpt.chainTo(child):
|
||||||
|
if not child.shouldBurnGas:
|
||||||
|
k.cpt.gasMeter.returnGas(child.gasMeter.gasRemaining)
|
||||||
|
|
||||||
|
if child.isSuccess:
|
||||||
|
k.cpt.merge(child)
|
||||||
|
k.cpt.stack.top(1)
|
||||||
|
|
||||||
|
k.cpt.returnData = child.output
|
||||||
|
let actualOutputSize = min(memOutLen, child.output.len)
|
||||||
|
if actualOutputSize > 0:
|
||||||
|
k.cpt.memory.write(memOutPos,
|
||||||
|
child.output.toOpenArray(0, actualOutputSize - 1))
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
delegateCallOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
||||||
|
## 0xf4, Message-call into this account with an alternative account's
|
||||||
|
## code, but persisting the current values for sender and value.
|
||||||
|
let (gas, value, destination, sender, memInPos, memInLen, memOutPos,
|
||||||
|
memOutLen, flags) = delegateCallParams(k.cpt)
|
||||||
|
|
||||||
|
let (memOffset, memLength) =
|
||||||
|
if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
|
||||||
|
(memInPos, memInLen)
|
||||||
|
else:
|
||||||
|
(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
# EIP2929
|
||||||
|
# This came before old gas calculator
|
||||||
|
# because it will affect `k.cpt.gasMeter.gasRemaining`
|
||||||
|
# and further `childGasLimit`
|
||||||
|
if k.cpt.fork >= FkBerlin:
|
||||||
|
k.cpt.vmState.mutateStateDB:
|
||||||
|
if not db.inAccessList(destination):
|
||||||
|
db.accessList(destination)
|
||||||
|
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
||||||
|
# the form of a constant `gasCall`
|
||||||
|
k.cpt.gasMeter.consumeGas(
|
||||||
|
ColdAccountAccessCost - WarmStorageReadCost,
|
||||||
|
reason = "EIP2929 gasCall")
|
||||||
|
|
||||||
|
let contractAddress = k.cpt.msg.contractAddress
|
||||||
|
|
||||||
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[DelegateCall].c_handler(
|
||||||
|
value,
|
||||||
|
GasParams(
|
||||||
|
kind: DelegateCall,
|
||||||
|
c_isNewAccount: not k.cpt.accountExists(contractAddress),
|
||||||
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
||||||
|
c_contractGas: gas,
|
||||||
|
c_currentMemSize: k.cpt.memory.len,
|
||||||
|
c_memOffset: memOffset,
|
||||||
|
c_memLength: memLength))
|
||||||
|
|
||||||
|
# EIP 2046: temporary disabled
|
||||||
|
# reduce gas fee for precompiles
|
||||||
|
# from 700 to 40
|
||||||
|
if gasCost >= 0:
|
||||||
|
k.cpt.gasMeter.consumeGas(gasCost, reason = $DelegateCall)
|
||||||
|
|
||||||
|
k.cpt.returnData.setLen(0)
|
||||||
|
if k.cpt.msg.depth >= MaxCallDepth:
|
||||||
|
debug "Computation Failure",
|
||||||
|
reason = "Stack too deep",
|
||||||
|
maximumDepth = MaxCallDepth,
|
||||||
|
depth = k.cpt.msg.depth
|
||||||
|
k.cpt.gasMeter.returnGas(childGasLimit)
|
||||||
|
return
|
||||||
|
|
||||||
|
if gasCost < 0 and childGasLimit <= 0:
|
||||||
|
raise newException(
|
||||||
|
OutOfGas, "Gas not enough to perform calculation (delegateCall)")
|
||||||
|
|
||||||
|
k.cpt.memory.extend(memInPos, memInLen)
|
||||||
|
k.cpt.memory.extend(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
let msg = Message(
|
||||||
|
kind: evmcDelegateCall,
|
||||||
|
depth: k.cpt.msg.depth + 1,
|
||||||
|
gas: childGasLimit,
|
||||||
|
sender: sender,
|
||||||
|
contractAddress: contractAddress,
|
||||||
|
codeAddress: destination,
|
||||||
|
value: value,
|
||||||
|
data: k.cpt.memory.read(memInPos, memInLen),
|
||||||
|
flags: flags)
|
||||||
|
|
||||||
|
var child = newComputation(k.cpt.vmState, msg)
|
||||||
|
k.cpt.chainTo(child):
|
||||||
|
if not child.shouldBurnGas:
|
||||||
|
k.cpt.gasMeter.returnGas(child.gasMeter.gasRemaining)
|
||||||
|
|
||||||
|
if child.isSuccess:
|
||||||
|
k.cpt.merge(child)
|
||||||
|
k.cpt.stack.top(1)
|
||||||
|
|
||||||
|
k.cpt.returnData = child.output
|
||||||
|
let actualOutputSize = min(memOutLen, child.output.len)
|
||||||
|
if actualOutputSize > 0:
|
||||||
|
k.cpt.memory.write(memOutPos,
|
||||||
|
child.output.toOpenArray(0, actualOutputSize - 1))
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
staticCallOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
||||||
|
## 0xfa, Static message-call into an account.
|
||||||
|
let (gas, value, destination, sender, memInPos, memInLen, memOutPos,
|
||||||
|
memOutLen, flags) = staticCallParams(k.cpt)
|
||||||
|
|
||||||
|
let (memOffset, memLength) =
|
||||||
|
if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
|
||||||
|
(memInPos, memInLen)
|
||||||
|
else:
|
||||||
|
(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
# EIP2929
|
||||||
|
# This came before old gas calculator
|
||||||
|
# because it will affect `k.cpt.gasMeter.gasRemaining`
|
||||||
|
# and further `childGasLimit`
|
||||||
|
if k.cpt.fork >= FkBerlin:
|
||||||
|
if k.cpt.fork >= FkBerlin:
|
||||||
|
k.cpt.vmState.mutateStateDB:
|
||||||
|
if not db.inAccessList(destination):
|
||||||
|
db.accessList(destination)
|
||||||
|
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
||||||
|
# the form of a constant `gasCall`
|
||||||
|
k.cpt.gasMeter.consumeGas(
|
||||||
|
ColdAccountAccessCost - WarmStorageReadCost,
|
||||||
|
reason = "EIP2929 gasCall")
|
||||||
|
|
||||||
|
let contractAddress = destination
|
||||||
|
|
||||||
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[StaticCall].c_handler(
|
||||||
|
value,
|
||||||
|
GasParams(
|
||||||
|
kind: StaticCall,
|
||||||
|
c_isNewAccount: not k.cpt.accountExists(contractAddress),
|
||||||
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
||||||
|
c_contractGas: gas,
|
||||||
|
c_currentMemSize: k.cpt.memory.len,
|
||||||
|
c_memOffset: memOffset,
|
||||||
|
c_memLength: memLength))
|
||||||
|
|
||||||
|
# EIP 2046: temporary disabled
|
||||||
|
# reduce gas fee for precompiles
|
||||||
|
# from 700 to 40
|
||||||
|
#
|
||||||
|
# when opCode == StaticCall:
|
||||||
|
# if k.cpt.fork >= FkBerlin and destination.toInt <= MaxPrecompilesAddr:
|
||||||
|
# gasCost = gasCost - 660.GasInt
|
||||||
|
if gasCost >= 0:
|
||||||
|
k.cpt.gasMeter.consumeGas(gasCost, reason = $StaticCall)
|
||||||
|
|
||||||
|
k.cpt.returnData.setLen(0)
|
||||||
|
|
||||||
|
if k.cpt.msg.depth >= MaxCallDepth:
|
||||||
|
debug "Computation Failure",
|
||||||
|
reason = "Stack too deep",
|
||||||
|
maximumDepth = MaxCallDepth,
|
||||||
|
depth = k.cpt.msg.depth
|
||||||
|
k.cpt.gasMeter.returnGas(childGasLimit)
|
||||||
|
return
|
||||||
|
|
||||||
|
if gasCost < 0 and childGasLimit <= 0:
|
||||||
|
raise newException(
|
||||||
|
OutOfGas, "Gas not enough to perform calculation (staticCall)")
|
||||||
|
|
||||||
|
k.cpt.memory.extend(memInPos, memInLen)
|
||||||
|
k.cpt.memory.extend(memOutPos, memOutLen)
|
||||||
|
|
||||||
|
let msg = Message(
|
||||||
|
kind: evmcCall,
|
||||||
|
depth: k.cpt.msg.depth + 1,
|
||||||
|
gas: childGasLimit,
|
||||||
|
sender: sender,
|
||||||
|
contractAddress: contractAddress,
|
||||||
|
codeAddress: destination,
|
||||||
|
value: value,
|
||||||
|
data: k.cpt.memory.read(memInPos, memInLen),
|
||||||
|
flags: flags)
|
||||||
|
|
||||||
|
var child = newComputation(k.cpt.vmState, msg)
|
||||||
|
k.cpt.chainTo(child):
|
||||||
|
if not child.shouldBurnGas:
|
||||||
|
k.cpt.gasMeter.returnGas(child.gasMeter.gasRemaining)
|
||||||
|
|
||||||
|
if child.isSuccess:
|
||||||
|
k.cpt.merge(child)
|
||||||
|
k.cpt.stack.top(1)
|
||||||
|
|
||||||
|
k.cpt.returnData = child.output
|
||||||
|
let actualOutputSize = min(memOutLen, child.output.len)
|
||||||
|
if actualOutputSize > 0:
|
||||||
|
k.cpt.memory.write(memOutPos,
|
||||||
|
child.output.toOpenArray(0, actualOutputSize - 1))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public, op exec table entries
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const
|
||||||
|
vm2OpExecCall*: seq[Vm2OpExec] = @[
|
||||||
|
|
||||||
|
(opCode: Call, ## 0xf1, Message-Call into an account
|
||||||
|
forks: Vm2OpAllForks,
|
||||||
|
info: "Message-Call into an account",
|
||||||
|
exec: (prep: vm2OpIgnore,
|
||||||
|
run: callOp,
|
||||||
|
post: vm2OpIgnore)),
|
||||||
|
|
||||||
|
(opCode: CallCode, ## 0xf2, Message-Call with alternative code
|
||||||
|
forks: Vm2OpAllForks,
|
||||||
|
info: "Message-call into this account with alternative account's code",
|
||||||
|
exec: (prep: vm2OpIgnore,
|
||||||
|
run: callCodeOp,
|
||||||
|
post: vm2OpIgnore)),
|
||||||
|
|
||||||
|
(opCode: DelegateCall, ## 0xf4, CallCode with persisting sender and value
|
||||||
|
forks: Vm2OpAllForks,
|
||||||
|
info: "Message-call into this account with an alternative account's " &
|
||||||
|
"code but persisting the current values for sender and value.",
|
||||||
|
exec: (prep: vm2OpIgnore,
|
||||||
|
run: delegateCallOp,
|
||||||
|
post: vm2OpIgnore)),
|
||||||
|
|
||||||
|
(opCode: StaticCall, ## 0xfa, Static message-call into an account
|
||||||
|
forks: Vm2OpAllForks,
|
||||||
|
info: "Static message-call into an account",
|
||||||
|
exec: (prep: vm2OpIgnore,
|
||||||
|
run: staticCallOp,
|
||||||
|
post: vm2OpIgnore))]
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# End
|
||||||
|
# ------------------------------------------------------------------------------
|
@ -57,6 +57,7 @@ else:
|
|||||||
depth*: int
|
depth*: int
|
||||||
gas*: GasInt
|
gas*: GasInt
|
||||||
contractAddress*: EthAddress
|
contractAddress*: EthAddress
|
||||||
|
codeAddress*: EthAddress
|
||||||
sender*: EthAddress
|
sender*: EthAddress
|
||||||
value*: UInt256
|
value*: UInt256
|
||||||
data*: seq[byte]
|
data*: seq[byte]
|
||||||
|
@ -6,44 +6,12 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
strformat, times, sets, options,
|
./op_handlers/oph_defs,
|
||||||
chronicles, stint, nimcrypto, eth/common,
|
./op_handlers
|
||||||
./utils/[macros_procs_opcodes, v2utils_numeric],
|
|
||||||
./gas_meter, ./v2gas_costs, ./v2opcode_values, ./v2forks,
|
|
||||||
../v2memory, ../stack, ../code_stream, ../v2computation, ../v2state, ../v2types,
|
|
||||||
../../errors, ../../constants,
|
|
||||||
../../db/[db_chain, accounts_cache]
|
|
||||||
|
|
||||||
# verify that experimental op table compiles
|
|
||||||
import
|
|
||||||
./op_handlers, ./op_handlers/oph_defs
|
|
||||||
|
|
||||||
logScope:
|
|
||||||
topics = "opcode impl"
|
|
||||||
|
|
||||||
# ##################################
|
|
||||||
# Syntactic sugar
|
|
||||||
|
|
||||||
template push(x: typed) {.dirty.} =
|
|
||||||
## Push an expression on the computation stack
|
|
||||||
c.stack.push x
|
|
||||||
|
|
||||||
# ##################################
|
# ##################################
|
||||||
# re-implemented OP handlers
|
# re-implemented OP handlers
|
||||||
|
|
||||||
var gdbBPHook_counter = 0
|
|
||||||
proc gdbBPHook*() =
|
|
||||||
gdbBPHook_counter.inc
|
|
||||||
stderr.write &"*** Hello {gdbBPHook_counter}\n"
|
|
||||||
stderr.flushFile
|
|
||||||
|
|
||||||
template opHandlerX(callName: untyped; opCode: Op; fork = FkBerlin) =
|
|
||||||
proc callName*(c: Computation) =
|
|
||||||
gdbBPHook()
|
|
||||||
var desc: Vm2Ctx
|
|
||||||
desc.cpt = c
|
|
||||||
vm2OpHandlers[fork][opCode].exec.run(desc)
|
|
||||||
|
|
||||||
template opHandler(callName: untyped; opCode: Op; fork = FkBerlin) =
|
template opHandler(callName: untyped; opCode: Op; fork = FkBerlin) =
|
||||||
proc callName*(c: Computation) =
|
proc callName*(c: Computation) =
|
||||||
var desc: Vm2Ctx
|
var desc: Vm2Ctx
|
||||||
@ -203,8 +171,12 @@ opHandler log2, Op.Log2
|
|||||||
opHandler log3, Op.Log3
|
opHandler log3, Op.Log3
|
||||||
opHandler log4, Op.Log4
|
opHandler log4, Op.Log4
|
||||||
opHandler create, Op.Create
|
opHandler create, Op.Create
|
||||||
|
opHandler call, Op.Call
|
||||||
|
opHandler callCode, Op.CallCode
|
||||||
opHandler create2, Op.Create2
|
opHandler create2, Op.Create2
|
||||||
opHandler returnOp, Op.Return
|
opHandler returnOp, Op.Return
|
||||||
|
opHandler delegateCall, Op.DelegateCall
|
||||||
|
opHandler staticCall, Op.StaticCall
|
||||||
opHandler revert, Op.Revert
|
opHandler revert, Op.Revert
|
||||||
opHandler invalidOp, Op.Invalid
|
opHandler invalidOp, Op.Invalid
|
||||||
|
|
||||||
@ -212,175 +184,3 @@ opHandler selfDestruct, Op.SelfDestruct, FkFrontier
|
|||||||
opHandler selfDestructEIP150, Op.SelfDestruct, FkTangerine
|
opHandler selfDestructEIP150, Op.SelfDestruct, FkTangerine
|
||||||
opHandler selfDestructEIP161, Op.SelfDestruct, FkSpurious
|
opHandler selfDestructEIP161, Op.SelfDestruct, FkSpurious
|
||||||
opHandler selfDestructEIP2929, Op.SelfDestruct
|
opHandler selfDestructEIP2929, Op.SelfDestruct
|
||||||
|
|
||||||
# ##########################################
|
|
||||||
# f0s: System operations.
|
|
||||||
|
|
||||||
proc callParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
|
|
||||||
let gas = c.stack.popInt()
|
|
||||||
let destination = c.stack.popAddress()
|
|
||||||
let value = c.stack.popInt()
|
|
||||||
|
|
||||||
result = (gas,
|
|
||||||
value,
|
|
||||||
destination,
|
|
||||||
c.msg.contractAddress, # sender
|
|
||||||
evmcCall,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.msg.flags)
|
|
||||||
|
|
||||||
proc callCodeParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
|
|
||||||
let gas = c.stack.popInt()
|
|
||||||
let destination = c.stack.popAddress()
|
|
||||||
let value = c.stack.popInt()
|
|
||||||
|
|
||||||
result = (gas,
|
|
||||||
value,
|
|
||||||
destination,
|
|
||||||
c.msg.contractAddress, # sender
|
|
||||||
evmcCallCode,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.msg.flags)
|
|
||||||
|
|
||||||
proc delegateCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
|
|
||||||
let gas = c.stack.popInt()
|
|
||||||
let destination = c.stack.popAddress()
|
|
||||||
|
|
||||||
result = (gas,
|
|
||||||
c.msg.value, # value
|
|
||||||
destination,
|
|
||||||
c.msg.sender, # sender
|
|
||||||
evmcDelegateCall,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.msg.flags)
|
|
||||||
|
|
||||||
proc staticCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, CallKind, int, int, int, int, MsgFlags) =
|
|
||||||
let gas = c.stack.popInt()
|
|
||||||
let destination = c.stack.popAddress()
|
|
||||||
|
|
||||||
result = (gas,
|
|
||||||
0.u256, # value
|
|
||||||
destination,
|
|
||||||
c.msg.contractAddress, # sender
|
|
||||||
evmcCall,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
c.stack.popInt().cleanMemRef,
|
|
||||||
emvcStatic) # is_static
|
|
||||||
|
|
||||||
template genCall(callName: untyped, opCode: Op): untyped =
|
|
||||||
op callName, inline = false:
|
|
||||||
## CALL, 0xf1, Message-Call into an account
|
|
||||||
## CALLCODE, 0xf2, Message-call into this account with an alternative account's code.
|
|
||||||
## DELEGATECALL, 0xf4, Message-call into this account with an alternative account's code, but persisting the current values for sender and value.
|
|
||||||
## STATICCALL, 0xfa, Static message-call into an account.
|
|
||||||
when opCode == Call:
|
|
||||||
if emvcStatic == c.msg.flags and c.stack[^3, Uint256] > 0.u256:
|
|
||||||
raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context")
|
|
||||||
|
|
||||||
let (gas, value, destination, sender, callKind,
|
|
||||||
memInPos, memInLen, memOutPos, memOutLen, flags) = `callName Params`(c)
|
|
||||||
|
|
||||||
push: 0
|
|
||||||
|
|
||||||
let (memOffset, memLength) = if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
|
|
||||||
(memInPos, memInLen)
|
|
||||||
else:
|
|
||||||
(memOutPos, memOutLen)
|
|
||||||
|
|
||||||
# EIP2929
|
|
||||||
# This came before old gas calculator
|
|
||||||
# because it will affect `c.gasMeter.gasRemaining`
|
|
||||||
# and further `childGasLimit`
|
|
||||||
if c.fork >= FkBerlin:
|
|
||||||
c.vmState.mutateStateDB:
|
|
||||||
if not db.inAccessList(destination):
|
|
||||||
db.accessList(destination)
|
|
||||||
# The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant `gasCall`
|
|
||||||
c.gasMeter.consumeGas(ColdAccountAccessCost - WarmStorageReadCost, reason = "EIP2929 gasCall")
|
|
||||||
|
|
||||||
let contractAddress = when opCode in {Call, StaticCall}: destination else: c.msg.contractAddress
|
|
||||||
var (gasCost, childGasLimit) = c.gasCosts[opCode].c_handler(
|
|
||||||
value,
|
|
||||||
GasParams(kind: opCode,
|
|
||||||
c_isNewAccount: not c.accountExists(contractAddress),
|
|
||||||
c_gasBalance: c.gasMeter.gasRemaining,
|
|
||||||
c_contractGas: gas,
|
|
||||||
c_currentMemSize: c.memory.len,
|
|
||||||
c_memOffset: memOffset,
|
|
||||||
c_memLength: memLength
|
|
||||||
))
|
|
||||||
|
|
||||||
# EIP 2046: temporary disabled
|
|
||||||
# reduce gas fee for precompiles
|
|
||||||
# from 700 to 40
|
|
||||||
#when opCode == StaticCall:
|
|
||||||
# if c.fork >= FkBerlin and destination.toInt <= MaxPrecompilesAddr:
|
|
||||||
# gasCost = gasCost - 660.GasInt
|
|
||||||
|
|
||||||
if gasCost >= 0:
|
|
||||||
c.gasMeter.consumeGas(gasCost, reason = $opCode)
|
|
||||||
|
|
||||||
c.returnData.setLen(0)
|
|
||||||
|
|
||||||
if c.msg.depth >= MaxCallDepth:
|
|
||||||
debug "Computation Failure", reason = "Stack too deep", maximumDepth = MaxCallDepth, depth = c.msg.depth
|
|
||||||
# return unused gas
|
|
||||||
c.gasMeter.returnGas(childGasLimit)
|
|
||||||
return
|
|
||||||
|
|
||||||
if gasCost < 0 and childGasLimit <= 0:
|
|
||||||
raise newException(OutOfGas, "Gas not enough to perform calculation (" & callName.astToStr & ")")
|
|
||||||
|
|
||||||
c.memory.extend(memInPos, memInLen)
|
|
||||||
c.memory.extend(memOutPos, memOutLen)
|
|
||||||
|
|
||||||
when opCode in {CallCode, Call}:
|
|
||||||
let senderBalance = c.getBalance(sender)
|
|
||||||
if senderBalance < value:
|
|
||||||
debug "Insufficient funds", available = senderBalance, needed = c.msg.value
|
|
||||||
# return unused gas
|
|
||||||
c.gasMeter.returnGas(childGasLimit)
|
|
||||||
return
|
|
||||||
|
|
||||||
block:
|
|
||||||
let msg = Message(
|
|
||||||
kind: callKind,
|
|
||||||
depth: c.msg.depth + 1,
|
|
||||||
gas: childGasLimit,
|
|
||||||
sender: sender,
|
|
||||||
contractAddress: contractAddress,
|
|
||||||
codeAddress: destination,
|
|
||||||
value: value,
|
|
||||||
data: c.memory.read(memInPos, memInLen),
|
|
||||||
flags: flags)
|
|
||||||
|
|
||||||
var child = newComputation(c.vmState, msg)
|
|
||||||
c.chainTo(child):
|
|
||||||
if not child.shouldBurnGas:
|
|
||||||
c.gasMeter.returnGas(child.gasMeter.gasRemaining)
|
|
||||||
|
|
||||||
if child.isSuccess:
|
|
||||||
c.merge(child)
|
|
||||||
c.stack.top(1)
|
|
||||||
|
|
||||||
c.returnData = child.output
|
|
||||||
let actualOutputSize = min(memOutLen, child.output.len)
|
|
||||||
if actualOutputSize > 0:
|
|
||||||
c.memory.write(memOutPos,
|
|
||||||
child.output.toOpenArray(0, actualOutputSize - 1))
|
|
||||||
|
|
||||||
genCall(call, Call)
|
|
||||||
genCall(callCode, CallCode)
|
|
||||||
genCall(delegateCall, DelegateCall)
|
|
||||||
genCall(staticCall, StaticCall)
|
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
# Nimbus
|
|
||||||
# Copyright (c) 2018 Status Research & Development GmbH
|
|
||||||
# Licensed under either of
|
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
||||||
|
|
||||||
# ##################################################################
|
|
||||||
# Macros to facilitate opcode procs creation
|
|
||||||
|
|
||||||
import
|
|
||||||
macros, strformat, stint, eth/common,
|
|
||||||
../../v2computation, ../../stack, ../../code_stream, ../../v2memory,
|
|
||||||
../../v2types, ../../../errors, ../gas_meter, ../v2opcode_values,
|
|
||||||
./v2utils_numeric
|
|
||||||
|
|
||||||
proc pop(tree: var NimNode): NimNode =
|
|
||||||
## Returns the last value of a NimNode and remove it
|
|
||||||
result = tree[tree.len-1]
|
|
||||||
tree.del(tree.len-1)
|
|
||||||
|
|
||||||
macro op*(procname: untyped, inline: static[bool], stackParams_body: varargs[untyped]): untyped =
|
|
||||||
## Usage:
|
|
||||||
## .. code-block:: nim
|
|
||||||
## op add, inline = true, lhs, rhs:
|
|
||||||
## push:
|
|
||||||
## lhs + rhs
|
|
||||||
|
|
||||||
# TODO: Unfortunately due to varargs[untyped] consuming all following parameters,
|
|
||||||
# we can't have a nicer macro signature `stackParams: varargs[untyped], body: untyped`
|
|
||||||
# see https://github.com/nim-lang/Nim/issues/5855 and are forced to "pop"
|
|
||||||
|
|
||||||
let computation = newIdentNode("c")
|
|
||||||
var stackParams = stackParams_body
|
|
||||||
|
|
||||||
# 1. Separate stackParams and body with pop
|
|
||||||
let body = newStmtList().add stackParams.pop
|
|
||||||
|
|
||||||
# 3. let (x, y, z) = computation.stack.popInt(3)
|
|
||||||
let len = stackParams.len
|
|
||||||
var popStackStmt = nnkVarTuple.newTree()
|
|
||||||
|
|
||||||
if len != 0:
|
|
||||||
for params in stackParams:
|
|
||||||
popStackStmt.add newIdentNode(params.strVal)
|
|
||||||
|
|
||||||
popStackStmt.add newEmptyNode()
|
|
||||||
popStackStmt.add quote do:
|
|
||||||
`computation`.stack.popInt(`len`)
|
|
||||||
|
|
||||||
popStackStmt = nnkStmtList.newTree(
|
|
||||||
nnkLetSection.newTree(popStackStmt)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
popStackStmt = nnkDiscardStmt.newTree(newEmptyNode())
|
|
||||||
|
|
||||||
# 4. Generate the proc
|
|
||||||
# TODO: replace by func to ensure no side effects
|
|
||||||
if inline:
|
|
||||||
result = quote do:
|
|
||||||
proc `procname`*(`computation`: Computation) {.inline.} =
|
|
||||||
`popStackStmt`
|
|
||||||
`body`
|
|
||||||
else:
|
|
||||||
result = quote do:
|
|
||||||
proc `procname`*(`computation`: Computation) {.gcsafe.} =
|
|
||||||
`popStackStmt`
|
|
||||||
`body`
|
|
||||||
|
|
||||||
macro genPush*(): untyped =
|
|
||||||
# TODO: avoid allocating a seq[byte], transforming to a string, stripping char
|
|
||||||
func genName(size: int): NimNode = ident(&"push{size}")
|
|
||||||
result = newStmtList()
|
|
||||||
|
|
||||||
for size in 1 .. 32:
|
|
||||||
let name = genName(size)
|
|
||||||
result.add quote do:
|
|
||||||
func `name`*(computation: Computation) {.inline.}=
|
|
||||||
## Push `size`-byte(s) on the stack
|
|
||||||
computation.stack.push computation.code.readVmWord(`size`)
|
|
||||||
|
|
||||||
macro genDup*(): untyped =
|
|
||||||
func genName(position: int): NimNode = ident(&"dup{position}")
|
|
||||||
result = newStmtList()
|
|
||||||
|
|
||||||
for pos in 1 .. 16:
|
|
||||||
let name = genName(pos)
|
|
||||||
result.add quote do:
|
|
||||||
func `name`*(computation: Computation) {.inline.}=
|
|
||||||
computation.stack.dup(`pos`)
|
|
||||||
|
|
||||||
macro genSwap*(): untyped =
|
|
||||||
func genName(position: int): NimNode = ident(&"swap{position}")
|
|
||||||
result = newStmtList()
|
|
||||||
|
|
||||||
for pos in 1 .. 16:
|
|
||||||
let name = genName(pos)
|
|
||||||
result.add quote do:
|
|
||||||
func `name`*(computation: Computation) {.inline.}=
|
|
||||||
computation.stack.swap(`pos`)
|
|
||||||
|
|
||||||
template checkInStaticContext*(comp: Computation) =
|
|
||||||
# TODO: if possible, this check only appear
|
|
||||||
# when fork >= FkByzantium
|
|
||||||
if emvcStatic == comp.msg.flags:
|
|
||||||
raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context")
|
|
||||||
|
|
||||||
proc logImpl(c: Computation, opcode: Op, topicCount: int) =
|
|
||||||
doAssert(topicCount in 0 .. 4)
|
|
||||||
checkInStaticContext(c)
|
|
||||||
let (memStartPosition, size) = c.stack.popInt(2)
|
|
||||||
let (memPos, len) = (memStartPosition.cleanMemRef, size.cleanMemRef)
|
|
||||||
|
|
||||||
if memPos < 0 or len < 0:
|
|
||||||
raise newException(OutOfBoundsRead, "Out of bounds memory access")
|
|
||||||
|
|
||||||
c.gasMeter.consumeGas(
|
|
||||||
c.gasCosts[opcode].m_handler(c.memory.len, memPos, len),
|
|
||||||
reason="Memory expansion, Log topic and data gas cost")
|
|
||||||
c.memory.extend(memPos, len)
|
|
||||||
|
|
||||||
block:
|
|
||||||
var log: Log
|
|
||||||
log.topics = newSeqOfCap[Topic](topicCount)
|
|
||||||
for i in 0 ..< topicCount:
|
|
||||||
log.topics.add(c.stack.popTopic())
|
|
||||||
|
|
||||||
log.data = c.memory.read(memPos, len)
|
|
||||||
log.address = c.msg.contractAddress
|
|
||||||
c.addLogEntry(log)
|
|
||||||
|
|
||||||
template genLog*() =
|
|
||||||
proc log0*(c: Computation) {.inline.} = logImpl(c, Log0, 0)
|
|
||||||
proc log1*(c: Computation) {.inline.} = logImpl(c, Log1, 1)
|
|
||||||
proc log2*(c: Computation) {.inline.} = logImpl(c, Log2, 2)
|
|
||||||
proc log3*(c: Computation) {.inline.} = logImpl(c, Log3, 3)
|
|
||||||
proc log4*(c: Computation) {.inline.} = logImpl(c, Log4, 4)
|
|
Loading…
x
Reference in New Issue
Block a user