2021-04-16 15:06:02 +00:00
|
|
|
# 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
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
type
|
|
|
|
LocalParams = tuple
|
|
|
|
gas: UInt256
|
|
|
|
value: UInt256
|
|
|
|
destination: EthAddress
|
|
|
|
sender: EthAddress
|
|
|
|
memInPos: int
|
|
|
|
memInLen: int
|
|
|
|
memOutPos: int
|
|
|
|
memOutLen: int
|
|
|
|
flags: MsgFlags
|
|
|
|
memOffset: int
|
|
|
|
memLength: int
|
|
|
|
contractAddress: EthAddress
|
|
|
|
|
|
|
|
|
|
|
|
proc updateStackAndParams(q: var LocalParams; c: Computation) =
|
2021-04-16 15:06:02 +00:00
|
|
|
c.stack.push(0)
|
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
let
|
|
|
|
outLen = calcMemSize(q.memOutPos, q.memOutLen)
|
|
|
|
inLen = calcMemSize(q.memInPos, q.memInLen)
|
|
|
|
|
|
|
|
# get the bigger one
|
|
|
|
if outLen < inLen:
|
|
|
|
q.memOffset = q.memInPos
|
|
|
|
q.memLength = q.memInLen
|
|
|
|
else:
|
|
|
|
q.memOffset = q.memOutPos
|
|
|
|
q.memLength = q.memOutLen
|
|
|
|
|
|
|
|
# EIP2929: This came before old gas calculator
|
|
|
|
# because it will affect `c.gasMeter.gasRemaining`
|
|
|
|
# and further `childGasLimit`
|
|
|
|
if FkBerlin <= c.fork:
|
|
|
|
c.vmState.mutateStateDB:
|
|
|
|
if not db.inAccessList(q.destination):
|
|
|
|
db.accessList(q.destination)
|
|
|
|
|
|
|
|
# The WarmStorageReadCostEIP2929 (100) is already deducted in
|
|
|
|
# the form of a constant `gasCall`
|
|
|
|
c.gasMeter.consumeGas(
|
|
|
|
ColdAccountAccessCost - WarmStorageReadCost,
|
|
|
|
reason = "EIP2929 gasCall")
|
|
|
|
|
|
|
|
|
|
|
|
proc callParams(c: Computation): LocalParams =
|
|
|
|
## Helper for callOp()
|
|
|
|
result.gas = c.stack.popInt()
|
|
|
|
result.destination = c.stack.popAddress()
|
|
|
|
result.value = c.stack.popInt()
|
|
|
|
result.memInPos = c.stack.popInt().cleanMemRef
|
|
|
|
result.memInLen = c.stack.popInt().cleanMemRef
|
|
|
|
result.memOutPos = c.stack.popInt().cleanMemRef
|
|
|
|
result.memOutLen = c.stack.popInt().cleanMemRef
|
|
|
|
|
|
|
|
result.sender = c.msg.contractAddress
|
|
|
|
result.flags = c.msg.flags
|
|
|
|
result.contractAddress = result.destination
|
|
|
|
|
|
|
|
result.updateStackAndParams(c)
|
|
|
|
|
|
|
|
|
|
|
|
proc callCodeParams(c: Computation): LocalParams =
|
|
|
|
## Helper for callCodeOp()
|
|
|
|
result = c.callParams
|
|
|
|
result.contractAddress = c.msg.contractAddress
|
|
|
|
|
|
|
|
|
|
|
|
proc delegateCallParams(c: Computation): LocalParams =
|
|
|
|
## Helper for delegateCall()
|
|
|
|
result.gas = c.stack.popInt()
|
|
|
|
result.destination = c.stack.popAddress()
|
|
|
|
result.memInPos = c.stack.popInt().cleanMemRef
|
|
|
|
result.memInLen = c.stack.popInt().cleanMemRef
|
|
|
|
result.memOutPos = c.stack.popInt().cleanMemRef
|
|
|
|
result.memOutLen = c.stack.popInt().cleanMemRef
|
|
|
|
|
|
|
|
result.value = c.msg.value
|
|
|
|
result.sender = c.msg.sender
|
|
|
|
result.flags = c.msg.flags
|
|
|
|
result.contractAddress = c.msg.contractAddress
|
|
|
|
|
|
|
|
result.updateStackAndParams(c)
|
|
|
|
|
|
|
|
|
|
|
|
proc staticCallParams(c: Computation): LocalParams =
|
|
|
|
## Helper for staticCall()
|
|
|
|
result.gas = c.stack.popInt()
|
|
|
|
result.destination = c.stack.popAddress()
|
|
|
|
result.memInPos = c.stack.popInt().cleanMemRef
|
|
|
|
result.memInLen = c.stack.popInt().cleanMemRef
|
|
|
|
result.memOutPos = c.stack.popInt().cleanMemRef
|
|
|
|
result.memOutLen = c.stack.popInt().cleanMemRef
|
|
|
|
|
|
|
|
result.value = 0.u256
|
|
|
|
result.sender = c.msg.contractAddress
|
|
|
|
result.flags = emvcStatic
|
|
|
|
result.contractAddress = result.destination
|
|
|
|
|
|
|
|
result.updateStackAndParams(c)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private, op handlers implementation
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
const
|
|
|
|
callOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|
|
|
## 0xf1, Message-Call into an account
|
2021-04-16 17:46:51 +00:00
|
|
|
|
2021-04-16 15:06:02 +00:00
|
|
|
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")
|
2021-04-16 17:46:51 +00:00
|
|
|
let
|
|
|
|
p = k.cpt.callParams
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[Call].c_handler(
|
2021-04-16 17:46:51 +00:00
|
|
|
p.value,
|
2021-04-16 15:06:02 +00:00
|
|
|
GasParams(
|
|
|
|
kind: Call,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_isNewAccount: not k.cpt.accountExists(p.contractAddress),
|
2021-04-16 15:06:02 +00:00
|
|
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_contractGas: p.gas,
|
2021-04-16 15:06:02 +00:00
|
|
|
c_currentMemSize: k.cpt.memory.len,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_memOffset: p.memOffset,
|
|
|
|
c_memLength: p.memLength))
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
# 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)")
|
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.extend(p.memInPos, p.memInLen)
|
|
|
|
k.cpt.memory.extend(p.memOutPos, p.memOutLen)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
let senderBalance = k.cpt.getBalance(p.sender)
|
|
|
|
if senderBalance < p.value:
|
2021-04-16 15:06:02 +00:00
|
|
|
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,
|
2021-04-16 17:46:51 +00:00
|
|
|
sender: p.sender,
|
|
|
|
contractAddress: p.contractAddress,
|
|
|
|
codeAddress: p.destination,
|
|
|
|
value: p.value,
|
|
|
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
|
|
|
flags: p.flags)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
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
|
2021-04-16 17:46:51 +00:00
|
|
|
let actualOutputSize = min(p.memOutLen, child.output.len)
|
2021-04-16 15:06:02 +00:00
|
|
|
if actualOutputSize > 0:
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.write(p.memOutPos,
|
2021-04-16 15:06:02 +00:00
|
|
|
child.output.toOpenArray(0, actualOutputSize - 1))
|
|
|
|
|
|
|
|
# ---------------------
|
|
|
|
|
|
|
|
callCodeOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|
|
|
## 0xf2, Message-call into this account with an alternative account's code.
|
2021-04-16 17:46:51 +00:00
|
|
|
let
|
|
|
|
p = k.cpt.callCodeParams
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[CallCode].c_handler(
|
2021-04-16 17:46:51 +00:00
|
|
|
p.value,
|
2021-04-16 15:06:02 +00:00
|
|
|
GasParams(
|
|
|
|
kind: CallCode,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_isNewAccount: not k.cpt.accountExists(p.contractAddress),
|
2021-04-16 15:06:02 +00:00
|
|
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_contractGas: p.gas,
|
2021-04-16 15:06:02 +00:00
|
|
|
c_currentMemSize: k.cpt.memory.len,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_memOffset: p.memOffset,
|
|
|
|
c_memLength: p.memLength))
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
# 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)")
|
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.extend(p.memInPos, p.memInLen)
|
|
|
|
k.cpt.memory.extend(p.memOutPos, p.memOutLen)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
let senderBalance = k.cpt.getBalance(p.sender)
|
|
|
|
if senderBalance < p.value:
|
2021-04-16 15:06:02 +00:00
|
|
|
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,
|
2021-04-16 17:46:51 +00:00
|
|
|
sender: p.sender,
|
|
|
|
contractAddress: p.contractAddress,
|
|
|
|
codeAddress: p.destination,
|
|
|
|
value: p.value,
|
|
|
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
|
|
|
flags: p.flags)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
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
|
2021-04-16 17:46:51 +00:00
|
|
|
let actualOutputSize = min(p.memOutLen, child.output.len)
|
2021-04-16 15:06:02 +00:00
|
|
|
if actualOutputSize > 0:
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.write(p.memOutPos,
|
2021-04-16 15:06:02 +00:00
|
|
|
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.
|
2021-04-16 17:46:51 +00:00
|
|
|
let
|
|
|
|
p = k.cpt.delegateCallParams
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[DelegateCall].c_handler(
|
2021-04-16 17:46:51 +00:00
|
|
|
p.value,
|
2021-04-16 15:06:02 +00:00
|
|
|
GasParams(
|
|
|
|
kind: DelegateCall,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_isNewAccount: not k.cpt.accountExists(p.contractAddress),
|
2021-04-16 15:06:02 +00:00
|
|
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_contractGas: p.gas,
|
2021-04-16 15:06:02 +00:00
|
|
|
c_currentMemSize: k.cpt.memory.len,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_memOffset: p.memOffset,
|
|
|
|
c_memLength: p.memLength))
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
# 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)")
|
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.extend(p.memInPos, p.memInLen)
|
|
|
|
k.cpt.memory.extend(p.memOutPos, p.memOutLen)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
let msg = Message(
|
|
|
|
kind: evmcDelegateCall,
|
|
|
|
depth: k.cpt.msg.depth + 1,
|
|
|
|
gas: childGasLimit,
|
2021-04-16 17:46:51 +00:00
|
|
|
sender: p.sender,
|
|
|
|
contractAddress: p.contractAddress,
|
|
|
|
codeAddress: p.destination,
|
|
|
|
value: p.value,
|
|
|
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
|
|
|
flags: p.flags)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
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
|
2021-04-16 17:46:51 +00:00
|
|
|
let actualOutputSize = min(p.memOutLen, child.output.len)
|
2021-04-16 15:06:02 +00:00
|
|
|
if actualOutputSize > 0:
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.write(p.memOutPos,
|
2021-04-16 15:06:02 +00:00
|
|
|
child.output.toOpenArray(0, actualOutputSize - 1))
|
|
|
|
|
|
|
|
# ---------------------
|
|
|
|
|
|
|
|
staticCallOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
|
|
|
## 0xfa, Static message-call into an account.
|
2021-04-16 17:46:51 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
p = k.cpt.staticCallParams
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
var (gasCost, childGasLimit) = k.cpt.gasCosts[StaticCall].c_handler(
|
2021-04-16 17:46:51 +00:00
|
|
|
p.value,
|
2021-04-16 15:06:02 +00:00
|
|
|
GasParams(
|
|
|
|
kind: StaticCall,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_isNewAccount: not k.cpt.accountExists(p.contractAddress),
|
2021-04-16 15:06:02 +00:00
|
|
|
c_gasBalance: k.cpt.gasMeter.gasRemaining,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_contractGas: p.gas,
|
2021-04-16 15:06:02 +00:00
|
|
|
c_currentMemSize: k.cpt.memory.len,
|
2021-04-16 17:46:51 +00:00
|
|
|
c_memOffset: p.memOffset,
|
|
|
|
c_memLength: p.memLength))
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
# 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)")
|
|
|
|
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.extend(p.memInPos, p.memInLen)
|
|
|
|
k.cpt.memory.extend(p.memOutPos, p.memOutLen)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
let msg = Message(
|
|
|
|
kind: evmcCall,
|
|
|
|
depth: k.cpt.msg.depth + 1,
|
|
|
|
gas: childGasLimit,
|
2021-04-16 17:46:51 +00:00
|
|
|
sender: p.sender,
|
|
|
|
contractAddress: p.contractAddress,
|
|
|
|
codeAddress: p.destination,
|
|
|
|
value: p.value,
|
|
|
|
data: k.cpt.memory.read(p.memInPos, p.memInLen),
|
|
|
|
flags: p.flags)
|
2021-04-16 15:06:02 +00:00
|
|
|
|
|
|
|
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
|
2021-04-16 17:46:51 +00:00
|
|
|
let actualOutputSize = min(p.memOutLen, child.output.len)
|
2021-04-16 15:06:02 +00:00
|
|
|
if actualOutputSize > 0:
|
2021-04-16 17:46:51 +00:00
|
|
|
k.cpt.memory.write(p.memOutPos,
|
2021-04-16 15:06:02 +00:00
|
|
|
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
|
|
|
|
# ------------------------------------------------------------------------------
|