256 lines
8.0 KiB
Nim
256 lines
8.0 KiB
Nim
|
# 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: Create Operations
|
||
|
## ======================================
|
||
|
##
|
||
|
|
||
|
const
|
||
|
kludge {.intdefine.}: int = 0
|
||
|
breakCircularDependency {.used.} = kludge > 0
|
||
|
|
||
|
import
|
||
|
../../../errors,
|
||
|
./oph_defs,
|
||
|
./oph_helpers,
|
||
|
chronicles,
|
||
|
strformat,
|
||
|
stint
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Kludge BEGIN
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
when not breakCircularDependency:
|
||
|
import
|
||
|
../../../constants,
|
||
|
../../stack,
|
||
|
../../v2computation,
|
||
|
../../v2memory,
|
||
|
../../v2state,
|
||
|
../../v2types,
|
||
|
../gas_meter,
|
||
|
../utils/v2utils_numeric,
|
||
|
../v2gas_costs,
|
||
|
eth/common
|
||
|
|
||
|
else:
|
||
|
import macros
|
||
|
|
||
|
type
|
||
|
GasResult = tuple[gasCost, gasRefund: GasInt]
|
||
|
const
|
||
|
evmcCreate = 42
|
||
|
evmcCreate2 = 43
|
||
|
MaxCallDepth = 45
|
||
|
|
||
|
# function stubs from stack.nim (to satisfy compiler logic)
|
||
|
proc top[T](x: Stack, value: T) = discard
|
||
|
proc peekInt(x: Stack): UInt256 = 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 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 safeInt(x: Uint256): int = 0
|
||
|
|
||
|
# function stubs from v2memory.nim
|
||
|
proc len(mem: Memory): int = 0
|
||
|
proc extend(mem: var Memory; startPos: Natural; size: Natural) = discard
|
||
|
proc read(mem: var Memory, startPos: Natural, size: Natural): seq[byte] = @[]
|
||
|
|
||
|
# function stubs from gas_meter.nim
|
||
|
proc consumeGas(gasMeter: var GasMeter; amount: int; reason: string) = discard
|
||
|
proc returnGas(gasMeter: var GasMeter; amount: GasInt) = discard
|
||
|
|
||
|
# stubs from v2gas_costs.nim
|
||
|
type GasParams = object
|
||
|
case kind*: Op
|
||
|
of Create:
|
||
|
cr_currentMemSize, cr_memOffset, cr_memLength: int64
|
||
|
else:
|
||
|
discard
|
||
|
proc c_handler(x: int; y: Uint256, z: GasParams): GasResult = result
|
||
|
proc m_handler(x: int; curMemSize, memOffset, memLen: int64): int = result
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Kludge END
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Private, op handlers implementation
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
const
|
||
|
createOp: Vm2OpFn = proc(k: Vm2Ctx) =
|
||
|
## 0xf0, Create a new account with associated code
|
||
|
checkInStaticContext(k.cpt)
|
||
|
|
||
|
let
|
||
|
endowment = k.cpt.stack.popInt()
|
||
|
memPos = k.cpt.stack.popInt().safeInt
|
||
|
memLen = k.cpt.stack.peekInt().safeInt
|
||
|
salt = 0.u256
|
||
|
|
||
|
k.cpt.stack.top(0)
|
||
|
|
||
|
let gasParams = GasParams(
|
||
|
kind: Create,
|
||
|
cr_currentMemSize: k.cpt.memory.len,
|
||
|
cr_memOffset: memPos,
|
||
|
cr_memLength: memLen)
|
||
|
|
||
|
var gasCost = k.cpt.gasCosts[Create].c_handler(1.u256, gasParams).gasCost
|
||
|
k.cpt.gasMeter.consumeGas(
|
||
|
gasCost, reason = &"CREATE: GasCreate + {memLen} * memory expansion")
|
||
|
k.cpt.memory.extend(memPos, memLen)
|
||
|
k.cpt.returnData.setLen(0)
|
||
|
|
||
|
if k.cpt.msg.depth >= MaxCallDepth:
|
||
|
debug "Computation Failure",
|
||
|
reason = "Stack too deep",
|
||
|
maxDepth = MaxCallDepth,
|
||
|
depth = k.cpt.msg.depth
|
||
|
return
|
||
|
|
||
|
if endowment != 0:
|
||
|
let senderBalance = k.cpt.getBalance(k.cpt.msg.contractAddress)
|
||
|
if senderBalance < endowment:
|
||
|
debug "Computation Failure",
|
||
|
reason = "Insufficient funds available to transfer",
|
||
|
required = endowment,
|
||
|
balance = senderBalance
|
||
|
return
|
||
|
|
||
|
var createMsgGas = k.cpt.gasMeter.gasRemaining
|
||
|
if k.cpt.fork >= FkTangerine:
|
||
|
createMsgGas -= createMsgGas div 64
|
||
|
k.cpt.gasMeter.consumeGas(createMsgGas, reason = "CREATE")
|
||
|
|
||
|
let childMsg = Message(
|
||
|
kind: evmcCreate,
|
||
|
depth: k.cpt.msg.depth + 1,
|
||
|
gas: createMsgGas,
|
||
|
sender: k.cpt.msg.contractAddress,
|
||
|
value: endowment,
|
||
|
data: k.cpt.memory.read(memPos, memLen))
|
||
|
|
||
|
var child = newComputation(k.cpt.vmState, childMsg, salt)
|
||
|
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 child.msg.contractAddress
|
||
|
else:
|
||
|
k.cpt.returnData = child.output
|
||
|
|
||
|
# ---------------------
|
||
|
|
||
|
create2Op: Vm2OpFn = proc(k: Vm2Ctx) =
|
||
|
## 0xf5, Behaves identically to CREATE, except using keccak256
|
||
|
checkInStaticContext(k.cpt)
|
||
|
|
||
|
let
|
||
|
endowment = k.cpt.stack.popInt()
|
||
|
memPos = k.cpt.stack.popInt().safeInt
|
||
|
memLen = k.cpt.stack.popInt().safeInt
|
||
|
salt = k.cpt.stack.peekInt()
|
||
|
|
||
|
k.cpt.stack.top(0)
|
||
|
|
||
|
let gasParams = GasParams(
|
||
|
kind: Create,
|
||
|
cr_currentMemSize: k.cpt.memory.len,
|
||
|
cr_memOffset: memPos,
|
||
|
cr_memLength: memLen)
|
||
|
|
||
|
var gasCost = k.cpt.gasCosts[Create].c_handler(1.u256, gasParams).gasCost
|
||
|
gasCost = gasCost + k.cpt.gasCosts[Create2].m_handler(0, 0, memLen)
|
||
|
|
||
|
k.cpt.gasMeter.consumeGas(
|
||
|
gasCost, reason = &"CREATE: GasCreate + {memLen} * memory expansion")
|
||
|
k.cpt.memory.extend(memPos, memLen)
|
||
|
k.cpt.returnData.setLen(0)
|
||
|
|
||
|
if k.cpt.msg.depth >= MaxCallDepth:
|
||
|
debug "Computation Failure",
|
||
|
reason = "Stack too deep",
|
||
|
maxDepth = MaxCallDepth,
|
||
|
depth = k.cpt.msg.depth
|
||
|
return
|
||
|
|
||
|
if endowment != 0:
|
||
|
let senderBalance = k.cpt.getBalance(k.cpt.msg.contractAddress)
|
||
|
if senderBalance < endowment:
|
||
|
debug "Computation Failure",
|
||
|
reason = "Insufficient funds available to transfer",
|
||
|
required = endowment,
|
||
|
balance = senderBalance
|
||
|
return
|
||
|
|
||
|
var createMsgGas = k.cpt.gasMeter.gasRemaining
|
||
|
if k.cpt.fork >= FkTangerine:
|
||
|
createMsgGas -= createMsgGas div 64
|
||
|
k.cpt.gasMeter.consumeGas(createMsgGas, reason = "CREATE")
|
||
|
|
||
|
let childMsg = Message(
|
||
|
kind: evmcCreate2,
|
||
|
depth: k.cpt.msg.depth + 1,
|
||
|
gas: createMsgGas,
|
||
|
sender: k.cpt.msg.contractAddress,
|
||
|
value: endowment,
|
||
|
data: k.cpt.memory.read(memPos, memLen))
|
||
|
|
||
|
var child = newComputation(k.cpt.vmState, childMsg, salt)
|
||
|
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 child.msg.contractAddress
|
||
|
else:
|
||
|
k.cpt.returnData = child.output
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Public, op exec table entries
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
const
|
||
|
vm2OpExecCreate*: seq[Vm2OpExec] = @[
|
||
|
|
||
|
(opCode: Create, ## 0xf0, Create a new account with associated code
|
||
|
forks: Vm2OpAllForks,
|
||
|
info: "Create a new account with associated code",
|
||
|
exec: (prep: vm2OpIgnore,
|
||
|
run: createOp,
|
||
|
post: vm2OpIgnore)),
|
||
|
|
||
|
(opCode: Create2, ## 0xf5, Create using keccak256
|
||
|
forks: Vm2OpAllForks,
|
||
|
info: "Behaves identically to CREATE, except using keccak256",
|
||
|
exec: (prep: vm2OpIgnore,
|
||
|
run: create2Op,
|
||
|
post: vm2OpIgnore))]
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# End
|
||
|
# ------------------------------------------------------------------------------
|