re-integrated 0x3# op handlers

This commit is contained in:
Jordan Hrycaj 2021-04-13 18:12:47 +01:00 committed by zah
parent fda676062f
commit fb94aa8a35
4 changed files with 453 additions and 174 deletions

View File

@ -20,7 +20,7 @@ import
strformat,
./op_codes,
./op_handlers/[oph_defs,
oph_arithmetic, oph_hash,
oph_arithmetic, oph_hash, oph_envinfo,
oph_sysops]
# ------------------------------------------------------------------------------
@ -50,6 +50,11 @@ const
doAssert rc[w.opCode].info == ""
rc[w.opCode] = w
for w in vm2OpExecEnvInfo:
if w.complain("EnvInfo"):
doAssert rc[w.opCode].info == ""
rc[w.opCode] = w
for w in vm2OpExecSysOP:
if w.complain("SysOp"):
doAssert rc[w.opCode].info == ""

View File

@ -8,8 +8,8 @@
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
## EVM Opcodes, Definitons
## =======================
## EVM Opcodes, Definitions
## ========================
##
const
@ -36,17 +36,32 @@ else:
{.fatal: "Flag \"vm2_enabled\" must be unset "&
"while circular dependency breaker kludge is activated".}
type
GasMeter* = object
whatever: int
CodeStream* = ref object
bytes*: seq[byte]
Message* = ref object
contractAddress*: UInt256
sender*: UInt256
value*: UInt256
data*: seq[byte]
Computation* = ref object
gasMeter*: GasMeter
stack*: Stack
memory*: Memory
code: int
msg*: Message
code*: CodeStream
returnData*: seq[byte]
# ------------------------------------------------------------------------------
# Kludge END
# ------------------------------------------------------------------------------
export
Op, Fork, Computation, Memory, Stack, UInt256
Op, Fork, Computation, Memory, Stack, UInt256, Message
type
Vm2Ctx* = object of RootObj

View File

@ -0,0 +1,370 @@
# 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: Environmental Information
## ==============================================
##
const
kludge {.intdefine.}: int = 0
breakCircularDependency {.used.} = kludge > 0
import
../../../errors,
./oph_defs,
sequtils,
strformat,
stint
# ------------------------------------------------------------------------------
# Kludge BEGIN
# ------------------------------------------------------------------------------
when not breakCircularDependency:
import
../../code_stream,
../../stack,
../../v2computation,
../../v2memory,
../../v2state,
../gas_meter,
../utils/v2utils_numeric,
eth/common
else:
import macros
# copied from stack.nim
macro genTupleType(len: static[int], elemType: untyped): untyped =
result = nnkTupleConstr.newNimNode()
for i in 0 ..< len: result.add(elemType)
# function stubs from stack.nim (to satisfy compiler logic)
proc push[T](x: Stack; n: T) = discard
proc popAddress(x: var Stack): UInt256 = 0.u256
proc popInt(x: var Stack, n: static[int]): auto =
var rc: genTupleType(n, UInt256)
return rc
# function stubs from v2computation.nim (to satisfy compiler logic)
proc getBalance[T](c: Computation, address: T): Uint256 = 0.u256
proc getOrigin(c: Computation): Uint256 = 0.u256
proc getGasPrice(c: Computation): Uint256 = 0.u256
proc getCodeSize[T](c: Computation, address: T): uint = 0
proc getCode[T](c: Computation, address: T): seq[byte] = @[]
# function stubs from v2utils_numeric.nim
func cleanMemRef(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 write(mem: var Memory, startPos: Natural, val: openarray[byte]) = discard
# function stubs from code_stream.nim
proc len(c: CodeStream): int = len(c.bytes)
# ------------------------------------------------------------------------------
# Kludge END
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc writePaddedResult(mem: var Memory,
data: openarray[byte],
memPos, dataPos, len: Natural,
paddingValue = 0.byte) =
mem.extend(memPos, len)
let dataEndPosition = dataPos.int64 + len - 1
let sourceBytes =
data[min(dataPos, data.len) .. min(data.len - 1, dataEndPosition)]
mem.write(memPos, sourceBytes)
# Don't duplicate zero-padding of mem.extend
let paddingOffset = min(memPos + sourceBytes.len, mem.len)
let numPaddingBytes = min(mem.len - paddingOffset, len - sourceBytes.len)
if numPaddingBytes > 0:
# TODO: avoid unnecessary memory allocation
mem.write(paddingOffset, repeat(paddingValue, numPaddingBytes))
# ------------------------------------------------------------------------------
# Private, op handlers implementation
# ------------------------------------------------------------------------------
const
addressOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x30, Get address of currently executing account.
k.cpt.stack.push:
k.cpt.msg.contractAddress
balanceOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x31, Get balance of the given account.
let address = k.cpt.stack.popAddress
k.cpt.stack.push:
k.cpt.getBalance(address)
originOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x32, Get execution origination address.
k.cpt.stack.push:
k.cpt.getOrigin()
callerOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x33, Get caller address.
k.cpt.stack.push:
k.cpt.msg.sender
callValueOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x34, Get deposited value by the instruction/transaction
## responsible for this execution
k.cpt.stack.push:
k.cpt.msg.value
callDataLoadOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x35, Get input data of current environment
let (startPos) = k.cpt.stack.popInt(1)
let start = startPos.cleanMemRef
if start >= k.cpt.msg.data.len:
k.cpt.stack.push:
0
return
# If the data does not take 32 bytes, pad with zeros
let endRange = min(k.cpt.msg.data.len - 1, start + 31)
let presentBytes = endRange - start
# We rely on value being initialized with 0 by default
var value: array[32, byte]
value[0 .. presentBytes] = k.cpt.msg.data.toOpenArray(start, endRange)
k.cpt.stack.push:
value
callDataSizeOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x36, Get size of input data in current environment.
k.cpt.stack.push:
k.cpt.msg.data.len.u256
callDataCopyOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x37, Copy input data in current environment to memory.
let (memStartPos, copyStartPos, size) = k.cpt.stack.popInt(3)
# TODO tests: https://github.com/status-im/nimbus/issues/67
let (memPos, copyPos, len) =
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
when not breakCircularDependency:
k.cpt.gasMeter.consumeGas(
k.cpt.gasCosts[CallDataCopy].m_handler(k.cpt.memory.len, memPos, len),
reason = "CallDataCopy fee")
k.cpt.memory.writePaddedResult(k.cpt.msg.data, memPos, copyPos, len)
codeSizeOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x38, Get size of code running in current environment.
k.cpt.stack.push:
k.cpt.code.len
codeCopyOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x39, Copy code running in current environment to memory.
let (memStartPos, copyStartPos, size) = k.cpt.stack.popInt(3)
# TODO tests: https://github.com/status-im/nimbus/issues/67
let (memPos, copyPos, len) =
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
when not breakCircularDependency:
k.cpt.gasMeter.consumeGas(
k.cpt.gasCosts[CodeCopy].m_handler(k.cpt.memory.len, memPos, len),
reason = "CodeCopy fee")
k.cpt.memory.writePaddedResult(k.cpt.code.bytes, memPos, copyPos, len)
gasPriceOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x3A, Get price of gas in current environment.
k.cpt.stack.push:
k.cpt.getGasPrice
extCodeSizeOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x3b, Get size of an account's code
let address = k.cpt.stack.popAddress()
k.cpt.stack.push:
k.cpt.getCodeSize(address)
extCodeCopyOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x3c, Copy an account's code to memory.
let address = k.cpt.stack.popAddress()
let (memStartPos, codeStartPos, size) = k.cpt.stack.popInt(3)
let (memPos, codePos, len) =
(memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef)
when not breakCircularDependency:
k.cpt.gasMeter.consumeGas(
k.cpt.gasCosts[ExtCodeCopy].m_handler(k.cpt.memory.len, memPos, len),
reason = "ExtCodeCopy fee")
let codeBytes = k.cpt.getCode(address)
k.cpt.memory.writePaddedResult(codeBytes, memPos, codePos, len)
returnDataSizeOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x3d, Get size of output data from the previous call from the
## current environment.
k.cpt.stack.push:
k.cpt.returnData.len
returnDataCopyOp: Vm2OpFn = proc (k: Vm2Ctx) =
## 0x3e, Copy output data from the previous call to memory.
let (memStartPos, copyStartPos, size) = k.cpt.stack.popInt(3)
let (memPos, copyPos, len) =
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
when not breakCircularDependency:
let gasCost = k.cpt.gasCosts[ReturnDataCopy].m_handler(
k.cpt.memory.len, memPos, len)
k.cpt.gasMeter.consumeGas(gasCost, reason = "returnDataCopy fee")
if copyPos + len > k.cpt.returnData.len:
raise newException(
OutOfBoundsRead,
"Return data length is not sufficient to satisfy request. Asked\n"&
&"for data from index {copyStartPos} to {copyStartPos + size}. "&
&"Return data is {k.cpt.returnData.len} in \n" &
"length")
k.cpt.memory.writePaddedResult(k.cpt.returnData, memPos, copyPos, len)
# ------------------------------------------------------------------------------
# Public, op exec table entries
# ------------------------------------------------------------------------------
const
vm2OpExecEnvInfo*: seq[Vm2OpExec] = @[
(opCode: Address, ## 0x20, Address
forks: Vm2OpAllForks,
info: "Get address of currently executing account",
exec: (prep: vm2OpIgnore,
run: addressOp,
post: vm2OpIgnore)),
(opCode: Balance, ## 0x31, Balance
forks: Vm2OpAllForks,
info: "Get balance of the given account",
exec: (prep: vm2OpIgnore,
run: balanceOp,
post: vm2OpIgnore)),
(opCode: Origin, ## 0x32, Origination address
forks: Vm2OpAllForks,
info: "Get execution origination address",
exec: (prep: vm2OpIgnore,
run: originOp,
post: vm2OpIgnore)),
(opCode: Caller, ## 0x33, Caller address
forks: Vm2OpAllForks,
info: "Get caller address",
exec: (prep: vm2OpIgnore,
run: callerOp,
post: vm2OpIgnore)),
(opCode: CallValue, ## 0x34, Execution deposited value
forks: Vm2OpAllForks,
info: "Get deposited value by the instruction/transaction " &
"responsible for this execution",
exec: (prep: vm2OpIgnore,
run: callValueOp,
post: vm2OpIgnore)),
(opCode: CallDataLoad, ## 0x35, Input data
forks: Vm2OpAllForks,
info: "Get input data of current environment",
exec: (prep: vm2OpIgnore,
run: callDataLoadOp,
post: vm2OpIgnore)),
(opCode: CallDataSize, ## 0x36, Size of input data
forks: Vm2OpAllForks,
info: "Get size of input data in current environment",
exec: (prep: vm2OpIgnore,
run: callDataSizeOp,
post: vm2OpIgnore)),
(opCode: CallDataCopy, ## 0x37, Copy input data to memory.
forks: Vm2OpAllForks,
info: "Copy input data in current environment to memory",
exec: (prep: vm2OpIgnore,
run: callDataCopyOp,
post: vm2OpIgnore)),
(opCode: CodeSize, ## 0x38, Size of code
forks: Vm2OpAllForks,
info: "Get size of code running in current environment",
exec: (prep: vm2OpIgnore,
run: codeSizeOp,
post: vm2OpIgnore)),
(opCode: CodeCopy, ## 0x39, Copy code to memory.
forks: Vm2OpAllForks,
info: "Copy code running in current environment to memory",
exec: (prep: vm2OpIgnore,
run: codeCopyOp,
post: vm2OpIgnore)),
(opCode: GasPrice, ## 0x3a, Gas price
forks: Vm2OpAllForks,
info: "Get price of gas in current environment",
exec: (prep: vm2OpIgnore,
run: gasPriceOp,
post: vm2OpIgnore)),
(opCode: ExtCodeSize, ## 0x3b, Account code size
forks: Vm2OpAllForks,
info: "Get size of an account's code",
exec: (prep: vm2OpIgnore,
run: extCodeSizeOp,
post: vm2OpIgnore)),
(opCode: ExtCodeCopy, ## 0x3c, Account code copy to memory.
forks: Vm2OpAllForks,
info: "Copy an account's code to memory",
exec: (prep: vm2OpIgnore,
run: extCodeCopyOp,
post: vm2OpIgnore)),
(opCode: ReturnDataSize, ## 0x3d, Previous call output data size
forks: Vm2OpAllForks,
info: "Get size of output data from the previous call " &
"from the current environment",
exec: (prep: vm2OpIgnore,
run: returnDataSizeOp,
post: vm2OpIgnore)),
(opCode: ReturnDataCopy, ## 0x3e, Previous call output data copy to memory
forks: Vm2OpAllForks,
info: "Copy output data from the previous call to memory",
exec: (prep: vm2OpIgnore,
run: returnDataCopyOp,
post: vm2OpIgnore))]
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------

View File

@ -38,6 +38,26 @@ template push(x: typed) {.dirty.} =
## Push an expression on the computation stack
c.stack.push x
proc writePaddedResult(mem: var Memory,
data: openarray[byte],
memPos, dataPos, len: Natural,
paddingValue = 0.byte) =
mem.extend(memPos, len)
let dataEndPosition = dataPos.int64 + len - 1
let sourceBytes = data[min(dataPos, data.len) .. min(data.len - 1, dataEndPosition)]
mem.write(memPos, sourceBytes)
# Don't duplicate zero-padding of mem.extend
let paddingOffset = min(memPos + sourceBytes.len, mem.len)
let numPaddingBytes = min(mem.len - paddingOffset, len - sourceBytes.len)
if numPaddingBytes > 0:
# TODO: avoid unnecessary memory allocation
mem.write(paddingOffset, repeat(paddingValue, numPaddingBytes))
# ##################################
# re-implemented OP handlers
var gdbBPHook_counter = 0
proc gdbBPHook*() =
gdbBPHook_counter.inc
@ -57,175 +77,44 @@ template opHandler(callName: untyped; opCode: Op) =
desc.cpt = c
vm2OpTabBerlin[opCode].exec.run(desc)
# ##################################
# 0s: Stop and Arithmetic Operations
opHandler add, Op.Add
opHandler mul, Op.Mul
opHandler sub, Op.Sub
opHandler divide, Op.Div
opHandler sdiv, Op.Sdiv
opHandler modulo, Op.Mod
opHandler smod, Op.Smod
opHandler addmod, Op.AddMod
opHandler mulmod, Op.MulMod
opHandler exp, Op.Exp
opHandler signExtend, Op.SignExtend
# ##########################################
# 10s: Comparison & Bitwise Logic Operations
opHandler lt, Op.Lt
opHandler gt, Op.Gt
opHandler slt, Op.Slt
opHandler sgt, Op.Sgt
opHandler eq, Op.Eq
opHandler isZero, Op.IsZero
opHandler andOp, Op.And
opHandler orOp, Op.Or
opHandler xorOp, Op.Xor
opHandler notOp, Op.Not
opHandler byteOp, Op.Byte
# ##########################################
# 20s: SHA3
opHandler sha3, Op.Sha3
# ##########################################
# 30s: Environmental Information
proc writePaddedResult(mem: var Memory,
data: openarray[byte],
memPos, dataPos, len: Natural,
paddingValue = 0.byte) =
mem.extend(memPos, len)
let dataEndPosition = dataPos.int64 + len - 1
let sourceBytes = data[min(dataPos, data.len) .. min(data.len - 1, dataEndPosition)]
mem.write(memPos, sourceBytes)
# Don't duplicate zero-padding of mem.extend
let paddingOffset = min(memPos + sourceBytes.len, mem.len)
let numPaddingBytes = min(mem.len - paddingOffset, len - sourceBytes.len)
if numPaddingBytes > 0:
# TODO: avoid unnecessary memory allocation
mem.write(paddingOffset, repeat(paddingValue, numPaddingBytes))
op address, inline = true:
## 0x30, Get address of currently executing account.
push: c.msg.contractAddress
op balance, inline = true:
## 0x31, Get balance of the given account.
let address = c.stack.popAddress()
push: c.getBalance(address)
op origin, inline = true:
## 0x32, Get execution origination address.
push: c.getOrigin()
op caller, inline = true:
## 0x33, Get caller address.
push: c.msg.sender
op callValue, inline = true:
## 0x34, Get deposited value by the instruction/transaction
## responsible for this execution
push: c.msg.value
op callDataLoad, inline = false, startPos:
## 0x35, Get input data of current environment
let start = startPos.cleanMemRef
if start >= c.msg.data.len:
push: 0
return
# If the data does not take 32 bytes, pad with zeros
let endRange = min(c.msg.data.len - 1, start + 31)
let presentBytes = endRange - start
# We rely on value being initialized with 0 by default
var value: array[32, byte]
value[0 .. presentBytes] = c.msg.data.toOpenArray(start, endRange)
push: value
op callDataSize, inline = true:
## 0x36, Get size of input data in current environment.
push: c.msg.data.len.u256
op callDataCopy, inline = false, memStartPos, copyStartPos, size:
## 0x37, Copy input data in current environment to memory.
# TODO tests: https://github.com/status-im/nimbus/issues/67
let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
c.gasMeter.consumeGas(
c.gasCosts[CallDataCopy].m_handler(c.memory.len, memPos, len),
reason="CallDataCopy fee")
c.memory.writePaddedResult(c.msg.data, memPos, copyPos, len)
op codeSize, inline = true:
## 0x38, Get size of code running in current environment.
push: c.code.len
op codeCopy, inline = false, memStartPos, copyStartPos, size:
## 0x39, Copy code running in current environment to memory.
# TODO tests: https://github.com/status-im/nimbus/issues/67
let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
c.gasMeter.consumeGas(
c.gasCosts[CodeCopy].m_handler(c.memory.len, memPos, len),
reason="CodeCopy fee")
c.memory.writePaddedResult(c.code.bytes, memPos, copyPos, len)
op gasprice, inline = true:
## 0x3A, Get price of gas in current environment.
push: c.getGasPrice()
op extCodeSize, inline = true:
## 0x3b, Get size of an account's code
let address = c.stack.popAddress()
push: c.getCodeSize(address)
op extCodeCopy, inline = true:
## 0x3c, Copy an account's code to memory.
let address = c.stack.popAddress()
let (memStartPos, codeStartPos, size) = c.stack.popInt(3)
let (memPos, codePos, len) = (memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef)
c.gasMeter.consumeGas(
c.gasCosts[ExtCodeCopy].m_handler(c.memory.len, memPos, len),
reason="ExtCodeCopy fee")
let codeBytes = c.getCode(address)
c.memory.writePaddedResult(codeBytes, memPos, codePos, len)
op returnDataSize, inline = true:
## 0x3d, Get size of output data from the previous call from the current environment.
push: c.returnData.len
op returnDataCopy, inline = false, memStartPos, copyStartPos, size:
## 0x3e, Copy output data from the previous call to memory.
let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
let gasCost = c.gasCosts[ReturnDataCopy].m_handler(c.memory.len, memPos, len)
c.gasMeter.consumeGas(
gasCost,
reason="returnDataCopy fee")
if copyPos + len > c.returnData.len:
# TODO Geth additionally checks copyPos + len < 64
# Parity uses a saturating addition
# Yellow paper mentions μs[1] + i are not subject to the 2^256 modulo.
raise newException(OutOfBoundsRead,
"Return data length is not sufficient to satisfy request. Asked \n" &
&"for data from index {copyStartPos} to {copyStartPos + size}. Return data is {c.returnData.len} in \n" &
"length")
c.memory.writePaddedResult(c.returnData, memPos, copyPos, len)
opHandler add, Op.Add
opHandler mul, Op.Mul
opHandler sub, Op.Sub
opHandler divide, Op.Div
opHandler sdiv, Op.Sdiv
opHandler modulo, Op.Mod
opHandler smod, Op.Smod
opHandler addmod, Op.AddMod
opHandler mulmod, Op.MulMod
opHandler exp, Op.Exp
opHandler signExtend, Op.SignExtend
opHandler lt, Op.Lt
opHandler gt, Op.Gt
opHandler slt, Op.Slt
opHandler sgt, Op.Sgt
opHandler eq, Op.Eq
opHandler isZero, Op.IsZero
opHandler andOp, Op.And
opHandler orOp, Op.Or
opHandler xorOp, Op.Xor
opHandler notOp, Op.Not
opHandler byteOp, Op.Byte
opHandler sha3, Op.Sha3
opHandler address, Op.Address
opHandler balance, Op.Balance
opHandler origin, Op.Origin
opHandler caller, Op.Caller
opHandler callValue, Op.CallValue
opHandler callDataLoad, Op.CallDataLoad
opHandler callDataSize, Op.CallDataSize
opHandler callDataCopy, Op.CallDataCopy
opHandler codeSize, Op.CodeSize
opHandler codeCopy, Op.CodeCopy
opHandler gasprice, Op.GasPrice
opHandler extCodeSize, Op.ExtCodeSize
opHandler extCodeCopy, Op.ExtCodeCopy
opHandler returnDataSize, Op.ReturnDataSize
opHandler returnDataCopy, Op.ReturnDataCopy
# ##########################################
# 40s: Block Information