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,
|
||||
oph_arithmetic, oph_hash, oph_envinfo, oph_blockdata,
|
||||
oph_memory, oph_push, oph_dup, oph_swap, oph_log,
|
||||
oph_create, oph_sysops]
|
||||
oph_create, oph_call, oph_sysops]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Helper
|
||||
@ -52,7 +52,7 @@ proc mkOpTable(select: Fork): array[Op,Vm2OpExec] {.compileTime.} =
|
||||
result.importList(select, vm2OpExecSwap, "Swap")
|
||||
result.importList(select, vm2OpExecLog, "Log")
|
||||
result.importList(select, vm2OpExecCreate, "Create")
|
||||
#result.importList(select, vm2OpExecCall, "Call")
|
||||
result.importList(select, vm2OpExecCall, "Call")
|
||||
result.importList(select, vm2OpExecSysOp, "SysOp")
|
||||
|
||||
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
|
||||
gas*: GasInt
|
||||
contractAddress*: EthAddress
|
||||
codeAddress*: EthAddress
|
||||
sender*: EthAddress
|
||||
value*: UInt256
|
||||
data*: seq[byte]
|
||||
|
@ -6,44 +6,12 @@
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
strformat, times, sets, options,
|
||||
chronicles, stint, nimcrypto, eth/common,
|
||||
./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
|
||||
./op_handlers/oph_defs,
|
||||
./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) =
|
||||
proc callName*(c: Computation) =
|
||||
var desc: Vm2Ctx
|
||||
@ -203,8 +171,12 @@ opHandler log2, Op.Log2
|
||||
opHandler log3, Op.Log3
|
||||
opHandler log4, Op.Log4
|
||||
opHandler create, Op.Create
|
||||
opHandler call, Op.Call
|
||||
opHandler callCode, Op.CallCode
|
||||
opHandler create2, Op.Create2
|
||||
opHandler returnOp, Op.Return
|
||||
opHandler delegateCall, Op.DelegateCall
|
||||
opHandler staticCall, Op.StaticCall
|
||||
opHandler revert, Op.Revert
|
||||
opHandler invalidOp, Op.Invalid
|
||||
|
||||
@ -212,175 +184,3 @@ opHandler selfDestruct, Op.SelfDestruct, FkFrontier
|
||||
opHandler selfDestructEIP150, Op.SelfDestruct, FkTangerine
|
||||
opHandler selfDestructEIP161, Op.SelfDestruct, FkSpurious
|
||||
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