change 'BaseComputation' to 'Computation'

This commit is contained in:
andri lim 2020-01-10 08:26:17 +07:00 committed by zah
parent 79df931234
commit 0b99b76cd1
12 changed files with 440 additions and 446 deletions

View File

@ -35,7 +35,7 @@ template balance(addressDb: ReadOnlyStateDb, address: EthAddress): GasInt =
addressDb.getBalance(address).truncate(int64) addressDb.getBalance(address).truncate(int64)
proc binarySearchGas(vmState: var BaseVMState, transaction: Transaction, sender: EthAddress, gasPrice: GasInt, tolerance = 1): GasInt = proc binarySearchGas(vmState: var BaseVMState, transaction: Transaction, sender: EthAddress, gasPrice: GasInt, tolerance = 1): GasInt =
proc dummyComputation(vmState: var BaseVMState, transaction: Transaction, sender: EthAddress): BaseComputation = proc dummyComputation(vmState: var BaseVMState, transaction: Transaction, sender: EthAddress): Computation =
let recipient = transaction.getRecipient() let recipient = transaction.getRecipient()
# Note that vmState may be altered # Note that vmState may be altered
setupComputation( setupComputation(
@ -276,7 +276,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
value: UInt256, data: seq[byte], value: UInt256, data: seq[byte],
sender, destination: EthAddress, sender, destination: EthAddress,
gasLimit, gasPrice: GasInt, gasLimit, gasPrice: GasInt,
contractCreation: bool): BaseComputation = contractCreation: bool): Computation =
let let
# Handle optional defaults. # Handle optional defaults.
message = Message( message = Message(
@ -292,7 +292,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
data: data, data: data,
code: vmState.readOnlyStateDB.getCode(destination).toSeq code: vmState.readOnlyStateDB.getCode(destination).toSeq
) )
result = newBaseComputation(vmState, message) result = newComputation(vmState, message)
rpcsrv.rpc("eth_call") do(call: EthCall, quantityTag: string) -> HexDataStr: rpcsrv.rpc("eth_call") do(call: EthCall, quantityTag: string) -> HexDataStr:
## Executes a new message call immediately without creating a transaction on the block chain. ## Executes a new message call immediately without creating a transaction on the block chain.

View File

@ -17,7 +17,7 @@ import
logScope: logScope:
topics = "vm computation" topics = "vm computation"
proc newBaseComputation*(vmState: BaseVMState, message: Message, forkOverride=none(Fork)): BaseComputation = proc newComputation*(vmState: BaseVMState, message: Message, forkOverride=none(Fork)): Computation =
new result new result
result.vmState = vmState result.vmState = vmState
result.msg = message result.msg = message
@ -37,213 +37,213 @@ proc newBaseComputation*(vmState: BaseVMState, message: Message, forkOverride=no
result.nextProc = proc() = result.nextProc = proc() =
discard discard
proc isOriginComputation*(c: BaseComputation): bool = proc isOriginComputation*(c: Computation): bool =
# Is this computation the computation initiated by a transaction # Is this computation the computation initiated by a transaction
c.msg.isOrigin c.msg.isOrigin
template isSuccess*(c: BaseComputation): bool = template isSuccess*(c: Computation): bool =
c.error.isNil c.error.isNil
template isError*(c: BaseComputation): bool = template isError*(c: Computation): bool =
not c.isSuccess not c.isSuccess
func shouldBurnGas*(c: BaseComputation): bool = func shouldBurnGas*(c: Computation): bool =
c.isError and c.error.burnsGas c.isError and c.error.burnsGas
func shouldEraseReturnData*(c: BaseComputation): bool = func shouldEraseReturnData*(c: Computation): bool =
c.isError and c.error.erasesReturnData c.isError and c.error.erasesReturnData
func bytesToHex(x: openarray[byte]): string {.inline.} = func bytesToHex(x: openarray[byte]): string {.inline.} =
## TODO: use seq[byte] for raw data and delete this proc ## TODO: use seq[byte] for raw data and delete this proc
foldl(x, a & b.int.toHex(2).toLowerAscii, "0x") foldl(x, a & b.int.toHex(2).toLowerAscii, "0x")
func output*(c: BaseComputation): seq[byte] = func output*(c: Computation): seq[byte] =
if c.shouldEraseReturnData: if c.shouldEraseReturnData:
@[] @[]
else: else:
c.rawOutput c.rawOutput
func `output=`*(c: BaseComputation, value: openarray[byte]) = func `output=`*(c: Computation, value: openarray[byte]) =
c.rawOutput = @value c.rawOutput = @value
proc outputHex*(c: BaseComputation): string = proc outputHex*(c: Computation): string =
if c.shouldEraseReturnData: if c.shouldEraseReturnData:
return "0x" return "0x"
c.rawOutput.bytesToHex c.rawOutput.bytesToHex
proc isSuicided*(c: BaseComputation, address: EthAddress): bool = proc isSuicided*(c: Computation, address: EthAddress): bool =
result = address in c.suicides result = address in c.suicides
proc snapshot*(comp: BaseComputation) = proc snapshot*(c: Computation) =
comp.dbsnapshot.transaction = comp.vmState.chaindb.db.beginTransaction() c.dbsnapshot.transaction = c.vmState.chaindb.db.beginTransaction()
comp.dbsnapshot.intermediateRoot = comp.vmState.accountDb.rootHash c.dbsnapshot.intermediateRoot = c.vmState.accountDb.rootHash
proc commit*(comp: BaseComputation) = proc commit*(c: Computation) =
comp.dbsnapshot.transaction.commit() c.dbsnapshot.transaction.commit()
proc dispose*(comp: BaseComputation) {.inline.} = proc dispose*(c: Computation) {.inline.} =
comp.dbsnapshot.transaction.dispose() c.dbsnapshot.transaction.dispose()
proc rollback*(comp: BaseComputation) = proc rollback*(c: Computation) =
comp.dbsnapshot.transaction.rollback() c.dbsnapshot.transaction.rollback()
comp.vmState.accountDb.rootHash = comp.dbsnapshot.intermediateRoot c.vmState.accountDb.rootHash = c.dbsnapshot.intermediateRoot
proc setError*(comp: BaseComputation, msg: string, burnsGas = false) {.inline.} = proc setError*(c: Computation, msg: string, burnsGas = false) {.inline.} =
comp.error = Error(info: msg, burnsGas: burnsGas) c.error = Error(info: msg, burnsGas: burnsGas)
proc writeContract*(computation: BaseComputation, fork: Fork): bool {.gcsafe.} = proc writeContract*(c: Computation, fork: Fork): bool {.gcsafe.} =
result = true result = true
let contractCode = computation.output let contractCode = c.output
if contractCode.len == 0: return if contractCode.len == 0: return
if fork >= FkSpurious and contractCode.len >= EIP170_CODE_SIZE_LIMIT: if fork >= FkSpurious and contractCode.len >= EIP170_CODE_SIZE_LIMIT:
debug "Contract code size exceeds EIP170", limit=EIP170_CODE_SIZE_LIMIT, actual=contractCode.len debug "Contract code size exceeds EIP170", limit=EIP170_CODE_SIZE_LIMIT, actual=contractCode.len
return false return false
let storageAddr = computation.msg.contractAddress let storageAddr = c.msg.contractAddress
if computation.isSuicided(storageAddr): return if c.isSuicided(storageAddr): return
let gasParams = GasParams(kind: Create, cr_memLength: contractCode.len) let gasParams = GasParams(kind: Create, cr_memLength: contractCode.len)
let codeCost = computation.gasCosts[Create].c_handler(0.u256, gasParams).gasCost let codeCost = c.gasCosts[Create].c_handler(0.u256, gasParams).gasCost
if computation.gasMeter.gasRemaining >= codeCost: if c.gasMeter.gasRemaining >= codeCost:
computation.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE") c.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE")
computation.vmState.mutateStateDb: c.vmState.mutateStateDb:
db.setCode(storageAddr, contractCode.toRange) db.setCode(storageAddr, contractCode.toRange)
result = true result = true
else: else:
if fork < FkHomestead or fork >= FkByzantium: computation.output = @[] if fork < FkHomestead or fork >= FkByzantium: c.output = @[]
result = false result = false
proc transferBalance(computation: BaseComputation, opCode: static[Op]) = proc transferBalance(c: Computation, opCode: static[Op]) =
let senderBalance = computation.vmState.readOnlyStateDb(). let senderBalance = c.vmState.readOnlyStateDb().
getBalance(computation.msg.sender) getBalance(c.msg.sender)
if senderBalance < computation.msg.value: if senderBalance < c.msg.value:
computation.setError(&"insufficient funds available={senderBalance}, needed={computation.msg.value}") c.setError(&"insufficient funds available={senderBalance}, needed={c.msg.value}")
return return
when opCode in {Call, Create}: when opCode in {Call, Create}:
computation.vmState.mutateStateDb: c.vmState.mutateStateDb:
db.subBalance(computation.msg.sender, computation.msg.value) db.subBalance(c.msg.sender, c.msg.value)
db.addBalance(computation.msg.contractAddress, computation.msg.value) db.addBalance(c.msg.contractAddress, c.msg.value)
template continuation*(comp: BaseComputation, body: untyped) = template continuation*(c: Computation, body: untyped) =
# this is a helper template to implement continuation # this is a helper template to implement continuation
# passing and convert all recursion into tail call # passing and convert all recursion into tail call
var tmpNext = comp.nextProc var tmpNext = c.nextProc
comp.nextProc = proc() {.gcsafe.} = c.nextProc = proc() {.gcsafe.} =
body body
tmpNext() tmpNext()
proc initAddress(x: int): EthAddress {.compileTime.} = result[19] = x.byte proc initAddress(x: int): EthAddress {.compileTime.} = result[19] = x.byte
const ripemdAddr = initAddress(3) const ripemdAddr = initAddress(3)
proc postExecuteVM(computation: BaseComputation, opCode: static[Op]) {.gcsafe.} = proc postExecuteVM(c: Computation, opCode: static[Op]) {.gcsafe.} =
when opCode == Create: when opCode == Create:
if computation.isSuccess: if c.isSuccess:
let fork = computation.fork let fork = c.fork
let contractFailed = not computation.writeContract(fork) let contractFailed = not c.writeContract(fork)
if contractFailed and fork >= FkHomestead: if contractFailed and fork >= FkHomestead:
computation.setError(&"writeContract failed, depth={computation.msg.depth}", true) c.setError(&"writeContract failed, depth={c.msg.depth}", true)
if computation.isSuccess: if c.isSuccess:
computation.commit() c.commit()
else: else:
computation.rollback() c.rollback()
proc executeOpcodes*(computation: BaseComputation) {.gcsafe.} proc executeOpcodes*(c: Computation) {.gcsafe.}
proc applyMessage*(computation: BaseComputation, opCode: static[Op]) = proc applyMessage*(c: Computation, opCode: static[Op]) =
if computation.msg.depth > MaxCallDepth: if c.msg.depth > MaxCallDepth:
computation.setError(&"Stack depth limit reached depth={computation.msg.depth}") c.setError(&"Stack depth limit reached depth={c.msg.depth}")
computation.nextProc() c.nextProc()
return return
computation.snapshot() c.snapshot()
defer: defer:
computation.dispose() c.dispose()
# EIP161 nonce incrementation # EIP161 nonce incrementation
when opCode in {Create, Create2}: when opCode in {Create, Create2}:
if computation.fork >= FkSpurious: if c.fork >= FkSpurious:
computation.vmState.mutateStateDb: c.vmState.mutateStateDb:
db.incNonce(computation.msg.contractAddress) db.incNonce(c.msg.contractAddress)
if computation.fork >= FkByzantium: if c.fork >= FkByzantium:
# RevertInCreateInInit.json # RevertInCreateInInit.json
db.setStorageRoot(computation.msg.contractAddress, emptyRlpHash) db.setStorageRoot(c.msg.contractAddress, emptyRlpHash)
when opCode in {CallCode, Call, Create}: when opCode in {CallCode, Call, Create}:
computation.transferBalance(opCode) c.transferBalance(opCode)
if computation.isError(): if c.isError():
computation.rollback() c.rollback()
computation.nextProc() c.nextProc()
return return
if computation.gasMeter.gasRemaining < 0: if c.gasMeter.gasRemaining < 0:
computation.commit() c.commit()
computation.nextProc() c.nextProc()
return return
continuation(computation): continuation(c):
postExecuteVM(computation, opCode) postExecuteVM(c, opCode)
executeOpcodes(computation) executeOpcodes(c)
proc addChildComputation*(computation: BaseComputation, child: BaseComputation) = proc addChildComputation*(c, child: Computation) =
if child.isError or computation.fork == FKIstanbul: if child.isError or c.fork == FKIstanbul:
if not child.msg.isCreate: if not child.msg.isCreate:
if child.msg.contractAddress == ripemdAddr: if child.msg.contractAddress == ripemdAddr:
child.vmState.touchedAccounts.incl child.msg.contractAddress child.vmState.touchedAccounts.incl child.msg.contractAddress
if child.isError: if child.isError:
if child.shouldBurnGas: if child.shouldBurnGas:
computation.returnData = @[] c.returnData = @[]
else: else:
computation.returnData = child.output c.returnData = child.output
else: else:
if child.msg.isCreate: if child.msg.isCreate:
computation.returnData = @[] c.returnData = @[]
else: else:
computation.returnData = child.output c.returnData = child.output
child.touchedAccounts.incl child.msg.contractAddress child.touchedAccounts.incl child.msg.contractAddress
computation.logEntries.add child.logEntries c.logEntries.add child.logEntries
computation.gasMeter.refundGas(child.gasMeter.gasRefunded) c.gasMeter.refundGas(child.gasMeter.gasRefunded)
computation.suicides.incl child.suicides c.suicides.incl child.suicides
computation.touchedAccounts.incl child.touchedAccounts c.touchedAccounts.incl child.touchedAccounts
if not child.shouldBurnGas: if not child.shouldBurnGas:
computation.gasMeter.returnGas(child.gasMeter.gasRemaining) c.gasMeter.returnGas(child.gasMeter.gasRemaining)
proc registerAccountForDeletion*(c: BaseComputation, beneficiary: EthAddress) = proc registerAccountForDeletion*(c: Computation, beneficiary: EthAddress) =
c.touchedAccounts.incl beneficiary c.touchedAccounts.incl beneficiary
c.suicides.incl(c.msg.contractAddress) c.suicides.incl(c.msg.contractAddress)
proc addLogEntry*(c: BaseComputation, log: Log) {.inline.} = proc addLogEntry*(c: Computation, log: Log) {.inline.} =
c.logEntries.add(log) c.logEntries.add(log)
iterator accountsForDeletion*(c: BaseComputation): EthAddress = iterator accountsForDeletion*(c: Computation): EthAddress =
if c.isSuccess: if c.isSuccess:
for address in c.suicides: for address in c.suicides:
yield address yield address
proc getGasRefund*(c: BaseComputation): GasInt = proc getGasRefund*(c: Computation): GasInt =
if c.isSuccess: if c.isSuccess:
result = c.gasMeter.gasRefunded result = c.gasMeter.gasRefunded
proc getGasUsed*(c: BaseComputation): GasInt = proc getGasUsed*(c: Computation): GasInt =
if c.shouldBurnGas: if c.shouldBurnGas:
result = c.msg.gas result = c.msg.gas
else: else:
result = max(0, c.msg.gas - c.gasMeter.gasRemaining) result = max(0, c.msg.gas - c.gasMeter.gasRemaining)
proc getGasRemaining*(c: BaseComputation): GasInt = proc getGasRemaining*(c: Computation): GasInt =
if c.shouldBurnGas: if c.shouldBurnGas:
result = 0 result = 0
else: else:
result = c.gasMeter.gasRemaining result = c.gasMeter.gasRemaining
proc collectTouchedAccounts*(c: BaseComputation) = proc collectTouchedAccounts*(c: Computation) =
## Collect all of the accounts that *may* need to be deleted based on EIP161: ## Collect all of the accounts that *may* need to be deleted based on EIP161:
## https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md ## https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md
## also see: https://github.com/ethereum/EIPs/issues/716 ## also see: https://github.com/ethereum/EIPs/issues/716
@ -259,19 +259,19 @@ proc collectTouchedAccounts*(c: BaseComputation) =
if c.msg.contractAddress == ripemdAddr: if c.msg.contractAddress == ripemdAddr:
c.vmState.touchedAccounts.incl c.msg.contractAddress c.vmState.touchedAccounts.incl c.msg.contractAddress
proc tracingEnabled*(c: BaseComputation): bool = proc tracingEnabled*(c: Computation): bool =
c.vmState.tracingEnabled c.vmState.tracingEnabled
proc traceOpCodeStarted*(c: BaseComputation, op: Op): int = proc traceOpCodeStarted*(c: Computation, op: Op): int =
c.vmState.tracer.traceOpCodeStarted(c, op) c.vmState.tracer.traceOpCodeStarted(c, op)
proc traceOpCodeEnded*(c: BaseComputation, op: Op, lastIndex: int) = proc traceOpCodeEnded*(c: Computation, op: Op, lastIndex: int) =
c.vmState.tracer.traceOpCodeEnded(c, op, lastIndex) c.vmState.tracer.traceOpCodeEnded(c, op, lastIndex)
proc traceError*(c: BaseComputation) = proc traceError*(c: Computation) =
c.vmState.tracer.traceError(c) c.vmState.tracer.traceError(c)
proc prepareTracer*(c: BaseComputation) = proc prepareTracer*(c: Computation) =
c.vmState.tracer.prepare(c.msg.depth) c.vmState.tracer.prepare(c.msg.depth)
include interpreter_dispatch include interpreter_dispatch

View File

@ -22,7 +22,7 @@ logScope:
template push(x: typed) {.dirty.} = template push(x: typed) {.dirty.} =
## Push an expression on the computation stack ## Push an expression on the computation stack
computation.stack.push x c.stack.push x
# ################################## # ##################################
# 0s: Stop and Arithmetic Operations # 0s: Stop and Arithmetic Operations
@ -94,8 +94,8 @@ op mulmod, inline = true, lhs, rhs, modulus:
op exp, inline = true, base, exponent: op exp, inline = true, base, exponent:
## 0x0A, Exponentiation ## 0x0A, Exponentiation
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[Exp].d_handler(exponent), c.gasCosts[Exp].d_handler(exponent),
reason="EXP: exponent bytes" reason="EXP: exponent bytes"
) )
push: push:
@ -196,18 +196,18 @@ op sha3, inline = true, startPos, length:
if pos < 0 or len < 0 or pos > 2147483648: if pos < 0 or len < 0 or pos > 2147483648:
raise newException(OutOfBoundsRead, "Out of bounds memory access") raise newException(OutOfBoundsRead, "Out of bounds memory access")
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[Op.Sha3].m_handler(computation.memory.len, pos, len), c.gasCosts[Op.Sha3].m_handler(c.memory.len, pos, len),
reason="SHA3: word gas cost" reason="SHA3: word gas cost"
) )
computation.memory.extend(pos, len) c.memory.extend(pos, len)
let endRange = min(pos + len, computation.memory.len) - 1 let endRange = min(pos + len, c.memory.len) - 1
if endRange == -1 or pos >= computation.memory.len: if endRange == -1 or pos >= c.memory.len:
push(EMPTY_SHA3) push(EMPTY_SHA3)
else: else:
push: push:
keccak256.digest computation.memory.bytes.toOpenArray(pos, endRange) keccak256.digest c.memory.bytes.toOpenArray(pos, endRange)
# ########################################## # ##########################################
# 30s: Environmental Information # 30s: Environmental Information
@ -231,45 +231,45 @@ proc writePaddedResult(mem: var Memory,
op address, inline = true: op address, inline = true:
## 0x30, Get address of currently executing account. ## 0x30, Get address of currently executing account.
push: computation.msg.contractAddress push: c.msg.contractAddress
op balance, inline = true: op balance, inline = true:
## 0x31, Get balance of the given account. ## 0x31, Get balance of the given account.
let address = computation.stack.popAddress() let address = c.stack.popAddress()
push: computation.vmState.readOnlyStateDB.getBalance(address) push: c.vmState.readOnlyStateDB.getBalance(address)
op origin, inline = true: op origin, inline = true:
## 0x32, Get execution origination address. ## 0x32, Get execution origination address.
push: computation.msg.origin push: c.msg.origin
op caller, inline = true: op caller, inline = true:
## 0x33, Get caller address. ## 0x33, Get caller address.
push: computation.msg.sender push: c.msg.sender
op callValue, inline = true: op callValue, inline = true:
## 0x34, Get deposited value by the instruction/transaction ## 0x34, Get deposited value by the instruction/transaction
## responsible for this execution ## responsible for this execution
push: computation.msg.value push: c.msg.value
op callDataLoad, inline = false, startPos: op callDataLoad, inline = false, startPos:
## 0x35, Get input data of current environment ## 0x35, Get input data of current environment
let start = startPos.cleanMemRef let start = startPos.cleanMemRef
if start >= computation.msg.data.len: if start >= c.msg.data.len:
push: 0 push: 0
return return
# If the data does not take 32 bytes, pad with zeros # If the data does not take 32 bytes, pad with zeros
let endRange = min(computation.msg.data.len - 1, start + 31) let endRange = min(c.msg.data.len - 1, start + 31)
let presentBytes = endRange - start let presentBytes = endRange - start
# We rely on value being initialized with 0 by default # We rely on value being initialized with 0 by default
var value: array[32, byte] var value: array[32, byte]
value[0 .. presentBytes] = computation.msg.data.toOpenArray(start, endRange) value[0 .. presentBytes] = c.msg.data.toOpenArray(start, endRange)
push: value push: value
op callDataSize, inline = true: op callDataSize, inline = true:
## 0x36, Get size of input data in current environment. ## 0x36, Get size of input data in current environment.
push: computation.msg.data.len.u256 push: c.msg.data.len.u256
op callDataCopy, inline = false, memStartPos, copyStartPos, size: op callDataCopy, inline = false, memStartPos, copyStartPos, size:
## 0x37, Copy input data in current environment to memory. ## 0x37, Copy input data in current environment to memory.
@ -277,15 +277,15 @@ op callDataCopy, inline = false, memStartPos, copyStartPos, size:
let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef) let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[CallDataCopy].m_handler(computation.memory.len, memPos, len), c.gasCosts[CallDataCopy].m_handler(c.memory.len, memPos, len),
reason="CallDataCopy fee") reason="CallDataCopy fee")
computation.memory.writePaddedResult(computation.msg.data, memPos, copyPos, len) c.memory.writePaddedResult(c.msg.data, memPos, copyPos, len)
op codeSize, inline = true: op codeSize, inline = true:
## 0x38, Get size of code running in current environment. ## 0x38, Get size of code running in current environment.
push: computation.code.len push: c.code.len
op codeCopy, inline = false, memStartPos, copyStartPos, size: op codeCopy, inline = false, memStartPos, copyStartPos, size:
## 0x39, Copy code running in current environment to memory. ## 0x39, Copy code running in current environment to memory.
@ -293,196 +293,196 @@ op codeCopy, inline = false, memStartPos, copyStartPos, size:
let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef) let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[CodeCopy].m_handler(computation.memory.len, memPos, len), c.gasCosts[CodeCopy].m_handler(c.memory.len, memPos, len),
reason="CodeCopy fee") reason="CodeCopy fee")
computation.memory.writePaddedResult(computation.code.bytes, memPos, copyPos, len) c.memory.writePaddedResult(c.code.bytes, memPos, copyPos, len)
op gasprice, inline = true: op gasprice, inline = true:
## 0x3A, Get price of gas in current environment. ## 0x3A, Get price of gas in current environment.
push: computation.msg.gasPrice push: c.msg.gasPrice
op extCodeSize, inline = true: op extCodeSize, inline = true:
## 0x3b, Get size of an account's code ## 0x3b, Get size of an account's code
let account = computation.stack.popAddress() let account = c.stack.popAddress()
let codeSize = computation.vmState.readOnlyStateDB.getCode(account).len let codeSize = c.vmState.readOnlyStateDB.getCode(account).len
push uint(codeSize) push uint(codeSize)
op extCodeCopy, inline = true: op extCodeCopy, inline = true:
## 0x3c, Copy an account's code to memory. ## 0x3c, Copy an account's code to memory.
let account = computation.stack.popAddress() let account = c.stack.popAddress()
let (memStartPos, codeStartPos, size) = computation.stack.popInt(3) let (memStartPos, codeStartPos, size) = c.stack.popInt(3)
let (memPos, codePos, len) = (memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef) let (memPos, codePos, len) = (memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef)
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[ExtCodeCopy].m_handler(computation.memory.len, memPos, len), c.gasCosts[ExtCodeCopy].m_handler(c.memory.len, memPos, len),
reason="ExtCodeCopy fee") reason="ExtCodeCopy fee")
let codeBytes = computation.vmState.readOnlyStateDB.getCode(account) let codeBytes = c.vmState.readOnlyStateDB.getCode(account)
computation.memory.writePaddedResult(codeBytes.toOpenArray, memPos, codePos, len) c.memory.writePaddedResult(codeBytes.toOpenArray, memPos, codePos, len)
op returnDataSize, inline = true: op returnDataSize, inline = true:
## 0x3d, Get size of output data from the previous call from the current environment. ## 0x3d, Get size of output data from the previous call from the current environment.
push: computation.returnData.len push: c.returnData.len
op returnDataCopy, inline = false, memStartPos, copyStartPos, size: op returnDataCopy, inline = false, memStartPos, copyStartPos, size:
## 0x3e, Copy output data from the previous call to memory. ## 0x3e, Copy output data from the previous call to memory.
let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef) let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
let gasCost = computation.gasCosts[ReturnDataCopy].m_handler(computation.memory.len, memPos, len) let gasCost = c.gasCosts[ReturnDataCopy].m_handler(c.memory.len, memPos, len)
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
gasCost, gasCost,
reason="returnDataCopy fee") reason="returnDataCopy fee")
if copyPos + len > computation.returnData.len: if copyPos + len > c.returnData.len:
# TODO Geth additionally checks copyPos + len < 64 # TODO Geth additionally checks copyPos + len < 64
# Parity uses a saturating addition # Parity uses a saturating addition
# Yellow paper mentions μs[1] + i are not subject to the 2^256 modulo. # Yellow paper mentions μs[1] + i are not subject to the 2^256 modulo.
raise newException(OutOfBoundsRead, raise newException(OutOfBoundsRead,
"Return data length is not sufficient to satisfy request. Asked \n" & "Return data length is not sufficient to satisfy request. Asked \n" &
&"for data from index {copyStartPos} to {copyStartPos + size}. Return data is {computation.returnData.len} in \n" & &"for data from index {copyStartPos} to {copyStartPos + size}. Return data is {c.returnData.len} in \n" &
"length") "length")
computation.memory.writePaddedResult(computation.returnData, memPos, copyPos, len) c.memory.writePaddedResult(c.returnData, memPos, copyPos, len)
# ########################################## # ##########################################
# 40s: Block Information # 40s: Block Information
op blockhash, inline = true, blockNumber: op blockhash, inline = true, blockNumber:
## 0x40, Get the hash of one of the 256 most recent complete blocks. ## 0x40, Get the hash of one of the 256 most recent complete blocks.
push: computation.vmState.getAncestorHash(blockNumber.vmWordToBlockNumber) push: c.vmState.getAncestorHash(blockNumber.vmWordToBlockNumber)
op coinbase, inline = true: op coinbase, inline = true:
## 0x41, Get the block's beneficiary address. ## 0x41, Get the block's beneficiary address.
push: computation.vmState.coinbase push: c.vmState.coinbase
op timestamp, inline = true: op timestamp, inline = true:
## 0x42, Get the block's timestamp. ## 0x42, Get the block's timestamp.
push: computation.vmState.timestamp.toUnix push: c.vmState.timestamp.toUnix
op blocknumber, inline = true: op blocknumber, inline = true:
## 0x43, Get the block's number. ## 0x43, Get the block's number.
push: computation.vmState.blockNumber.blockNumberToVmWord push: c.vmState.blockNumber.blockNumberToVmWord
op difficulty, inline = true: op difficulty, inline = true:
## 0x44, Get the block's difficulty ## 0x44, Get the block's difficulty
push: computation.vmState.difficulty push: c.vmState.difficulty
op gasLimit, inline = true: op gasLimit, inline = true:
## 0x45, Get the block's gas limit ## 0x45, Get the block's gas limit
push: computation.vmState.gasLimit push: c.vmState.gasLimit
op chainId, inline = true: op chainId, inline = true:
## 0x46, Get current chains EIP-155 unique identifier. ## 0x46, Get current chains EIP-155 unique identifier.
# TODO: this is a stub # TODO: this is a stub
push: computation.vmState.chaindb.config.chainId push: c.vmState.chaindb.config.chainId
op selfBalance, inline = true: op selfBalance, inline = true:
## 0x47, Get current contract's balance. ## 0x47, Get current contract's balance.
let stateDb = computation.vmState.readOnlyStateDb let stateDb = c.vmState.readOnlyStateDb
push: stateDb.getBalance(computation.msg.contractAddress) push: stateDb.getBalance(c.msg.contractAddress)
# ########################################## # ##########################################
# 50s: Stack, Memory, Storage and Flow Operations # 50s: Stack, Memory, Storage and Flow Operations
op pop, inline = true: op pop, inline = true:
## 0x50, Remove item from stack. ## 0x50, Remove item from stack.
discard computation.stack.popInt() discard c.stack.popInt()
op mload, inline = true, memStartPos: op mload, inline = true, memStartPos:
## 0x51, Load word from memory ## 0x51, Load word from memory
let memPos = memStartPos.cleanMemRef let memPos = memStartPos.cleanMemRef
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[MLoad].m_handler(computation.memory.len, memPos, 32), c.gasCosts[MLoad].m_handler(c.memory.len, memPos, 32),
reason="MLOAD: GasVeryLow + memory expansion" reason="MLOAD: GasVeryLow + memory expansion"
) )
computation.memory.extend(memPos, 32) c.memory.extend(memPos, 32)
push: computation.memory.read(memPos, 32) # TODO, should we convert to native endianness? push: c.memory.read(memPos, 32) # TODO, should we convert to native endianness?
op mstore, inline = true, memStartPos, value: op mstore, inline = true, memStartPos, value:
## 0x52, Save word to memory ## 0x52, Save word to memory
let memPos = memStartPos.cleanMemRef let memPos = memStartPos.cleanMemRef
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[MStore].m_handler(computation.memory.len, memPos, 32), c.gasCosts[MStore].m_handler(c.memory.len, memPos, 32),
reason="MSTORE: GasVeryLow + memory expansion" reason="MSTORE: GasVeryLow + memory expansion"
) )
computation.memory.extend(memPos, 32) c.memory.extend(memPos, 32)
computation.memory.write(memPos, value.toByteArrayBE) # is big-endian correct? Parity/Geth do convert c.memory.write(memPos, value.toByteArrayBE) # is big-endian correct? Parity/Geth do convert
op mstore8, inline = true, memStartPos, value: op mstore8, inline = true, memStartPos, value:
## 0x53, Save byte to memory ## 0x53, Save byte to memory
let memPos = memStartPos.cleanMemRef let memPos = memStartPos.cleanMemRef
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[MStore].m_handler(computation.memory.len, memPos, 1), c.gasCosts[MStore].m_handler(c.memory.len, memPos, 1),
reason="MSTORE8: GasVeryLow + memory expansion" reason="MSTORE8: GasVeryLow + memory expansion"
) )
computation.memory.extend(memPos, 1) c.memory.extend(memPos, 1)
computation.memory.write(memPos, [value.toByteArrayBE[31]]) c.memory.write(memPos, [value.toByteArrayBE[31]])
op sload, inline = true, slot: op sload, inline = true, slot:
## 0x54, Load word from storage. ## 0x54, Load word from storage.
let (value, _) = computation.vmState.readOnlyStateDB.getStorage(computation.msg.contractAddress, slot) let (value, _) = c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot)
push(value) push(value)
op sstore, inline = false, slot, value: op sstore, inline = false, slot, value:
## 0x55, Save word to storage. ## 0x55, Save word to storage.
checkInStaticContext(computation) checkInStaticContext(c)
let (currentValue, existing) = computation.vmState.readOnlyStateDB.getStorage(computation.msg.contractAddress, slot) let (currentValue, existing) = c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot)
let let
gasParam = GasParams(kind: Op.Sstore, s_isStorageEmpty: currentValue.isZero) gasParam = GasParams(kind: Op.Sstore, s_isStorageEmpty: currentValue.isZero)
(gasCost, gasRefund) = computation.gasCosts[Sstore].c_handler(value, gasParam) (gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(value, gasParam)
computation.gasMeter.consumeGas(gasCost, &"SSTORE: {computation.msg.contractAddress}[{slot}] -> {value} ({currentValue})") c.gasMeter.consumeGas(gasCost, &"SSTORE: {c.msg.contractAddress}[{slot}] -> {value} ({currentValue})")
if gasRefund > 0: if gasRefund > 0:
computation.gasMeter.refundGas(gasRefund) c.gasMeter.refundGas(gasRefund)
computation.vmState.mutateStateDB: c.vmState.mutateStateDB:
db.setStorage(computation.msg.contractAddress, slot, value) db.setStorage(c.msg.contractAddress, slot, value)
proc jumpImpl(computation: BaseComputation, jumpTarget: UInt256) = proc jumpImpl(c: Computation, jumpTarget: UInt256) =
if jumpTarget >= computation.code.len.u256: if jumpTarget >= c.code.len.u256:
raise newException(InvalidJumpDestination, "Invalid Jump Destination") raise newException(InvalidJumpDestination, "Invalid Jump Destination")
let jt = jumpTarget.truncate(int) let jt = jumpTarget.truncate(int)
computation.code.pc = jt c.code.pc = jt
let nextOpcode = computation.code.peek let nextOpcode = c.code.peek
if nextOpcode != JUMPDEST: if nextOpcode != JUMPDEST:
raise newException(InvalidJumpDestination, "Invalid Jump Destination") raise newException(InvalidJumpDestination, "Invalid Jump Destination")
# TODO: next check seems redundant # TODO: next check seems redundant
if not computation.code.isValidOpcode(jt): if not c.code.isValidOpcode(jt):
raise newException(InvalidInstruction, "Jump resulted in invalid instruction") raise newException(InvalidInstruction, "Jump resulted in invalid instruction")
op jump, inline = true, jumpTarget: op jump, inline = true, jumpTarget:
## 0x56, Alter the program counter ## 0x56, Alter the program counter
jumpImpl(computation, jumpTarget) jumpImpl(c, jumpTarget)
op jumpI, inline = true, jumpTarget, testedValue: op jumpI, inline = true, jumpTarget, testedValue:
## 0x57, Conditionally alter the program counter. ## 0x57, Conditionally alter the program counter.
if testedValue != 0: if testedValue != 0:
jumpImpl(computation, jumpTarget) jumpImpl(c, jumpTarget)
op pc, inline = true: op pc, inline = true:
## 0x58, Get the value of the program counter prior to the increment corresponding to this instruction. ## 0x58, Get the value of the program counter prior to the increment corresponding to this instruction.
push: max(computation.code.pc - 1, 0) push: max(c.code.pc - 1, 0)
op msize, inline = true: op msize, inline = true:
## 0x59, Get the size of active memory in bytes. ## 0x59, Get the size of active memory in bytes.
push: computation.memory.len push: c.memory.len
op gas, inline = true: op gas, inline = true:
## 0x5a, Get the amount of available gas, including the corresponding reduction for the cost of this instruction. ## 0x5a, Get the amount of available gas, including the corresponding reduction for the cost of this instruction.
push: computation.gasMeter.gasRemaining push: c.gasMeter.gasRemaining
op jumpDest, inline = true: op jumpDest, inline = true:
## 0x5b, Mark a valid destination for jumps. This operation has no effect on machine state during execution. ## 0x5b, Mark a valid destination for jumps. This operation has no effect on machine state during execution.
@ -502,53 +502,53 @@ genLog()
# ########################################## # ##########################################
# f0s: System operations. # f0s: System operations.
proc canTransfer(computation: BaseComputation, memPos, memLen: int, value: Uint256, opCode: static[Op]): bool = proc canTransfer(c: Computation, memPos, memLen: int, value: Uint256, opCode: static[Op]): bool =
let gasParams = GasParams(kind: Create, let gasParams = GasParams(kind: Create,
cr_currentMemSize: computation.memory.len, cr_currentMemSize: c.memory.len,
cr_memOffset: memPos, cr_memOffset: memPos,
cr_memLength: memLen cr_memLength: memLen
) )
var gasCost = computation.gasCosts[Create].c_handler(1.u256, gasParams).gasCost var gasCost = c.gasCosts[Create].c_handler(1.u256, gasParams).gasCost
let reason = &"CREATE: GasCreate + {memLen} * memory expansion" let reason = &"CREATE: GasCreate + {memLen} * memory expansion"
when opCode == Create2: when opCode == Create2:
gasCost = gasCost + computation.gasCosts[Create2].m_handler(0, 0, memLen) gasCost = gasCost + c.gasCosts[Create2].m_handler(0, 0, memLen)
computation.gasMeter.consumeGas(gasCost, reason = reason) c.gasMeter.consumeGas(gasCost, reason = reason)
computation.memory.extend(memPos, memLen) c.memory.extend(memPos, memLen)
# the sender is childmsg sender, not parent msg sender # the sender is childmsg sender, not parent msg sender
# perhaps we need to move this code somewhere else # perhaps we need to move this code somewhere else
# to avoid confusion # to avoid confusion
let senderBalance = let senderBalance =
computation.vmState.readOnlyStateDb(). c.vmState.readOnlyStateDb().
getBalance(computation.msg.contractAddress) getBalance(c.msg.contractAddress)
if senderBalance < value: if senderBalance < value:
debug "Computation Failure", reason = "Insufficient funds available to transfer", required = computation.msg.value, balance = senderBalance debug "Computation Failure", reason = "Insufficient funds available to transfer", required = c.msg.value, balance = senderBalance
return false return false
# unlike the other MaxCallDepth comparison, # unlike the other MaxCallDepth comparison,
# this one has not been entered child computation # this one has not been entered child computation
# thats why it has `+ 1` # thats why it has `+ 1`
if computation.msg.depth + 1 > MaxCallDepth: if c.msg.depth + 1 > MaxCallDepth:
debug "Computation Failure", reason = "Stack too deep", maximumDepth = MaxCallDepth, depth = computation.msg.depth debug "Computation Failure", reason = "Stack too deep", maximumDepth = MaxCallDepth, depth = c.msg.depth
return false return false
result = true result = true
proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256, opCode: static[Op]): BaseComputation = proc setupCreate(c: Computation, memPos, len: int, value: Uint256, opCode: static[Op]): Computation =
let let
callData = computation.memory.read(memPos, len) callData = c.memory.read(memPos, len)
var var
createMsgGas = computation.getGasRemaining() createMsgGas = c.getGasRemaining()
if computation.fork >= FkTangerine: if c.fork >= FkTangerine:
createMsgGas -= createMsgGas div 64 createMsgGas -= createMsgGas div 64
# Consume gas here that will be passed to child # Consume gas here that will be passed to child
computation.gasMeter.consumeGas(createMsgGas, reason="CREATE") c.gasMeter.consumeGas(createMsgGas, reason="CREATE")
# Generate new address and check for collisions # Generate new address and check for collisions
var var
@ -557,20 +557,20 @@ proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256,
when opCode == Create: when opCode == Create:
const callKind = evmcCreate const callKind = evmcCreate
computation.vmState.mutateStateDB: c.vmState.mutateStateDB:
# Regarding collisions, see: https://github.com/status-im/nimbus/issues/133 # Regarding collisions, see: https://github.com/status-im/nimbus/issues/133
# See: https://github.com/ethereum/EIPs/issues/684 # See: https://github.com/ethereum/EIPs/issues/684
let creationNonce = db.getNonce(computation.msg.contractAddress) let creationNonce = db.getNonce(c.msg.contractAddress)
db.setNonce(computation.msg.contractAddress, creationNonce + 1) db.setNonce(c.msg.contractAddress, creationNonce + 1)
contractAddress = generateAddress(computation.msg.contractAddress, creationNonce) contractAddress = generateAddress(c.msg.contractAddress, creationNonce)
isCollision = db.hasCodeOrNonce(contractAddress) isCollision = db.hasCodeOrNonce(contractAddress)
else: else:
const callKind = evmcCreate2 const callKind = evmcCreate2
computation.vmState.mutateStateDB: c.vmState.mutateStateDB:
db.incNonce(computation.msg.contractAddress) db.incNonce(c.msg.contractAddress)
let salt = computation.stack.popInt() let salt = c.stack.popInt()
contractAddress = generateSafeAddress(computation.msg.contractAddress, salt, callData) contractAddress = generateSafeAddress(c.msg.contractAddress, salt, callData)
isCollision = db.hasCodeOrNonce(contractAddress) isCollision = db.hasCodeOrNonce(contractAddress)
if isCollision: if isCollision:
@ -580,11 +580,11 @@ proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256,
let childMsg = Message( let childMsg = Message(
kind: callKind, kind: callKind,
depth: computation.msg.depth + 1, depth: c.msg.depth + 1,
gas: createMsgGas, gas: createMsgGas,
gasPrice: computation.msg.gasPrice, gasPrice: c.msg.gasPrice,
origin: computation.msg.origin, origin: c.msg.origin,
sender: computation.msg.contractAddress, sender: c.msg.contractAddress,
contractAddress: contractAddress, contractAddress: contractAddress,
codeAddress: CREATE_CONTRACT_ADDRESS, codeAddress: CREATE_CONTRACT_ADDRESS,
value: value, value: value,
@ -592,106 +592,103 @@ proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256,
code: callData code: callData
) )
result = newBaseComputation( result = newComputation(c.vmState, childMsg, some(c.fork))
computation.vmState,
childMsg,
some(computation.fork))
template genCreate(callName: untyped, opCode: Op): untyped = template genCreate(callName: untyped, opCode: Op): untyped =
op callName, inline = false, val, startPosition, size: op callName, inline = false, val, startPosition, size:
## 0xf0, Create a new account with associated code. ## 0xf0, Create a new account with associated code.
let (memPos, len) = (startPosition.safeInt, size.safeInt) let (memPos, len) = (startPosition.safeInt, size.safeInt)
if not computation.canTransfer(memPos, len, val, opCode): if not c.canTransfer(memPos, len, val, opCode):
push: 0 push: 0
return return
var childComp = setupCreate(computation, memPos, len, val, opCode) var child = setupCreate(c, memPos, len, val, opCode)
if childComp.isNil: return if child.isNil: return
continuation(childComp): continuation(child):
addChildComputation(computation, childComp) addChildComputation(c, child)
if childComp.isError: if child.isError:
push: 0 push: 0
else: else:
push: childComp.msg.contractAddress push: child.msg.contractAddress
checkInStaticContext(computation) checkInStaticContext(c)
childComp.applyMessage(Create) child.applyMessage(Create)
genCreate(create, Create) genCreate(create, Create)
genCreate(create2, Create2) genCreate(create2, Create2)
proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) = proc callParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
let gas = computation.stack.popInt() let gas = c.stack.popInt()
let codeAddress = computation.stack.popAddress() let codeAddress = c.stack.popAddress()
let (value, let (value,
memoryInputStartPosition, memoryInputSize, memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(5) memoryOutputStartPosition, memoryOutputSize) = c.stack.popInt(5)
result = (gas, result = (gas,
value, value,
codeAddress, # contractAddress codeAddress, # contractAddress
computation.msg.contractAddress, # sender c.msg.contractAddress, # sender
codeAddress, codeAddress,
evmcCall, evmcCall,
memoryInputStartPosition, memoryInputStartPosition,
memoryInputSize, memoryInputSize,
memoryOutputStartPosition, memoryOutputStartPosition,
memoryOutputSize, memoryOutputSize,
computation.msg.flags) c.msg.flags)
proc callCodeParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) = proc callCodeParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
let gas = computation.stack.popInt() let gas = c.stack.popInt()
let codeAddress = computation.stack.popAddress() let codeAddress = c.stack.popAddress()
let (value, let (value,
memoryInputStartPosition, memoryInputSize, memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(5) memoryOutputStartPosition, memoryOutputSize) = c.stack.popInt(5)
result = (gas, result = (gas,
value, value,
computation.msg.contractAddress, # contractAddress c.msg.contractAddress, # contractAddress
computation.msg.contractAddress, # sender c.msg.contractAddress, # sender
codeAddress, codeAddress,
evmcCallCode, evmcCallCode,
memoryInputStartPosition, memoryInputStartPosition,
memoryInputSize, memoryInputSize,
memoryOutputStartPosition, memoryOutputStartPosition,
memoryOutputSize, memoryOutputSize,
computation.msg.flags) c.msg.flags)
proc delegateCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) = proc delegateCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
let gas = computation.stack.popInt() let gas = c.stack.popInt()
let codeAddress = computation.stack.popAddress() let codeAddress = c.stack.popAddress()
let (memoryInputStartPosition, memoryInputSize, let (memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4) memoryOutputStartPosition, memoryOutputSize) = c.stack.popInt(4)
result = (gas, result = (gas,
computation.msg.value, # value c.msg.value, # value
computation.msg.contractAddress, # contractAddress c.msg.contractAddress, # contractAddress
computation.msg.sender, # sender c.msg.sender, # sender
codeAddress, codeAddress,
evmcDelegateCall, evmcDelegateCall,
memoryInputStartPosition, memoryInputStartPosition,
memoryInputSize, memoryInputSize,
memoryOutputStartPosition, memoryOutputStartPosition,
memoryOutputSize, memoryOutputSize,
computation.msg.flags) c.msg.flags)
proc staticCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) = proc staticCallParams(c: Computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, CallKind, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
let gas = computation.stack.popInt() let gas = c.stack.popInt()
let codeAddress = computation.stack.popAddress() let codeAddress = c.stack.popAddress()
let (memoryInputStartPosition, memoryInputSize, let (memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4) memoryOutputStartPosition, memoryOutputSize) = c.stack.popInt(4)
result = (gas, result = (gas,
0.u256, # value 0.u256, # value
codeAddress, # contractAddress codeAddress, # contractAddress
computation.msg.contractAddress, # sender c.msg.contractAddress, # sender
codeAddress, codeAddress,
evmcCall, evmcCall,
memoryInputStartPosition, memoryInputStartPosition,
@ -701,55 +698,55 @@ proc staticCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddre
emvcStatic) # is_static emvcStatic) # is_static
template genCall(callName: untyped, opCode: Op): untyped = template genCall(callName: untyped, opCode: Op): untyped =
proc `callName Setup`(computation: BaseComputation, callNameStr: string): BaseComputation = proc `callName Setup`(c: Computation, callNameStr: string): Computation =
let (gas, value, contractAddress, sender, let (gas, value, contractAddress, sender,
codeAddress, callKind, codeAddress, callKind,
memoryInputStartPosition, memoryInputSize, memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize, memoryOutputStartPosition, memoryOutputSize,
flags) = `callName Params`(computation) flags) = `callName Params`(c)
let (memInPos, memInLen, memOutPos, memOutLen) = (memoryInputStartPosition.cleanMemRef, memoryInputSize.cleanMemRef, memoryOutputStartPosition.cleanMemRef, memoryOutputSize.cleanMemRef) let (memInPos, memInLen, memOutPos, memOutLen) = (memoryInputStartPosition.cleanMemRef, memoryInputSize.cleanMemRef, memoryOutputStartPosition.cleanMemRef, memoryOutputSize.cleanMemRef)
let isNewAccount = if computation.fork >= FkSpurious: let isNewAccount = if c.fork >= FkSpurious:
computation.vmState.readOnlyStateDb.isDeadAccount(contractAddress) c.vmState.readOnlyStateDb.isDeadAccount(contractAddress)
else: else:
not computation.vmState.readOnlyStateDb.accountExists(contractAddress) not c.vmState.readOnlyStateDb.accountExists(contractAddress)
let (memOffset, memLength) = if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen): let (memOffset, memLength) = if calcMemSize(memInPos, memInLen) > calcMemSize(memOutPos, memOutLen):
(memInPos, memInLen) (memInPos, memInLen)
else: else:
(memOutPos, memOutLen) (memOutPos, memOutLen)
let (childGasFee, childGasLimit) = computation.gasCosts[opCode].c_handler( let (childGasFee, childGasLimit) = c.gasCosts[opCode].c_handler(
value, value,
GasParams(kind: opCode, GasParams(kind: opCode,
c_isNewAccount: isNewAccount, c_isNewAccount: isNewAccount,
c_gasBalance: computation.gasMeter.gasRemaining, c_gasBalance: c.gasMeter.gasRemaining,
c_contractGas: gas, c_contractGas: gas,
c_currentMemSize: computation.memory.len, c_currentMemSize: c.memory.len,
c_memOffset: memOffset, c_memOffset: memOffset,
c_memLength: memLength c_memLength: memLength
)) ))
if childGasFee >= 0: if childGasFee >= 0:
computation.gasMeter.consumeGas(childGasFee, reason = $opCode) c.gasMeter.consumeGas(childGasFee, reason = $opCode)
if childGasFee < 0 and childGasLimit <= 0: if childGasFee < 0 and childGasLimit <= 0:
raise newException(OutOfGas, "Gas not enough to perform calculation (" & callNameStr & ")") raise newException(OutOfGas, "Gas not enough to perform calculation (" & callNameStr & ")")
computation.memory.extend(memInPos, memInLen) c.memory.extend(memInPos, memInLen)
computation.memory.extend(memOutPos, memOutLen) c.memory.extend(memOutPos, memOutLen)
let let
callData = computation.memory.read(memInPos, memInLen) callData = c.memory.read(memInPos, memInLen)
code = computation.vmState.readOnlyStateDb.getCode(codeAddress) code = c.vmState.readOnlyStateDb.getCode(codeAddress)
var childMsg = Message( var childMsg = Message(
kind: callKind, kind: callKind,
depth: computation.msg.depth + 1, depth: c.msg.depth + 1,
gas: childGasLimit, gas: childGasLimit,
gasPrice: computation.msg.gasPrice, gasPrice: c.msg.gasPrice,
origin: computation.msg.origin, origin: c.msg.origin,
sender: sender, sender: sender,
contractAddress: contractAddress, contractAddress: contractAddress,
codeAddress: codeAddress, codeAddress: codeAddress,
@ -758,41 +755,38 @@ template genCall(callName: untyped, opCode: Op): untyped =
code: code.toSeq, code: code.toSeq,
flags: flags) flags: flags)
var childComp = newBaseComputation( var child = newComputation(c.vmState, childMsg, some(c.fork))
computation.vmState,
childMsg,
some(computation.fork))
computation.memOutPos = memOutPos c.memOutPos = memOutPos
computation.memOutLen = memOutLen c.memOutLen = memOutLen
result = childComp result = child
op callName, inline = false: op callName, inline = false:
## CALL, 0xf1, Message-Call into an account ## CALL, 0xf1, Message-Call into an account
## CALLCODE, 0xf2, Message-call into this account with an alternative account's code. ## 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. ## 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. ## STATICCALL, 0xfa, Static message-call into an account.
var childComp = `callName Setup`(computation, callName.astToStr) var child = `callName Setup`(c, callName.astToStr)
continuation(childComp): continuation(child):
addChildComputation(computation, childComp) addChildComputation(c, child)
if childComp.isError: if child.isError:
push: 0 push: 0
else: else:
push: 1 push: 1
if not childComp.shouldEraseReturnData: if not child.shouldEraseReturnData:
let actualOutputSize = min(computation.memOutLen, childComp.output.len) let actualOutputSize = min(c.memOutLen, child.output.len)
computation.memory.write( c.memory.write(
computation.memOutPos, c.memOutPos,
childComp.output.toOpenArray(0, actualOutputSize - 1)) child.output.toOpenArray(0, actualOutputSize - 1))
when opCode == Call: when opCode == Call:
if emvcStatic == computation.msg.flags and childComp.msg.value > 0.u256: if emvcStatic == c.msg.flags and child.msg.value > 0.u256:
raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context") raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context")
childComp.applyMessage(opCode) child.applyMessage(opCode)
genCall(call, Call) genCall(call, Call)
genCall(callCode, CallCode) genCall(callCode, CallCode)
@ -803,35 +797,35 @@ op returnOp, inline = false, startPos, size:
## 0xf3, Halt execution returning output data. ## 0xf3, Halt execution returning output data.
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef) let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[Return].m_handler(computation.memory.len, pos, len), c.gasCosts[Return].m_handler(c.memory.len, pos, len),
reason = "RETURN" reason = "RETURN"
) )
computation.memory.extend(pos, len) c.memory.extend(pos, len)
computation.output = computation.memory.read(pos, len) c.output = c.memory.read(pos, len)
op revert, inline = false, startPos, size: op revert, inline = false, startPos, size:
## 0xfd, Halt execution reverting state changes but returning data and remaining gas. ## 0xfd, Halt execution reverting state changes but returning data and remaining gas.
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef) let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
computation.gasMeter.consumeGas( c.gasMeter.consumeGas(
computation.gasCosts[Revert].m_handler(computation.memory.len, pos, len), c.gasCosts[Revert].m_handler(c.memory.len, pos, len),
reason = "REVERT" reason = "REVERT"
) )
computation.memory.extend(pos, len) c.memory.extend(pos, len)
computation.output = computation.memory.read(pos, len) c.output = c.memory.read(pos, len)
# setError(msg, false) will signal cheap revert # setError(msg, false) will signal cheap revert
computation.setError("REVERT opcode executed", false) c.setError("REVERT opcode executed", false)
proc selfDestructImpl(computation: BaseComputation, beneficiary: EthAddress) = proc selfDestructImpl(c: Computation, beneficiary: EthAddress) =
## 0xff Halt execution and register account for later deletion. ## 0xff Halt execution and register account for later deletion.
# TODO: This is the basic implementation of the self destruct op, # TODO: This is the basic implementation of the self destruct op,
# Other forks have some extra functionality around this call. # Other forks have some extra functionality around this call.
# In particular, EIP150 and EIP161 have extra requirements. # In particular, EIP150 and EIP161 have extra requirements.
computation.vmState.mutateStateDB: c.vmState.mutateStateDB:
let let
localBalance = db.getBalance(computation.msg.contractAddress) localBalance = db.getBalance(c.msg.contractAddress)
beneficiaryBalance = db.getBalance(beneficiary) beneficiaryBalance = db.getBalance(beneficiary)
# Transfer to beneficiary # Transfer to beneficiary
@ -840,47 +834,47 @@ proc selfDestructImpl(computation: BaseComputation, beneficiary: EthAddress) =
# Zero the balance of the address being deleted. # Zero the balance of the address being deleted.
# This must come after sending to beneficiary in case the # This must come after sending to beneficiary in case the
# contract named itself as the beneficiary. # contract named itself as the beneficiary.
db.setBalance(computation.msg.contractAddress, 0.u256) db.setBalance(c.msg.contractAddress, 0.u256)
# Register the account to be deleted # Register the account to be deleted
computation.registerAccountForDeletion(beneficiary) c.registerAccountForDeletion(beneficiary)
trace "SELFDESTRUCT", trace "SELFDESTRUCT",
contractAddress = computation.msg.contractAddress.toHex, contractAddress = c.msg.contractAddress.toHex,
localBalance = localBalance.toString, localBalance = localBalance.toString,
beneficiary = beneficiary.toHex beneficiary = beneficiary.toHex
op selfDestruct, inline = false: op selfDestruct, inline = false:
let beneficiary = computation.stack.popAddress() let beneficiary = c.stack.popAddress()
selfDestructImpl(computation, beneficiary) selfDestructImpl(c, beneficiary)
op selfDestructEip150, inline = false: op selfDestructEip150, inline = false:
let beneficiary = computation.stack.popAddress() let beneficiary = c.stack.popAddress()
let gasParams = GasParams(kind: SelfDestruct, let gasParams = GasParams(kind: SelfDestruct,
sd_condition: not computation.vmState.readOnlyStateDb.accountExists(beneficiary) sd_condition: not c.vmState.readOnlyStateDb.accountExists(beneficiary)
) )
let gasCost = computation.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost let gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
computation.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP150") c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP150")
selfDestructImpl(computation, beneficiary) selfDestructImpl(c, beneficiary)
op selfDestructEip161, inline = false: op selfDestructEip161, inline = false:
checkInStaticContext(computation) checkInStaticContext(c)
let let
beneficiary = computation.stack.popAddress() beneficiary = c.stack.popAddress()
stateDb = computation.vmState.readOnlyStateDb stateDb = c.vmState.readOnlyStateDb
isDead = stateDb.isDeadAccount(beneficiary) isDead = stateDb.isDeadAccount(beneficiary)
balance = stateDb.getBalance(computation.msg.contractAddress) balance = stateDb.getBalance(c.msg.contractAddress)
let gasParams = GasParams(kind: SelfDestruct, let gasParams = GasParams(kind: SelfDestruct,
sd_condition: isDead and not balance.isZero sd_condition: isDead and not balance.isZero
) )
let gasCost = computation.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost let gasCost = c.gasCosts[SelfDestruct].c_handler(0.u256, gasParams).gasCost
computation.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP161") c.gasMeter.consumeGas(gasCost, reason = "SELFDESTRUCT EIP161")
selfDestructImpl(computation, beneficiary) selfDestructImpl(c, beneficiary)
# Constantinople's new opcodes # Constantinople's new opcodes
op shlOp, inline = true, shift, num: op shlOp, inline = true, shift, num:
@ -899,8 +893,8 @@ op shrOp, inline = true, shift, num:
push: num shr shiftLen push: num shr shiftLen
op sarOp, inline = true: op sarOp, inline = true:
let shiftLen = computation.stack.popInt().safeInt let shiftLen = c.stack.popInt().safeInt
let num = cast[Int256](computation.stack.popInt()) let num = cast[Int256](c.stack.popInt())
if shiftLen >= 256: if shiftLen >= 256:
if num.isNegative: if num.isNegative:
push: cast[Uint256]((-1).i256) push: cast[Uint256]((-1).i256)
@ -912,41 +906,41 @@ op sarOp, inline = true:
push: cast[Uint256](num shr shiftLen) push: cast[Uint256](num shr shiftLen)
op extCodeHash, inline = true: op extCodeHash, inline = true:
let address = computation.stack.popAddress() let address = c.stack.popAddress()
# this is very inefficient, it calls underlying # this is very inefficient, it calls underlying
# database too much, we can reduce it by implementing accounts # database too much, we can reduce it by implementing accounts
# cache # cache
if not computation.vmState.readOnlyStateDB.accountExists(address): if not c.vmState.readOnlyStateDB.accountExists(address):
push: 0 push: 0
return return
if computation.vmState.readOnlyStateDB.isEmptyAccount(address): if c.vmState.readOnlyStateDB.isEmptyAccount(address):
push: 0 push: 0
else: else:
push: computation.vmState.readOnlyStateDB.getCodeHash(address) push: c.vmState.readOnlyStateDB.getCodeHash(address)
op sstoreEIP2200, inline = false, slot, value: op sstoreEIP2200, inline = false, slot, value:
checkInStaticContext(computation) checkInStaticContext(c)
const SentryGasEIP2200 = 2300 # Minimum gas required to be present for an SSTORE call, not consumed const SentryGasEIP2200 = 2300 # Minimum gas required to be present for an SSTORE call, not consumed
if computation.gasMeter.gasRemaining <= SentryGasEIP2200: if c.gasMeter.gasRemaining <= SentryGasEIP2200:
raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE") raise newException(OutOfGas, "Gas not enough to perform EIP2200 SSTORE")
let stateDB = computation.vmState.readOnlyStateDB let stateDB = c.vmState.readOnlyStateDB
let (currentValue, existing) = stateDB.getStorage(computation.msg.contractAddress, slot) let (currentValue, existing) = stateDB.getStorage(c.msg.contractAddress, slot)
let let
gasParam = GasParams(kind: Op.Sstore, gasParam = GasParams(kind: Op.Sstore,
s_isStorageEmpty: currentValue.isZero, s_isStorageEmpty: currentValue.isZero,
s_currentValue: currentValue, s_currentValue: currentValue,
s_originalValue: stateDB.getCommittedStorage(computation.msg.contractAddress, slot) s_originalValue: stateDB.getCommittedStorage(c.msg.contractAddress, slot)
) )
(gasCost, gasRefund) = computation.gasCosts[Sstore].c_handler(value, gasParam) (gasCost, gasRefund) = c.gasCosts[Sstore].c_handler(value, gasParam)
computation.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {computation.msg.contractAddress}[{slot}] -> {value} ({currentValue})") c.gasMeter.consumeGas(gasCost, &"SSTORE EIP2200: {c.msg.contractAddress}[{slot}] -> {value} ({currentValue})")
if gasRefund != 0: if gasRefund != 0:
computation.gasMeter.refundGas(gasRefund) c.gasMeter.refundGas(gasRefund)
computation.vmState.mutateStateDB: c.vmState.mutateStateDB:
db.setStorage(computation.msg.contractAddress, slot, value) db.setStorage(c.msg.contractAddress, slot, value)

View File

@ -31,7 +31,7 @@ macro op*(procname: untyped, inline: static[bool], stackParams_body: varargs[unt
# we can't have a nicer macro signature `stackParams: varargs[untyped], body: untyped` # 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" # see https://github.com/nim-lang/Nim/issues/5855 and are forced to "pop"
let computation = newIdentNode("computation") let computation = newIdentNode("c")
var stackParams = stackParams_body var stackParams = stackParams_body
# 1. Separate stackParams and body with pop # 1. Separate stackParams and body with pop
@ -59,12 +59,12 @@ macro op*(procname: untyped, inline: static[bool], stackParams_body: varargs[unt
# TODO: replace by func to ensure no side effects # TODO: replace by func to ensure no side effects
if inline: if inline:
result = quote do: result = quote do:
proc `procname`*(`computation`: BaseComputation) {.inline.} = proc `procname`*(`computation`: Computation) {.inline.} =
`popStackStmt` `popStackStmt`
`body` `body`
else: else:
result = quote do: result = quote do:
proc `procname`*(`computation`: BaseComputation) {.gcsafe.} = proc `procname`*(`computation`: Computation) {.gcsafe.} =
`popStackStmt` `popStackStmt`
`body` `body`
@ -76,7 +76,7 @@ macro genPush*(): untyped =
for size in 1 .. 32: for size in 1 .. 32:
let name = genName(size) let name = genName(size)
result.add quote do: result.add quote do:
func `name`*(computation: BaseComputation) {.inline.}= func `name`*(computation: Computation) {.inline.}=
## Push `size`-byte(s) on the stack ## Push `size`-byte(s) on the stack
computation.stack.push computation.code.readVmWord(`size`) computation.stack.push computation.code.readVmWord(`size`)
@ -87,7 +87,7 @@ macro genDup*(): untyped =
for pos in 1 .. 16: for pos in 1 .. 16:
let name = genName(pos) let name = genName(pos)
result.add quote do: result.add quote do:
func `name`*(computation: BaseComputation) {.inline.}= func `name`*(computation: Computation) {.inline.}=
computation.stack.dup(`pos`) computation.stack.dup(`pos`)
macro genSwap*(): untyped = macro genSwap*(): untyped =
@ -97,16 +97,16 @@ macro genSwap*(): untyped =
for pos in 1 .. 16: for pos in 1 .. 16:
let name = genName(pos) let name = genName(pos)
result.add quote do: result.add quote do:
func `name`*(computation: BaseComputation) {.inline.}= func `name`*(computation: Computation) {.inline.}=
computation.stack.swap(`pos`) computation.stack.swap(`pos`)
template checkInStaticContext*(comp: BaseComputation) = template checkInStaticContext*(comp: Computation) =
# TODO: if possible, this check only appear # TODO: if possible, this check only appear
# when fork >= FkByzantium # when fork >= FkByzantium
if emvcStatic == comp.msg.flags: if emvcStatic == comp.msg.flags:
raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context") raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context")
proc logImpl(c: BaseComputation, opcode: Op, topicCount: int) = proc logImpl(c: Computation, opcode: Op, topicCount: int) =
doAssert(topicCount in 0 .. 4) doAssert(topicCount in 0 .. 4)
checkInStaticContext(c) checkInStaticContext(c)
let (memStartPosition, size) = c.stack.popInt(2) let (memStartPosition, size) = c.stack.popInt(2)
@ -129,8 +129,8 @@ proc logImpl(c: BaseComputation, opcode: Op, topicCount: int) =
c.addLogEntry(log) c.addLogEntry(log)
template genLog*() = template genLog*() =
proc log0*(c: BaseComputation) {.inline.} = logImpl(c, Log0, 0) proc log0*(c: Computation) {.inline.} = logImpl(c, Log0, 0)
proc log1*(c: BaseComputation) {.inline.} = logImpl(c, Log1, 1) proc log1*(c: Computation) {.inline.} = logImpl(c, Log1, 1)
proc log2*(c: BaseComputation) {.inline.} = logImpl(c, Log2, 2) proc log2*(c: Computation) {.inline.} = logImpl(c, Log2, 2)
proc log3*(c: BaseComputation) {.inline.} = logImpl(c, Log3, 3) proc log3*(c: Computation) {.inline.} = logImpl(c, Log3, 3)
proc log4*(c: BaseComputation) {.inline.} = logImpl(c, Log4, 4) proc log4*(c: Computation) {.inline.} = logImpl(c, Log4, 4)

View File

@ -16,7 +16,7 @@ import
logScope: logScope:
topics = "vm opcode" topics = "vm opcode"
func invalidInstruction*(computation: BaseComputation) {.inline.} = func invalidInstruction*(c: Computation) {.inline.} =
raise newException(InvalidInstruction, "Invalid instruction, received an opcode not implemented in the current fork.") raise newException(InvalidInstruction, "Invalid instruction, received an opcode not implemented in the current fork.")
let FrontierOpDispatch {.compileTime.}: array[Op, NimNode] = block: let FrontierOpDispatch {.compileTime.}: array[Op, NimNode] = block:
@ -223,9 +223,9 @@ proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compile
let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch) let IstanbulOpDispatch {.compileTime.}: array[Op, NimNode] = genIstanbulJumpTable(ConstantinopleOpDispatch)
proc opTableToCaseStmt(opTable: array[Op, NimNode], computation: NimNode): NimNode = proc opTableToCaseStmt(opTable: array[Op, NimNode], c: NimNode): NimNode =
let instr = quote do: `computation`.instr let instr = quote do: `c`.instr
result = nnkCaseStmt.newTree(instr) result = nnkCaseStmt.newTree(instr)
# Add a branch for each (opcode, proc) pair # Add a branch for each (opcode, proc) pair
@ -236,27 +236,27 @@ proc opTableToCaseStmt(opTable: array[Op, NimNode], computation: NimNode): NimNo
if op == Stop: if op == Stop:
quote do: quote do:
trace "op: Stop" trace "op: Stop"
if not `computation`.code.atEnd() and `computation`.tracingEnabled: if not `c`.code.atEnd() and `c`.tracingEnabled:
# we only trace `REAL STOP` and ignore `FAKE STOP` # we only trace `REAL STOP` and ignore `FAKE STOP`
`computation`.opIndex = `computation`.traceOpCodeStarted(`asOp`) `c`.opIndex = `c`.traceOpCodeStarted(`asOp`)
`computation`.traceOpCodeEnded(`asOp`, `computation`.opIndex) `c`.traceOpCodeEnded(`asOp`, `c`.opIndex)
break break
else: else:
if BaseGasCosts[op].kind == GckFixed: if BaseGasCosts[op].kind == GckFixed:
quote do: quote do:
if `computation`.tracingEnabled: if `c`.tracingEnabled:
`computation`.opIndex = `computation`.traceOpCodeStarted(`asOp`) `c`.opIndex = `c`.traceOpCodeStarted(`asOp`)
`computation`.gasMeter.consumeGas(`computation`.gasCosts[`asOp`].cost, reason = $`asOp`) `c`.gasMeter.consumeGas(`c`.gasCosts[`asOp`].cost, reason = $`asOp`)
`opImpl`(`computation`) `opImpl`(`c`)
if `computation`.tracingEnabled: if `c`.tracingEnabled:
`computation`.traceOpCodeEnded(`asOp`, `computation`.opIndex) `c`.traceOpCodeEnded(`asOp`, `c`.opIndex)
else: else:
quote do: quote do:
if `computation`.tracingEnabled: if `c`.tracingEnabled:
`computation`.opIndex = `computation`.traceOpCodeStarted(`asOp`) `c`.opIndex = `c`.traceOpCodeStarted(`asOp`)
`opImpl`(`computation`) `opImpl`(`c`)
if `computation`.tracingEnabled: if `c`.tracingEnabled:
`computation`.traceOpCodeEnded(`asOp`, `computation`.opIndex) `c`.traceOpCodeEnded(`asOp`, `c`.opIndex)
when `asOp` in {Return, Revert, SelfDestruct}: when `asOp` in {Return, Revert, SelfDestruct}:
break break
@ -267,90 +267,90 @@ proc opTableToCaseStmt(opTable: array[Op, NimNode], computation: NimNode): NimNo
# Wrap the case statement in while true + computed goto # Wrap the case statement in while true + computed goto
result = quote do: result = quote do:
if `computation`.tracingEnabled: if `c`.tracingEnabled:
`computation`.prepareTracer() `c`.prepareTracer()
while true: while true:
`instr` = `computation`.code.next() `instr` = `c`.code.next()
#{.computedGoto.} #{.computedGoto.}
# computed goto causing stack overflow, it consumes a lot of space # computed goto causing stack overflow, it consumes a lot of space
# we could use manual jump table instead # we could use manual jump table instead
# TODO lots of macro magic here to unravel, with chronicles... # TODO lots of macro magic here to unravel, with chronicles...
# `computation`.logger.log($`computation`.stack & "\n\n", fgGreen) # `c`.logger.log($`c`.stack & "\n\n", fgGreen)
`result` `result`
macro genFrontierDispatch(computation: BaseComputation): untyped = macro genFrontierDispatch(c: Computation): untyped =
result = opTableToCaseStmt(FrontierOpDispatch, computation) result = opTableToCaseStmt(FrontierOpDispatch, c)
macro genHomesteadDispatch(computation: BaseComputation): untyped = macro genHomesteadDispatch(c: Computation): untyped =
result = opTableToCaseStmt(HomesteadOpDispatch, computation) result = opTableToCaseStmt(HomesteadOpDispatch, c)
macro genTangerineDispatch(computation: BaseComputation): untyped = macro genTangerineDispatch(c: Computation): untyped =
result = opTableToCaseStmt(TangerineOpDispatch, computation) result = opTableToCaseStmt(TangerineOpDispatch, c)
macro genSpuriousDispatch(computation: BaseComputation): untyped = macro genSpuriousDispatch(c: Computation): untyped =
result = opTableToCaseStmt(SpuriousOpDispatch, computation) result = opTableToCaseStmt(SpuriousOpDispatch, c)
macro genByzantiumDispatch(computation: BaseComputation): untyped = macro genByzantiumDispatch(c: Computation): untyped =
result = opTableToCaseStmt(ByzantiumOpDispatch, computation) result = opTableToCaseStmt(ByzantiumOpDispatch, c)
macro genConstantinopleDispatch(computation: BaseComputation): untyped = macro genConstantinopleDispatch(c: Computation): untyped =
result = opTableToCaseStmt(ConstantinopleOpDispatch, computation) result = opTableToCaseStmt(ConstantinopleOpDispatch, c)
macro genIstanbulDispatch(computation: BaseComputation): untyped = macro genIstanbulDispatch(c: Computation): untyped =
result = opTableToCaseStmt(IstanbulOpDispatch, computation) result = opTableToCaseStmt(IstanbulOpDispatch, c)
proc frontierVM(computation: BaseComputation) = proc frontierVM(c: Computation) =
genFrontierDispatch(computation) genFrontierDispatch(c)
proc homesteadVM(computation: BaseComputation) = proc homesteadVM(c: Computation) =
genHomesteadDispatch(computation) genHomesteadDispatch(c)
proc tangerineVM(computation: BaseComputation) = proc tangerineVM(c: Computation) =
genTangerineDispatch(computation) genTangerineDispatch(c)
proc spuriousVM(computation: BaseComputation) {.gcsafe.} = proc spuriousVM(c: Computation) {.gcsafe.} =
genSpuriousDispatch(computation) genSpuriousDispatch(c)
proc byzantiumVM(computation: BaseComputation) {.gcsafe.} = proc byzantiumVM(c: Computation) {.gcsafe.} =
genByzantiumDispatch(computation) genByzantiumDispatch(c)
proc constantinopleVM(computation: BaseComputation) {.gcsafe.} = proc constantinopleVM(c: Computation) {.gcsafe.} =
genConstantinopleDispatch(computation) genConstantinopleDispatch(c)
proc istanbulVM(computation: BaseComputation) {.gcsafe.} = proc istanbulVM(c: Computation) {.gcsafe.} =
genIstanbulDispatch(computation) genIstanbulDispatch(c)
proc selectVM(computation: BaseComputation, fork: Fork) {.gcsafe.} = proc selectVM(c: Computation, fork: Fork) {.gcsafe.} =
# TODO: Optimise getting fork and updating opCodeExec only when necessary # TODO: Optimise getting fork and updating opCodeExec only when necessary
case fork case fork
of FkFrontier..FkThawing: of FkFrontier..FkThawing:
computation.frontierVM() c.frontierVM()
of FkHomestead..FkDao: of FkHomestead..FkDao:
computation.homesteadVM() c.homesteadVM()
of FkTangerine: of FkTangerine:
computation.tangerineVM() c.tangerineVM()
of FkSpurious: of FkSpurious:
computation.spuriousVM() c.spuriousVM()
of FkByzantium: of FkByzantium:
computation.byzantiumVM() c.byzantiumVM()
of FkConstantinople: of FkConstantinople:
computation.constantinopleVM() c.constantinopleVM()
else: else:
computation.istanbulVM() c.istanbulVM()
proc executeOpcodes(computation: BaseComputation) = proc executeOpcodes(c: Computation) =
let fork = computation.fork let fork = c.fork
block: block:
if computation.execPrecompiles(fork): if c.execPrecompiles(fork):
break break
try: try:
computation.selectVM(fork) c.selectVM(fork)
except CatchableError as e: except CatchableError as e:
computation.setError(&"Opcode Dispatch Error msg={e.msg}, depth={computation.msg.depth}", true) c.setError(&"Opcode Dispatch Error msg={e.msg}, depth={c.msg.depth}", true)
computation.nextProc() c.nextProc()
if computation.isError(): if c.isError():
if computation.tracingEnabled: computation.traceError() if c.tracingEnabled: c.traceError()
debug "executeOpcodes error", msg=computation.error.info debug "executeOpcodes error", msg=c.error.info

View File

@ -18,7 +18,7 @@ type
# Istanbul # Istanbul
paBlake2bf = 9 paBlake2bf = 9
proc getSignature(computation: BaseComputation): (array[32, byte], Signature) = proc getSignature(computation: Computation): (array[32, byte], Signature) =
# input is Hash, V, R, S # input is Hash, V, R, S
template data: untyped = computation.msg.data template data: untyped = computation.msg.data
var bytes: array[65, byte] # will hold R[32], S[32], V[1], in that order var bytes: array[65, byte] # will hold R[32], S[32], V[1], in that order
@ -91,7 +91,7 @@ proc getFR(data: openarray[byte]): FR =
if not result.fromBytes2(data): if not result.fromBytes2(data):
raise newException(ValidationError, "Could not get FR value") raise newException(ValidationError, "Could not get FR value")
proc ecRecover*(computation: BaseComputation) = proc ecRecover*(computation: Computation) =
computation.gasMeter.consumeGas( computation.gasMeter.consumeGas(
GasECRecover, GasECRecover,
reason="ECRecover Precompile") reason="ECRecover Precompile")
@ -107,7 +107,7 @@ proc ecRecover*(computation: BaseComputation) =
computation.rawOutput[12..31] = pubKey.toCanonicalAddress() computation.rawOutput[12..31] = pubKey.toCanonicalAddress()
trace "ECRecover precompile", derivedKey = pubKey.toCanonicalAddress() trace "ECRecover precompile", derivedKey = pubKey.toCanonicalAddress()
proc sha256*(computation: BaseComputation) = proc sha256*(computation: Computation) =
let let
wordCount = wordCount(computation.msg.data.len) wordCount = wordCount(computation.msg.data.len)
gasFee = GasSHA256 + wordCount * GasSHA256Word gasFee = GasSHA256 + wordCount * GasSHA256Word
@ -116,7 +116,7 @@ proc sha256*(computation: BaseComputation) =
computation.rawOutput = @(nimcrypto.sha_256.digest(computation.msg.data).data) computation.rawOutput = @(nimcrypto.sha_256.digest(computation.msg.data).data)
trace "SHA256 precompile", output = computation.rawOutput.toHex trace "SHA256 precompile", output = computation.rawOutput.toHex
proc ripemd160*(computation: BaseComputation) = proc ripemd160*(computation: Computation) =
let let
wordCount = wordCount(computation.msg.data.len) wordCount = wordCount(computation.msg.data.len)
gasFee = GasRIPEMD160 + wordCount * GasRIPEMD160Word gasFee = GasRIPEMD160 + wordCount * GasRIPEMD160Word
@ -126,7 +126,7 @@ proc ripemd160*(computation: BaseComputation) =
computation.rawOutput[12..31] = @(nimcrypto.ripemd160.digest(computation.msg.data).data) computation.rawOutput[12..31] = @(nimcrypto.ripemd160.digest(computation.msg.data).data)
trace "RIPEMD160 precompile", output = computation.rawOutput.toHex trace "RIPEMD160 precompile", output = computation.rawOutput.toHex
proc identity*(computation: BaseComputation) = proc identity*(computation: Computation) =
let let
wordCount = wordCount(computation.msg.data.len) wordCount = wordCount(computation.msg.data.len)
gasFee = GasIdentity + wordCount * GasIdentityWord gasFee = GasIdentity + wordCount * GasIdentityWord
@ -135,7 +135,7 @@ proc identity*(computation: BaseComputation) =
computation.rawOutput = computation.msg.data computation.rawOutput = computation.msg.data
trace "Identity precompile", output = computation.rawOutput.toHex trace "Identity precompile", output = computation.rawOutput.toHex
proc modExpInternal(computation: BaseComputation, base_len, exp_len, mod_len: int, T: type StUint) = proc modExpInternal(computation: Computation, base_len, exp_len, mod_len: int, T: type StUint) =
template rawMsg: untyped {.dirty.} = template rawMsg: untyped {.dirty.} =
computation.msg.data computation.msg.data
@ -208,7 +208,7 @@ proc modExpInternal(computation: BaseComputation, base_len, exp_len, mod_len: in
computation.rawOutput = newSeq[byte](mod_len) computation.rawOutput = newSeq[byte](mod_len)
computation.rawOutput[^output.len..^1] = output[0..^1] computation.rawOutput[^output.len..^1] = output[0..^1]
proc modExp*(computation: BaseComputation) = proc modExp*(computation: Computation) =
## Modular exponentiation precompiled contract ## Modular exponentiation precompiled contract
## Yellow Paper Appendix E ## Yellow Paper Appendix E
## EIP-198 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md ## EIP-198 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md
@ -237,7 +237,7 @@ proc modExp*(computation: BaseComputation) =
else: else:
raise newException(EVMError, "The Nimbus VM doesn't support modular exponentiation with numbers larger than uint8192") raise newException(EVMError, "The Nimbus VM doesn't support modular exponentiation with numbers larger than uint8192")
proc bn256ecAdd*(computation: BaseComputation, fork: Fork = FkByzantium) = proc bn256ecAdd*(computation: Computation, fork: Fork = FkByzantium) =
let gasFee = if fork < FkIstanbul: GasECAdd else: GasECAddIstanbul let gasFee = if fork < FkIstanbul: GasECAdd else: GasECAddIstanbul
computation.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile") computation.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile")
@ -258,7 +258,7 @@ proc bn256ecAdd*(computation: BaseComputation, fork: Fork = FkByzantium) =
computation.rawOutput = @output computation.rawOutput = @output
proc bn256ecMul*(computation: BaseComputation, fork: Fork = FkByzantium) = proc bn256ecMul*(computation: Computation, fork: Fork = FkByzantium) =
let gasFee = if fork < FkIstanbul: GasECMul else: GasECMulIstanbul let gasFee = if fork < FkIstanbul: GasECMul else: GasECMulIstanbul
computation.gasMeter.consumeGas(gasFee, reason="ecMul Precompile") computation.gasMeter.consumeGas(gasFee, reason="ecMul Precompile")
@ -281,7 +281,7 @@ proc bn256ecMul*(computation: BaseComputation, fork: Fork = FkByzantium) =
computation.rawOutput = @output computation.rawOutput = @output
proc bn256ecPairing*(computation: BaseComputation, fork: Fork = FkByzantium) = proc bn256ecPairing*(computation: Computation, fork: Fork = FkByzantium) =
let msglen = len(computation.msg.data) let msglen = len(computation.msg.data)
if msglen mod 192 != 0: if msglen mod 192 != 0:
raise newException(ValidationError, "Invalid input length") raise newException(ValidationError, "Invalid input length")
@ -318,7 +318,7 @@ proc bn256ecPairing*(computation: BaseComputation, fork: Fork = FkByzantium) =
computation.rawOutput = @output computation.rawOutput = @output
proc blake2bf*(computation: BaseComputation) = proc blake2bf*(computation: Computation) =
template input(): untyped = template input(): untyped =
computation.msg.data computation.msg.data
@ -337,7 +337,7 @@ proc getMaxPrecompileAddr(fork: Fork): PrecompileAddresses =
elif fork < FkIstanbul: paPairing elif fork < FkIstanbul: paPairing
else: PrecompileAddresses.high else: PrecompileAddresses.high
proc execPrecompiles*(computation: BaseComputation, fork: Fork): bool {.inline.} = proc execPrecompiles*(computation: Computation, fork: Fork): bool {.inline.} =
for i in 0..18: for i in 0..18:
if computation.msg.codeAddress[i] != 0: return if computation.msg.codeAddress[i] != 0: return

View File

@ -44,7 +44,7 @@ iterator storage(tracer: TransactionTracer, compDepth: int): Uint256 =
for key in tracer.storageKeys[compDepth]: for key in tracer.storageKeys[compDepth]:
yield key yield key
proc traceOpCodeStarted*(tracer: var TransactionTracer, c: BaseComputation, op: Op): int = proc traceOpCodeStarted*(tracer: var TransactionTracer, c: Computation, op: Op): int =
if unlikely tracer.trace.isNil: if unlikely tracer.trace.isNil:
tracer.initTracer() tracer.initTracer()
@ -90,7 +90,7 @@ proc traceOpCodeStarted*(tracer: var TransactionTracer, c: BaseComputation, op:
result = tracer.trace["structLogs"].len - 1 result = tracer.trace["structLogs"].len - 1
proc traceOpCodeEnded*(tracer: var TransactionTracer, c: BaseComputation, op: Op, lastIndex: int) = proc traceOpCodeEnded*(tracer: var TransactionTracer, c: Computation, op: Op, lastIndex: int) =
let j = tracer.trace["structLogs"].elems[lastIndex] let j = tracer.trace["structLogs"].elems[lastIndex]
# TODO: figure out how to get storage # TODO: figure out how to get storage
@ -114,7 +114,7 @@ proc traceOpCodeEnded*(tracer: var TransactionTracer, c: BaseComputation, op: Op
trace "Op", json = j.pretty() trace "Op", json = j.pretty()
proc traceError*(tracer: var TransactionTracer, c: BaseComputation) = proc traceError*(tracer: var TransactionTracer, c: Computation) =
if tracer.trace["structLogs"].elems.len > 0: if tracer.trace["structLogs"].elems.len > 0:
let j = tracer.trace["structLogs"].elems[^1] let j = tracer.trace["structLogs"].elems[^1]
j["error"] = %(c.error.info) j["error"] = %(c.error.info)

View File

@ -26,7 +26,7 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction, sender: EthAddr
tx.accountNonce == account.nonce and tx.accountNonce == account.nonce and
account.balance >= gasCost account.balance >= gasCost
proc setupComputation*(vmState: BaseVMState, tx: Transaction, sender, recipient: EthAddress, fork: Fork) : BaseComputation = proc setupComputation*(vmState: BaseVMState, tx: Transaction, sender, recipient: EthAddress, fork: Fork) : Computation =
var gas = tx.gasLimit - tx.intrinsicGas(fork) var gas = tx.gasLimit - tx.intrinsicGas(fork)
# TODO: refactor message to use byterange # TODO: refactor message to use byterange
@ -58,46 +58,46 @@ proc setupComputation*(vmState: BaseVMState, tx: Transaction, sender, recipient:
code: code code: code
) )
result = newBaseComputation(vmState, msg, some(fork)) result = newComputation(vmState, msg, some(fork))
doAssert result.isOriginComputation doAssert result.isOriginComputation
proc execComputation*(computation: var BaseComputation) = proc execComputation*(c: Computation) =
if computation.msg.isCreate: if c.msg.isCreate:
computation.applyMessage(Create) c.applyMessage(Create)
else: else:
computation.applyMessage(Call) c.applyMessage(Call)
computation.vmState.mutateStateDB: c.vmState.mutateStateDB:
var suicidedCount = 0 var suicidedCount = 0
for deletedAccount in computation.accountsForDeletion: for deletedAccount in c.accountsForDeletion:
db.deleteAccount deletedAccount db.deleteAccount deletedAccount
inc suicidedCount inc suicidedCount
# FIXME: hook this into actual RefundSelfDestruct # FIXME: hook this into actual RefundSelfDestruct
const RefundSelfDestruct = 24_000 const RefundSelfDestruct = 24_000
computation.gasMeter.refundGas(RefundSelfDestruct * suicidedCount) c.gasMeter.refundGas(RefundSelfDestruct * suicidedCount)
if computation.fork >= FkSpurious: if c.fork >= FkSpurious:
computation.collectTouchedAccounts() c.collectTouchedAccounts()
computation.vmstate.status = computation.isSuccess c.vmstate.status = c.isSuccess
if computation.isSuccess: if c.isSuccess:
computation.vmState.addLogs(computation.logEntries) c.vmState.addLogs(c.logEntries)
proc refundGas*(computation: BaseComputation, tx: Transaction, sender: EthAddress): GasInt = proc refundGas*(c: Computation, tx: Transaction, sender: EthAddress): GasInt =
let let
gasRemaining = computation.gasMeter.gasRemaining gasRemaining = c.gasMeter.gasRemaining
gasRefunded = computation.getGasRefund() gasRefunded = c.getGasRefund()
gasUsed = tx.gasLimit - gasRemaining gasUsed = tx.gasLimit - gasRemaining
gasRefund = min(gasRefunded, gasUsed div 2) gasRefund = min(gasRefunded, gasUsed div 2)
computation.vmState.mutateStateDB: c.vmState.mutateStateDB:
db.addBalance(sender, (gasRemaining + gasRefund).u256 * tx.gasPrice.u256) db.addBalance(sender, (gasRemaining + gasRefund).u256 * tx.gasPrice.u256)
result = gasUsed - gasRefund result = gasUsed - gasRefund
#[ #[
method executeTransaction(vmState: BaseVMState, transaction: Transaction): (BaseComputation, BlockHeader) {.base.}= method executeTransaction(vmState: BaseVMState, transaction: Transaction): (Computation, BlockHeader) {.base.}=
# Execute the transaction in the vm # Execute the transaction in the vm
# TODO: introduced here: https://github.com/ethereum/py-evm/commit/21c57f2d56ab91bb62723c3f9ebe291d0b132dde # TODO: introduced here: https://github.com/ethereum/py-evm/commit/21c57f2d56ab91bb62723c3f9ebe291d0b132dde
# Refactored/Removed here: https://github.com/ethereum/py-evm/commit/cc991bf # Refactored/Removed here: https://github.com/ethereum/py-evm/commit/cc991bf
@ -105,7 +105,7 @@ method executeTransaction(vmState: BaseVMState, transaction: Transaction): (Base
raise newException(ValueError, "Must be implemented by subclasses") raise newException(ValueError, "Must be implemented by subclasses")
method addTransaction*(vmState: BaseVMState, transaction: Transaction, computation: BaseComputation, b: Block): (Block, Table[string, string]) = method addTransaction*(vmState: BaseVMState, transaction: Transaction, c: Computation, b: Block): (Block, Table[string, string]) =
# Add a transaction to the given block and # Add a transaction to the given block and
# return `trieData` to store the transaction data in chaindb in VM layer # return `trieData` to store the transaction data in chaindb in VM layer
# Update the bloomFilter, transaction trie and receipt trie roots, bloom_filter, # Update the bloomFilter, transaction trie and receipt trie roots, bloom_filter,
@ -138,7 +138,7 @@ method applyTransaction*(
vmState: BaseVMState, vmState: BaseVMState,
transaction: Transaction, transaction: Transaction,
b: Block, b: Block,
isStateless: bool): (BaseComputation, Block, Table[string, string]) = isStateless: bool): (Computation, Block, Table[string, string]) =
# Apply transaction to the given block # Apply transaction to the given block
# transaction: the transaction need to be applied # transaction: the transaction need to be applied
# b: the block which the transaction applies on # b: the block which the transaction applies on

View File

@ -51,7 +51,7 @@ type
transaction*: DbTransaction transaction*: DbTransaction
intermediateRoot*: Hash256 intermediateRoot*: Hash256
BaseComputation* = ref object of RootObj Computation* = ref object
# The execution computation # The execution computation
vmState*: BaseVMState vmState*: BaseVMState
msg*: Message msg*: Message

View File

@ -194,7 +194,7 @@ proc generateVMProxy(boa: Assembler): NimNode =
const const
blockFile = "tests" / "fixtures" / "PersistBlockTests" / "block47205.json" blockFile = "tests" / "fixtures" / "PersistBlockTests" / "block47205.json"
proc initComputation(vmState: BaseVMState, tx: Transaction, sender: EthAddress, data: seq[byte], forkOverride=none(Fork)) : BaseComputation = proc initComputation(vmState: BaseVMState, tx: Transaction, sender: EthAddress, data: seq[byte], forkOverride=none(Fork)) : Computation =
doAssert tx.isContractCreation doAssert tx.isContractCreation
let fork = let fork =
@ -220,7 +220,7 @@ proc initComputation(vmState: BaseVMState, tx: Transaction, sender: EthAddress,
code: tx.payload code: tx.payload
) )
newBaseComputation(vmState, msg, some(fork)) newComputation(vmState, msg, some(fork))
proc initDatabase*(): (Uint256, BaseChainDB) = proc initDatabase*(): (Uint256, BaseChainDB) =
let let
@ -236,7 +236,7 @@ proc initDatabase*(): (Uint256, BaseChainDB) =
result = (blockNumber, newBaseChainDB(memoryDB, false)) result = (blockNumber, newBaseChainDB(memoryDB, false))
proc initComputation(blockNumber: Uint256, chainDB: BaseChainDB, payload, data: seq[byte], fork: Fork): BaseComputation = proc initComputation(blockNumber: Uint256, chainDB: BaseChainDB, payload, data: seq[byte], fork: Fork): Computation =
let let
parentNumber = blockNumber - 1 parentNumber = blockNumber - 1
parent = chainDB.getBlockHeader(parentNumber) parent = chainDB.getBlockHeader(parentNumber)

View File

@ -41,7 +41,7 @@ template doTest(fixture: JsonNode, address: byte, action: untyped): untyped =
data: data, data: data,
code: @[] code: @[]
) )
computation = newBaseComputation(vmState, message) computation = newComputation(vmState, message)
echo "Running ", action.astToStr, " - ", test["name"] echo "Running ", action.astToStr, " - ", test["name"]
`action`(computation) `action`(computation)
let c = computation.rawOutput == expected let c = computation.rawOutput == expected

View File

@ -59,7 +59,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
code: code code: code
) )
var computation = newBaseComputation(vmState, message) var computation = newComputation(vmState, message)
computation.executeOpcodes() computation.executeOpcodes()
if not fixture{"post"}.isNil: if not fixture{"post"}.isNil: