Reorganize VM/interpreter + cleanup (#52)

* Move and cleanup interpreter files - prepare for redesign of VM

* fix call comment aobut recursive dependencies

* memory: use a template again and avoid (?) a cstring-> string conversion

* Fix stack test regression

* Fix recursive dependency on logging_ops, test_vm_json compiles but regression :/

* Fix signextend regression

* Fix 3 signed test and sha3 test
This commit is contained in:
Mamy Ratsimbazafy 2018-06-15 11:11:25 +02:00 committed by GitHub
parent 05275f9773
commit c26c751f9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 171 additions and 593 deletions

View File

@ -1,106 +0,0 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
tables, stint, eth_common, eth_keys,
./logging, ./constants, ./errors, ./validation, ./utils/hexadecimal, ./vm/base, ./db/db_chain,
./utils/header, ./vm/forks/f20150730_frontier/frontier_vm
type
Chain* = ref object
## An Chain is a combination of one or more VM classes. Each VM is associated
## with a range of blocks. The Chain class acts as a wrapper around these other
## VM classes, delegating operations to the appropriate VM depending on the
## current block number.
header*: BlockHeader
logger*: Logger
networkId*: string
vmsByRange*: seq[tuple[blockNumber: UInt256, vmk: VMkind]] # TODO: VM should actually be a runtime typedesc(VM)
importBlock*: bool
validateBlock*: bool
db*: BaseChainDB
fundedAddress*: EthAddress
fundedAddressInitialBalance*: int
fundedAddressPrivateKey*: PrivateKey
GenesisParams* = ref object
blockNumber*: BlockNumber
difficulty*: UInt256
gasLimit*: GasInt
parentHash*: Hash256
coinbase*: EthAddress
nonce*: string
mixHash*: Hash256
extraData*: string
timestamp*: EthTime
stateRoot*: Hash256
FundedAddress* = ref object
balance*: Int256
nonce*: int
code*: string
proc configureChain*(name: string, blockNumber: UInt256, vmk: VMKind, importBlock: bool = true, validateBlock: bool = true): Chain =
new(result)
result.vmsByRange = @[(blockNumber: blockNumber, vmk: vmk)]
result.importBlock = importBlock
result.validateBlock = validateBlock
proc fromGenesis*(
chain: Chain,
chainDB: BaseChainDB,
genesisParams: GenesisParams,
genesisState: Table[string, FundedAddress]): Chain =
## Initialize the Chain from a genesis state
var stateDB = chaindb.getStateDB(BLANK_ROOT_HASH)
# TODO
# for account, accountData in genesisState:
# stateDB.setBalance(account, accountData.balance)
# stateDB.setNonce(account, accountData.nonce)
# stateDB.setCode(account, accountData.code)
new(result)
result.db = chainDB
result.header = BlockHeader()
result.logger = logging.getLogger("evm.chain.chain.Chain")
result.importBlock = chain.importBlock
result.validateBlock = chain.validateBlock
result.vmsByRange = chain.vmsByRange
# TODO
# chainDB.persistBlockToDB(result.getBlock)
proc getVMClassForBlockNumber*(chain: Chain, blockNumber: BlockNumber): VMKind =
## Returns the VM class for the given block number
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
# TODO - Refactoring: redundant with constants.nim `toFork`
# TODO should the return value be a typedesc?
# TODO: validate_block_number
for idx in countdown(chain.vmsByRange.high, chain.vmsByRange.low):
let (n, vmk) = chain.vmsByRange[idx]
if blockNumber >= n:
return vmk
raise newException(ValueError, "VM not found for block #" & $blockNumber) # TODO: VMNotFound exception
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
proc getVM*(chain: Chain, header: BlockHeader): VM =
## Returns the VM instance for the given block number
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
# TODO - Refactoring: redundant with constants.nim `toFork`
# shadowing input param
let vm_class = chain.getVMClassForBlockNumber(header.blockNumber)
case vm_class:
of vmkFrontier: result = newFrontierVM(header, chain.db)
else:
raise newException(ValueError, "Chain: only FrontierVM is implemented")
proc getVM*(chain: Chain): VM {.inline.} = getVM(chain, chain.header)

View File

@ -2,103 +2,6 @@
import
stint, math, strutils, utils/padding, eth_common
proc int256*(i: int): Int256 =
i.i256
# template i256*(i: int): Int256 =
# i.initBigInt
template i256*(i: Int256): Int256 =
i
template u256*(i: int): UInt256 =
i.uint.u256
template u256*(i: UInt256): UInt256 =
i
template getInt*(i: int): int =
i
# TODO
# We'll have a fast fixed i256, for now this works
proc `==`*(a: Int256, b: int): bool =
a == b.i256
proc `!=`*(a: Int256, b: int): bool =
a != b.i256
proc `==`*(a: UInt256, b: int): bool =
a == b.u256
proc `!=`*(a: UInt256, b: int): bool =
a != b.u256
# proc `^`*(base: int; exp: int): UInt256 =
# let base = base.u256
# var ex = exp
# result = 1.u256
# while ex > 0:
# result = result * base
# dec(ex)
proc `^`*(left: Int256, right: int): Int256 =
var value = right.i256
result = 1.i256
var m = right.i256
while value > 0.i256:
result = result * m
value -= 1.i256
proc `^`*(left: UInt256, right: UInt256): UInt256 =
var value = right
result = 1.u256
var m = right.u256
while value > 0.u256:
result = result * m
value -= 1.u256
proc `^`*(left: UInt256, right: int): UInt256 =
left ^ right.u256
proc `>`*(a: Int256, b: int): bool =
a > b.i256
proc `<`*(a: Int256, b: int): bool =
a < b.i256
proc `>`*(a: UInt256, b: int): bool =
a > b.u256
proc `<`*(a: UInt256, b: int): bool =
a < b.u256
proc `mod`*(a: Int256, b: int): Int256 =
a mod b.i256
proc `div`*(a: Int256, b: int): Int256 =
a div b.i256
proc `mod`*(a: UInt256, b: int): UInt256 =
a mod b.u256
proc `div`*(a: UInt256, b: int): UInt256 =
a div b.u256
template mapOp(op: untyped): untyped =
proc `op`*(left: Int256, right: int): Int256 =
result = left.i256
result = `op`(result, right.i256)
proc `op`*(left: UInt256, right: int): UInt256 =
result = left.u256
result = `op`(result, right.u256)
mapOp(`and`)
mapOp(`or`)
mapOp(`xor`)
proc default(t: typedesc): t = discard
# constants
@ -108,7 +11,7 @@ let
INT_256_MAX_AS_UINT256* = cast[Uint256](high(Int256))
NULLBYTE* = "\x00"
EMPTYWORD* = repeat(NULLBYTE, 32)
UINT160CEILING*: UInt256 = 2.u256 ^ 160
UINT160CEILING*: UInt256 = 2.u256.pow(160)
ZERO_ADDRESS* = default(EthAddress)
CREATE_CONTRACT_ADDRESS* = ZERO_ADDRESS
ZERO_HASH32* = Hash256()
@ -133,14 +36,6 @@ let
MAX_UNCLE_DEPTH* = 6.u256
MAX_UNCLES* = 2.u256
SECPK1_P*: UInt256 = 2.u256 ^ 256 - 2.u256 ^ 32 - 977.u256
SECPK1_N*: UInt256 = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256
SECPK1_A* = 0.u256
SECPK1_B* = 7.u256
SECPK1_Gx* = 0.u256
SECPK1_Gy* = 0.u256
SECPK1_G* = (SECPK1Gx, SECPK1Gy)
EMPTY_UNCLE_HASH* = "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".toDigest
GENESIS_BLOCK_NUMBER* = 0.u256

View File

@ -6,9 +6,8 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
eth_common, tables,
../constants, ../errors, ../validation, ../account, ../logging, ../utils_numeric, .. / utils / [padding, bytes, keccak],
stint, rlp
eth_common, tables, nimcrypto, stint, rlp,
../constants, ../errors, ../validation, ../account, ../logging
type
AccountStateDB* = ref object
@ -64,11 +63,11 @@ proc setStorage*(db: var AccountStateDB, address: EthAddress, slot: UInt256, val
# let account = db.getAccount(address)
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot))
let slotAsKey = slot.intToBigEndian.pad32.toString
let slotAsKey = "0x" & slot.dumpHex # Convert to a number to hex big-endian representation including prefix and leading zeros
var storage = db.db
# TODO fix
if value > 0:
let encodedValue = rlp.encode value.intToBigEndian
let encodedValue = rlp.encode value.toByteArrayBE
storage[slotAsKey] = encodedValue
else:
storage.del(slotAsKey)
@ -87,13 +86,11 @@ proc getStorage*(db: var AccountStateDB, address: EthAddress, slot: UInt256): (U
# let account = db.GetAccount(address)
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot))
let slotAsKey = slot.intToBigEndian.pad32.toString
let slotAsKey = "0x" & slot.dumpHex # Convert to a number to hex big-endian representation including prefix and leading zeros
var storage = db.db
if storage.hasKey(slotAsKey):
#result = storage[slotAsKey]
# XXX: `bigEndianToInt` can be refactored to work with a BytesRange/openarray
# Then we won't need to call `toSeq` here.
result = (storage[slotAsKey].toSeq.bigEndianToInt, true)
let byteRange = storage[slotAsKey]
result = (readUintBE[256](byteRange.toOpenArray), true)
else:
result = (0.u256, false)
@ -117,7 +114,7 @@ proc setCode*(db: var AccountStateDB, address: EthAddress, code: string) =
var account = db.getAccount(address)
account.codeHash = keccak(code)
account.codeHash = keccak256.digest code
#db.db[account.codeHash] = code
db.setAccount(address, account)

View File

@ -51,7 +51,7 @@ type
## Error signaling that an account has insufficient funds to transfer the
## requested value.
StackDepthLimit* = object of VMError
StackDepthError* = object of VMError
## Error signaling that the call stack has exceeded it's maximum allowed depth.
ContractCreationCollision* = object of VMError

View File

@ -1 +0,0 @@
# --warning[XDeclaredButNotUsed]:off

View File

@ -1,34 +0,0 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, strutils, sequtils, macros,
constants, logging, errors, opcode_values, computation, vm/stack, stint,
./vm_types
# Super dirty fix for https://github.com/status-im/nimbus/issues/46
# Pending https://github.com/status-im/nimbus/issues/36
# Disentangle opcode logic
from logic.call import runLogic, BaseCall
template run*(opcode: Opcode, computation: var BaseComputation) =
# Hook for performing the actual VM execution
# opcode.consumeGas(computation)
if opcode.kind == Op.Call: # Super dirty fix for https://github.com/status-im/nimbus/issues/46
# TODO remove this branch
runLogic(BaseCall(opcode), computation)
elif computation.gasCosts[opcode.kind].kind != GckFixed:
opcode.runLogic(computation)
else:
computation.gasMeter.consumeGas(computation.gasCosts[opcode.kind].cost, reason = $opcode.kind)
opcode.runLogic(computation)
method logger*(opcode: Opcode): Logger =
logging.getLogger(&"vm.opcode.{opcode.kind}")

View File

@ -1,93 +0,0 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, strutils, tables, macros,
constants, stint, errors, logging, vm_state, opcode_table,
vm / [gas_meter, stack, code_stream, memory, message, value], db / chain, computation, opcode, opcode_values, utils / [header, address],
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops]
var mem = newMemory(pow(1024.int256, 2))
var to = toCanonicalAddress("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
var sender = toCanonicalAddress("0xcd1722f3947def4cf144679da39c4c32bdc35681")
var code = ""
var data: seq[byte] = @[]
var msg = newMessage(
25.int256,
1.int256,
to,
sender,
0.int256,
data,
code,
MessageOptions(depth: 1))
var c = BaseComputation(
vmState: BaseVMState(
prevHeaders: @[],
chaindb: BaseChainDB(),
blockHeader: BlockHeader(),
name: "zero"),
msg: msg,
memory: mem,
stack: newStack(),
gasMeter: newGasMeter(msg.gas),
code: newCodeStream(code),
children: @[],
rawOutput: "",
returnData: "",
error: nil,
logEntries: @[],
shouldEraseReturnData: false,
accountsToDelete: initTable[string, string](),
opcodes: OPCODE_TABLE,
precompiles: initTable[string, Opcode]())
# var c2 = c.applyComputation(c.vmState, c.msg)
macro runOpcodes*(computation: untyped, program: untyped): untyped =
# runOpcodes(c):
# stack: @[Value..]
#
# Op
# Op
#
# becomes
#
# c.stack.push(Value) ..
#
# c.getOpcodeFn(Op).run(c)
# echo c.stack ..
var stack = nnkStmtList.newTree()
for child in program[0][1][0][1]:
let push = quote:
`computation`.stack.push(`child`)
stack.add(push)
var ops = nnkStmtList.newTree()
for z, op in program:
if z > 0:
let run = quote:
`computation`.getOpcodeFn(`op`).run(`computation`)
echo `computation`.stack
ops.add(run)
result = nnkStmtList.newTree(stack, ops)
# useful for testing simple cases
runOpcodes(c):
stack: @[2.vint, 2.vint, 2.vint, 2.vint, 2.vint, 2.vint, 4.vint]
Op.Add
Op.Mul
Op.Div
Op.Sub
Op.Mul

View File

@ -24,5 +24,3 @@ proc toString*(value: seq[byte]): string =
proc toBytes*(value: string): seq[byte] =
result = value.mapIt(it.byte)

View File

@ -92,4 +92,8 @@ proc generateHeaderFromParentHeader*(
# TODO: data: extraData,
)
import nimcrypto
# TODO: required otherwise
# eth_common/rlp_serialization.nim(18, 12) template/generic instantiation from here
# nimcrypto/hash.nim(46, 6) Error: attempting to call undeclared routine: 'init'
proc hash*(b: BlockHeader): Hash256 {.inline.} = rlpHash(b)

View File

@ -1,20 +0,0 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# import
# eth_utils, rlp, trie, evm.db.backends.memory, evm.db.chain
# proc makeTrieRootAndNodes*(transactions: auto; trieClass: auto): auto =
# var
# chaindb = BaseChainDB(MemoryDB())
# db = chaindb.db
# transactionDb = trieClass(db)
# for index, transaction in transactions:
# var indexKey = rlp.encode(index)
# transactionDb[indexKey] = rlp.encode(transaction)
# return (transactionDb.rootHash, transactionDb.db.wrappedDb.kvStore)

View File

@ -6,7 +6,9 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../logging, ../constants, ../errors, ../transaction, ../vm_types, ../computation, ../block_types, ../vm_state, ../vm_state_transactions, ../db/db_chain, ../utils/header
../logging, ../constants, ../errors, ../transaction, ../vm_types,
../block_types, ../vm_state, ../vm_state_transactions, ../db/db_chain, ../utils/header,
./computation
type
VMkind* = enum

View File

@ -7,7 +7,7 @@
import
strformat, strutils, sequtils, parseutils, sets, macros,
../logging, ../constants, ../opcode_values
../logging, ../constants, ./interpreter/opcode_values
type
CodeStream* = ref object

View File

@ -7,13 +7,13 @@
import
strformat, strutils, sequtils, macros, stint, terminal, math, eth_common, byteutils, tables,
./constants, ./errors, ./utils/hexadecimal, ./utils_numeric, ./validation, ./vm_state, ./logging, ./opcode_values, ./vm_types,
./vm/[code_stream, gas_meter, memory, message, stack],
../constants, ../errors, ../validation, ../vm_state, ../logging, ../vm_types,
./interpreter/[opcode_values,gas_meter, gas_costs],
./code_stream, ./memory, ./message, ./stack,
# TODO further refactoring of gas cost
vm/forks/gas_costs,
vm/forks/f20150730_frontier/frontier_vm_state,
vm/forks/f20161018_tangerine_whistle/tangerine_vm_state
./forks/f20150730_frontier/frontier_vm_state,
./forks/f20161018_tangerine_whistle/tangerine_vm_state
method newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation {.base.}=
raise newException(ValueError, "Must be implemented by subclasses")
@ -239,6 +239,27 @@ method getOpcodeFn*(computation: var BaseComputation, op: Op): Opcode =
raise newException(InvalidInstruction,
&"Invalid opcode {op}")
# Super dirty fix for https://github.com/status-im/nimbus/issues/46
# Pending https://github.com/status-im/nimbus/issues/36
# Disentangle opcode logic
from ./interpreter/opcodes_impl/call import runLogic, BaseCall
template run*(opcode: Opcode, computation: var BaseComputation) =
# Hook for performing the actual VM execution
# opcode.consumeGas(computation)
if opcode.kind == Op.Call: # Super dirty fix for https://github.com/status-im/nimbus/issues/46
# TODO remove this branch
runLogic(BaseCall(opcode), computation)
elif computation.gasCosts[opcode.kind].kind != GckFixed:
opcode.runLogic(computation)
else:
computation.gasMeter.consumeGas(computation.gasCosts[opcode.kind].cost, reason = $opcode.kind)
opcode.runLogic(computation)
method logger*(opcode: Opcode): Logger =
logging.getLogger(&"vm.opcode.{opcode.kind}")
macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
# Perform the computation that would be triggered by the VM message
# c.applyComputation(vmState, message)

View File

@ -0,0 +1 @@
Folder to be deleted

View File

@ -5,14 +5,20 @@
# * 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.
import strutils
import
./interpreter/[opcode_values, gas_meter, opcode_table],
./interpreter/vm_forks
# proc encodeHex*(value: string): string =
# # return "0x" & codecs.decode(codecs.encode(value, "hex"), "utf8")
# return value
from utils/utils_numeric import bigEndianToInt
# proc decodeHex*(value: string): string =
# # var hexPart = value.rsplit("x", 1)[1]
# return value
# # return codecs.decode(hexPart, "hex")
import # Used in vm_types. Beware of recursive dependencies
./code_stream, ./computation, ./stack, ./message
export
opcode_values, gas_meter, opcode_table,
vm_forks
export utils_numeric.bigEndianToInt
export
code_stream, computation, stack, message

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -6,10 +6,9 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
stint, eth_common, math,
../../utils/macros_gen_opcodes,
../../opcode_values,
../../utils_numeric
stint, math, eth_common, # GasInt
../utils/[macros_gen_opcodes, utils_numeric],
./opcode_values
# Gas Fee Schedule
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf

View File

@ -6,8 +6,8 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat,
../logging, ../errors, ../vm_types
strformat, eth_common, # GasInt
../../logging, ../../errors, ../../vm_types
proc newGasMeter*(startGas: GasInt): GasMeter =
new(result)

View File

@ -6,11 +6,9 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
tables,
stint, logging,
vm / [gas_meter, stack, code_stream, memory, message], db / db_chain, computation, opcode, opcode_values, utils / [header, address],
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops],
./vm_types
tables, stint,
../../vm_types, ./opcode_values,
opcodes_impl/[arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops]
const
OpLogic*: Table[Op, proc(computation: var BaseComputation){.nimcall.}] = {

View File

@ -5,7 +5,7 @@
# * 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.
import ./utils/macros_gen_opcodes
import ../utils/macros_gen_opcodes
fill_enum_holes:
type

View File

@ -6,9 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../constants, ../utils_numeric, ../computation, ../vm_types,
.. / vm / [gas_meter, stack], ../opcode, ../opcode_values,
helpers, stint, strutils
helpers, stint, strutils, ./impl_std_import
proc add*(computation: var BaseComputation) =
# Addition
@ -107,7 +105,7 @@ proc signextend*(computation: var BaseComputation) =
if bits <= 31.u256:
let testBit = bits.toInt * 8 + 7
let bitPos = (1 shl testBit)
let mask = bitPos - 1
let mask = u256(bitPos - 1)
if not (value and bitPos).isZero:
res = value or (not mask)
else:

View File

@ -6,8 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
times,
../constants, ../errors, ../computation, ../vm_state, ../vm_types, .. / vm / [stack], stint
times, ./impl_std_import
{.this: computation.}
{.experimental.}

View File

@ -6,11 +6,15 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, eth_common,
../constants, ../vm_types, ../errors, ../computation, ../opcode_values, ../logging,
.. / vm / [stack, memory, gas_meter, message],
.. / utils / [address, bytes],
stint
strformat, eth_common, stint,
# ./impl_std_import # Cannot do that due to recursive dependencies
# .../vm/interpreter/opcodes_impl/impl_std_import.nim imports .../vm/computation.nim
# .../vm/computation.nim imports .../vm/interpreter/opcodes_impl/call.nim
# .../vm/interpreter/opcodes_impl/call.nim imports .../vm/interpreter/opcodes_impl/impl_std_import.nim
../../../constants, ../../../vm_types, ../../../errors, ../../../logging,
../../../utils/bytes,
../../computation, ../../stack, ../../memory, ../../message,
../opcode_values, ../gas_meter, ../gas_costs
type
# TODO most of these are for gas handling
@ -68,7 +72,7 @@ method runLogic*(call: BaseCall, computation) =
let senderBalance = 0.u256
let insufficientFunds = shouldTransferValue and senderBalance < value
let stackTooDeep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT
let stackTooDeep = computation.msg.depth + 1 > STACK_DEPTH_LIMIT
if insufficientFunds or stackTooDeep:
computation.returnData = ""

View File

@ -6,8 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../constants, ../utils_numeric, ../computation, ../vm/stack, ../vm_types,
helpers, stint
helpers, ./impl_std_import
quasiBoolean(lt, `<`) # Lesser Comparison
@ -35,7 +34,7 @@ proc iszero*(computation: var BaseComputation) =
proc notOp*(computation: var BaseComputation) =
var value = computation.stack.popInt()
var res = constants.UINT_256_MAX - value
var res = UINT_256_MAX - value
pushRes()
# TODO: seems like there is an implementation or a comment issue

View File

@ -7,9 +7,7 @@
import
strformat,
../constants, ../vm_types, ../errors, ../utils_numeric, ../computation, ../vm_state, ../account, ../db/state_db, ../validation,
.. /vm/[stack, message, gas_meter, memory, code_stream], ../utils/[address, padding, bytes], stint,
../opcode_values
./impl_std_import
proc balance*(computation: var BaseComputation) =
let address = computation.stack.popAddress()

View File

@ -6,8 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
macros, strformat,
../vm_types, ../computation, ../vm/stack
macros, strformat, ./impl_std_import
macro dupXX(position: static[int]): untyped =
let name = ident(&"dup{position}")

View File

@ -6,9 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, stint,
../constants, ../vm_types, ../opcode_values, ../logging, ../errors, ../computation, .. /vm / [code_stream, stack]
strformat, ./impl_std_import
{.this: computation.}
{.experimental.}

View File

@ -5,7 +5,7 @@
# * 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.
import macros
import macros, stint
template pushRes*: untyped =
computation.stack.push(res)
@ -22,8 +22,8 @@ macro quasiBoolean*(name: untyped, op: untyped, signed: untyped = nil, nonzero:
actualLeftNode = ident("leftSigned")
actualRightNode = ident("rightSigned")
signedNode = quote:
let `actualLeftNode` = unsignedToSigned(`leftNode`)
let `actualRightNode` = unsignedToSigned(`rightNode`)
let `actualLeftNode` = cast[Int256](`leftNode`)
let `actualRightNode` = cast[Int256](`rightNode`)
var test = if nonzero.isNil:
quote:
`op`(`actualLeftNode`, `actualRightNode`)

View File

@ -0,0 +1,22 @@
# 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.
import
stint, stint/lenient_stint,
../../../constants, ../../../vm_state, ../../../vm_types, ../../../vm_types,
../../../errors, ../../../logging, ../../../utils/padding, ../../../utils/bytes,
../../stack, ../../computation, ../../stack, ../../memory, ../../message,
../../code_stream, ../../utils/utils_numeric,
../opcode_values, ../gas_meter, ../gas_costs
export
stint, lenient_stint,
constants, vm_state, vm_types, vm_types,
errors, logging, padding, bytes,
stack, computation, stack, memory, message,
code_stream, utils_numeric,
opcode_values, gas_meter, gas_costs

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../errors, ../vm_types, ../computation
./impl_std_import
proc invalidOp*(computation: var BaseComputation) =
raise newException(InvalidInstruction, "Invalid opcode")

View File

@ -7,9 +7,7 @@
import
strformat, macros,
../constants, ../errors, ../vm_types, ../computation, ../vm/[stack, memory, gas_meter, message], ../utils/bytes,
../opcode_values,
stint
./impl_std_import
{.this: computation.}
{.experimental.}
@ -53,19 +51,18 @@ macro logXX(topicCount: static[int]): untyped =
result.body.add(topicCode)
let OpName = ident(&"Log{topicCount}")
let logicCode = quote:
let logicCode = quote do:
`computation`.gasMeter.consumeGas(
`computation`.gasCosts[`OpName`].m_handler(`computation`.memory.len, `memPos`, `len`),
reason="Memory expansion, Log topic and data gas cost")
`computation`.memory.extend(`memPos`, `len`)
let logData = `computation`.memory.read(`memPos`, `len`).toString
`computation`.addLogEntry(
account=`computation`.msg.storageAddress,
topics=`topics`,
data=log_data)
account = `computation`.msg.storageAddress,
topics = `topics`,
data = log_data)
result.body.add(logicCode)
# echo result.repr
logXX(0)
logXX(1)

View File

@ -6,10 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../constants, ../computation, ../vm_types, ../vm/[stack, memory, gas_meter], .. /utils/[padding, bytes],
../opcode_values,
stint
./impl_std_import
{.this: computation.}
{.experimental.}

View File

@ -6,21 +6,19 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../constants, ../utils_numeric, ../utils/[keccak, bytes], ../vm/[stack, memory, gas_meter],
../computation, ../vm_types, ../opcode_values,
./helpers,
stint
nimcrypto,
./impl_std_import, ./helpers
proc sha3op*(computation: var BaseComputation) =
let (startPosition, size) = computation.stack.popInt(2)
let (pos, len) = (startPosition.toInt, size.toInt)
computation.gasMeter.consumeGas(
computation.gasCosts[Sha3].m_handler(computation.memory.len, pos, len),
computation.gasCosts[Op.Sha3].m_handler(computation.memory.len, pos, len),
reason="SHA3: word gas cost"
)
computation.memory.extend(pos, len)
var res = keccak("") # TODO: stub
var res = keccak256.digest("") # TODO: stub
pushRes()

View File

@ -6,8 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, macros, sequtils,
../vm_types, ../constants, ../errors, ../computation, .. / vm / [stack, code_stream], .. / utils / [padding, bytes], stint
strformat, macros, ./impl_std_import
{.this: computation.}
{.experimental.}

View File

@ -6,11 +6,10 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../constants, ../vm_types, ../errors, ../computation, ../vm_state,
../utils/header,
../db/[db_chain, state_db], ../vm/[stack, gas_meter, message],
../opcode_values,
strformat, stint
./impl_std_import, strformat,
../../../utils/header,
../../../db/[db_chain, state_db]
{.this: computation.}
{.experimental.}

View File

@ -6,8 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
macros, strformat,
../vm_types, ../computation, ../vm/stack
macros, strformat, ./impl_std_import
macro swapXX(position: static[int]): untyped =
let name = ident(&"swap{position}")

View File

@ -7,9 +7,7 @@
import
strformat,
../constants, ../vm_types, ../errors, ../computation, ../opcode, ../opcode_values, ../logging, ../vm_state, call,
.. / vm / [stack, gas_meter, memory, message], .. / utils / [address, hexadecimal, bytes],
../opcode_values,
./call, ./impl_std_import,
stint, byteutils, eth_common
{.this: computation.}
@ -70,7 +68,7 @@ method runLogic*(create: Create, computation) =
let childMsg = computation.prepareChildMessage(
gas=0, # TODO refactor gas
to=constants.CREATE_CONTRACT_ADDRESS,
to=CREATE_CONTRACT_ADDRESS,
value=value,
data=cast[seq[byte]](@[]),
code=callData.toString,

View File

@ -9,8 +9,8 @@ import
../../db/db_chain, ../../constants,
../../utils/header,
../base,
./f20150730_frontier/frontier_vm,
./f20161018_tangerine_whistle/tangerine_vm,
../forks/f20150730_frontier/frontier_vm,
../forks/f20161018_tangerine_whistle/tangerine_vm,
stint
# Note (mamy): refactoring is in progress (2018-05-23), this is redundant with

View File

@ -7,7 +7,8 @@
import
sequtils, stint,
../constants, ../errors, ../logging, ../validation, ../utils_numeric, ../utils/bytes
../constants, ../errors, ../logging, ../validation, ../utils/bytes,
./utils/utils_numeric
type
Memory* = ref object
@ -57,3 +58,7 @@ proc write*(memory: var Memory, startPosition: Natural, value: openarray[byte])
template write*(memory: var Memory, startPosition: Natural, size: Natural, value: cstring) =
memory.write(startPosition, value.toBytes)
# TODO ~ O(n^3):
# - there is a string allocation with $ (?)
# - then a conversion to seq (= new allocation) with toBytes
# - then writing to memory

View File

@ -7,7 +7,7 @@
import
strformat, strutils, sequtils, macros, rlp, eth_common, nimcrypto,
../errors, ../validation, ../utils_numeric, ../constants, stint, ../logging, .. / utils / bytes
../errors, ../validation, ./utils/utils_numeric, ../constants, stint, ../logging, .. / utils / bytes
type
Stack* = ref object of RootObj
@ -99,6 +99,8 @@ proc swap*(stack: var Stack, position: int) =
raise newException(InsufficientStack,
&"Insufficient stack items for SWAP{position}")
template getint(x: int): int = x
proc dup*(stack: var Stack, position: int | UInt256) =
## Perform a DUP operation on the stack
let position = position.getInt

View File

@ -5,14 +5,12 @@
# * 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.
import stint, constants, strformat, strutils, sequtils, endians, macros, utils / padding, rlp
import
stint, strformat, strutils, sequtils, endians, macros, rlp,
../../constants, ../../utils/padding
# some methods based on py-evm utils/numeric
proc intToBigEndian*(value: UInt256): Bytes {.deprecated.} =
result = newSeq[byte](32)
result[0 .. ^1] = value.toByteArrayBE()
proc bigEndianToInt*(value: openarray[byte]): UInt256 =
if value.len == 32:
readUintBE[256](value)
@ -22,14 +20,6 @@ proc bigEndianToInt*(value: openarray[byte]): UInt256 =
proc log256*(value: UInt256): Natural =
(255 - value.countLeadingZeroBits) div 8 # Compilers optimize to `shr 3`
proc unsignedToSigned*(value: UInt256): Int256 =
0.i256
# TODO Remove stub (used in quasiBoolean for signed comparison)
proc signedToUnsigned*(value: Int256): UInt256 =
0.u256
# TODO Remove stub (used in quasiBoolean for signed comparison)
proc unsignedToPseudoSigned*(value: UInt256): UInt256 =
result = value
if value > INT_256_MAX_AS_UINT256:

View File

@ -9,7 +9,7 @@ import
macros, strformat, tables,
stint, eth_common,
./logging, ./constants, ./errors, ./transaction, ./db/[db_chain, state_db],
./utils/[state, header]
./utils/header
type
BaseVMState* = ref object of RootObj

View File

@ -7,7 +7,8 @@
import
strformat, tables,
logging, constants, errors, computation, transaction, vm_types, vm_state, block_types, db / db_chain, utils / [state, header]
./logging, ./constants, ./errors, ./vm/computation,
./transaction, ./vm_types, ./vm_state, ./block_types, ./db/db_chain, ./utils/header
method executeTransaction(vmState: var BaseVMState, transaction: BaseTransaction): (BaseComputation, BlockHeader) {.base.}=
# Execute the transaction in the vm

View File

@ -6,13 +6,11 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
tables,
constants, vm_state,
opcode_values, stint, eth_common,
vm / [code_stream, memory, stack, forks/gas_costs],
./logging
tables, stint, eth_common,
./constants, ./vm_state, ./logging,
./vm/[memory, stack, code_stream],
./vm/interpreter/[gas_costs, opcode_values] # TODO - will be hidden at a lower layer
export GasInt, gas_costs
type
BaseComputation* = ref object of RootObj
@ -32,7 +30,7 @@ type
accountsToDelete*: Table[EthAddress, EthAddress]
opcodes*: Table[Op, proc(computation: var BaseComputation){.nimcall.}]
precompiles*: Table[string, Opcode]
gasCosts*: GasCosts # TODO - avoid allocating memory for this const
gasCosts*: GasCosts # TODO - will be hidden at a lower layer
Error* = ref object
info*: string

View File

@ -10,5 +10,4 @@ import ./test_code_stream,
./test_memory,
./test_stack,
./test_opcode
# ./test_vm
# ./test_vm_json

View File

@ -1,38 +0,0 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
unittest, strformat, tables, times,
stint, eth_keys, eth_common,
../nimbus/[constants, chain, vm/base, vm/forks/f20150730_frontier/frontier_vm, utils/header, utils/address, db/db_chain, db/backends/memory_backend]
proc chainWithoutBlockValidation*: Chain =
result = configureChain("TestChain", GENESIS_BLOCK_NUMBER, vmkFrontier, false, false)
let privateKey = initPrivateKey("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
let fundedAddr = privateKey.getPublicKey.toCanonicalAddress
let initialBalance = 100_000_000
let genesisParams = GenesisParams(
blockNumber: GENESIS_BLOCK_NUMBER,
difficulty: GENESIS_DIFFICULTY,
gasLimit: GENESIS_GAS_LIMIT,
parentHash: GENESIS_PARENT_HASH,
coinbase: GENESIS_COINBASE,
nonce: GENESIS_NONCE,
mixHash: GENESIS_MIX_HASH,
extraData: GENESIS_EXTRA_DATA,
timestamp: fromUnix 1501851927,
stateRoot: "9d354f9b5ba851a35eced279ef377111387197581429cfcc7f744ef89a30b5d4".toDigest)
let genesisState = {"fundedAddr": FundedAddress(balance: initialBalance.int256, nonce: 0, code: "")}.toTable()
result = fromGenesis(
result,
newBaseChainDB(newMemoryDB()),
genesisParams,
genesisState)
result.fundedAddress = fundedAddr
result.fundedAddressInitialBalance = initialBalance
result.fundedAddressPrivateKey = privateKey

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import unittest, strutils, sequtils,
../nimbus/opcode_values, ../nimbus/vm/code_stream
../nimbus/vm/interpreter
suite "parse bytecode":
test "accepts bytes":

View File

@ -5,9 +5,9 @@
# * 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.
import unittest, macros, strformat, strutils, sequtils,
import unittest, macros, strformat,
stint,
../nimbus/[constants, opcode_values, errors, logging, vm_types, vm/gas_meter]
../nimbus/[vm_types, errors, logging, vm/interpreter]
# TODO: quicktest
# PS: parametrize can be easily immitated, but still quicktests would be even more useful

View File

@ -8,9 +8,9 @@
import
os, macros, json, strformat, strutils, parseutils, ospaths, tables,
stint, byteutils, eth_common, eth_keys,
../nimbus/utils/[hexadecimal, address, padding],
../nimbus/[chain, vm_state, constants],
../nimbus/db/[db_chain, state_db], ../nimbus/vm/forks/f20150730_frontier/frontier_vm,
../nimbus/utils/[address, padding],
../nimbus/[vm_state, constants],
../nimbus/db/[db_chain, state_db],
../nimbus/vm/base, ../nimbus/transaction
type

View File

@ -5,9 +5,9 @@
# * 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.
import unittest, macros, strformat, strutils, sequtils,
import unittest, sequtils,
stint,
../nimbus/[constants, opcode_values, errors, vm/memory]
../nimbus/[constants, errors, vm/memory]
proc memory32: Memory =
result = newMemory()

View File

@ -7,14 +7,13 @@
import
unittest, stint, tables, parseutils,
../nimbus/[constants, vm_types, errors, logging],
../nimbus/[chain, vm_state, computation, opcode, opcode_table],
../nimbus/[utils/header, utils/padding],
../nimbus/vm/[gas_meter, message, code_stream, stack],
../nimbus/vm/forks/vm_forks,
../nimbus/[constants, vm_types, logging],
../nimbus/vm/interpreter,
../nimbus/utils/header,
../nimbus/db/[db_chain, state_db, backends/memory_backend],
test_helpers
./test_helpers
from eth_common import GasInt
proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputation =
let header = BlockHeader(blockNumber: blockNum)

View File

@ -5,9 +5,8 @@
# * 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.
import unittest, macros, strformat, strutils, sequtils,
stint,
../nimbus/[constants, opcode_values, errors, utils_numeric, vm/stack, utils/bytes, utils/padding]
import unittest, stint,
../nimbus/[constants, errors, vm/interpreter, utils/bytes]
template testPush(value: untyped, expected: untyped): untyped =
@ -36,9 +35,6 @@ suite "stack":
expect(FullStack):
stack.push(1025)
test "dup does not allow stack to exceed 1024":
var stack = newStack()
stack.push(1.u256)

View File

@ -1,43 +0,0 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
unittest, stint, eth_common,
./test_helpers, ./fixtures,
../nimbus/[db/backends/memory_backend, db/state_db, chain, constants, utils/hexadecimal, vm_state],
../nimbus/[vm/base, computation]
import typetraits
suite "VM":
test "Apply transaction with no validation":
var
chain = chainWithoutBlockValidation()
vm = chain.getVM()
# txIdx = len(vm.`block`.transactions) # Can't take len of a runtime field
let
recipient = parseAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0c")
amount = 100.u256
ethaddr_from = chain.fundedAddress
tx = newTransaction(vm, ethaddr_from, recipient, amount, chain.fundedAddressPrivateKey)
# (computation, _) = vm.applyTransaction(tx)
# accessLogs = computation.vmState.accessLogs
# check(not computation.isError)
let
# txGas = tx.gasPrice * constants.GAS_TX
state_db = vm.state.readOnlyStateDB
b = vm.`block`
echo state_db.getBalance(ethaddr_from).type.name
# check:
# state_db.getBalance(ethaddr_from) == chain.fundedAddressInitialBalance - amount - txGas # TODO: this really should be i256
# state_db.getBalance(recipient) == amount
# b.transactions[txIdx] == tx
# b.header.gasUsed == constants.GAS_TX

View File

@ -9,14 +9,12 @@ import
unittest, strformat, strutils, sequtils, tables, stint, json, ospaths, times,
./test_helpers,
../nimbus/[constants, errors, logging],
../nimbus/[chain, vm_state, computation, opcode, vm_types],
../nimbus/[vm_state, vm_types],
../nimbus/utils/[header, padding],
../nimbus/vm/[gas_meter, message, code_stream, stack],
../nimbus/vm/forks/vm_forks, ../nimbus/db/[db_chain, state_db, backends/memory_backend],
../nimbus/vm/interpreter,
../nimbus/db/[db_chain, state_db, backends/memory_backend],
eth_common
from ../nimbus/opcode_table import OpLogic
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus)
suite "vm json tests":
@ -47,7 +45,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
let message = newMessage(
to = fexec{"address"}.getStr.parseAddress,
sender = fexec{"caller"}.getStr.parseAddress,
value = fexec{"value"}.getHexadecimalInt.u256,
value = cast[uint](fexec{"value"}.getHexadecimalInt).u256, # Cast workaround for negative value
data = fexec{"data"}.getStr.mapIt(it.byte),
code = code,
gas = fexec{"gas"}.getHexadecimalInt,