mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-24 19:19:21 +00:00
Pass first fixtures tests
Todo Pass most VM opcode tests that we can (of those that don't depend too much on porting the whole py-evm) Simplify a bit the current py-evm-inspired internal loop arch
This commit is contained in:
parent
3a0596bac7
commit
9c056b85de
@ -55,7 +55,8 @@ proc newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputatio
|
||||
result.children = @[]
|
||||
result.accountsToDelete = initTable[string, string]()
|
||||
result.logEntries = @[]
|
||||
result.code = newCodeStream(message.code)
|
||||
result.code = newCodeStreamFromUnescaped(message.code) # TODO: what is the best repr
|
||||
result.rawOutput = "0x"
|
||||
|
||||
method logger*(computation: BaseComputation): Logger =
|
||||
logging.getLogger("vm.computation.BaseComputation")
|
||||
@ -264,7 +265,7 @@ template inComputation*(c: untyped, handler: untyped): untyped =
|
||||
|
||||
|
||||
method getOpcodeFn*(computation: var BaseComputation, op: Op): Opcode =
|
||||
if computation.opcodes.hasKey(op):
|
||||
if computation.opcodes.len > 0 and computation.opcodes.hasKey(op):
|
||||
computation.opcodes[op]
|
||||
else:
|
||||
raise newException(InvalidInstruction,
|
||||
@ -279,7 +280,7 @@ macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
|
||||
result = quote:
|
||||
block:
|
||||
var res: `typName`
|
||||
var c = `name`(`vmState`, `message`)
|
||||
var c = `t` # `name`(`vmState`, `message`)
|
||||
var handler = proc: `typName` =
|
||||
# TODO
|
||||
# if `message`.codeAddress in c.precompiles:
|
||||
@ -290,7 +291,6 @@ macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
|
||||
var opcode = c.getOpcodeFn(op)
|
||||
c.logger.trace(
|
||||
"OPCODE: 0x$1 ($2) | pc: $3" % [opcode.kind.int.toHex(2), $opcode.kind, $max(0, c.code.pc - 1)])
|
||||
|
||||
try:
|
||||
opcode.run(c)
|
||||
except Halt:
|
||||
@ -298,4 +298,4 @@ macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
|
||||
return c
|
||||
inComputation(c):
|
||||
res = handler()
|
||||
res
|
||||
c
|
||||
|
@ -6,11 +6,15 @@ type
|
||||
# TODO db*: JournalDB
|
||||
|
||||
proc newBaseChainDB*(db: MemoryDB): BaseChainDB =
|
||||
new(result)
|
||||
result.db = db
|
||||
|
||||
proc exists*(self: BaseChainDB; key: string): bool =
|
||||
return self.db.exists(key)
|
||||
|
||||
proc `$`*(db: BaseChainDB): string =
|
||||
result = "BaseChainDB"
|
||||
|
||||
# proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
|
||||
# if notself.exists(CANONICALHEADHASHDBKEY):
|
||||
# raise newException(CanonicalHeadNotFound,
|
||||
|
@ -67,11 +67,27 @@ type
|
||||
NotImplementedError* = object of VMError
|
||||
## Not implemented error
|
||||
|
||||
# proc makeVMError*(): VMError =
|
||||
# result.burnsGas = true
|
||||
# result.erasesReturnData = true
|
||||
#proc makeVMError*: ref VMError =
|
||||
# var e: ref VMError
|
||||
# new(e)
|
||||
# e.burnsGas = true
|
||||
# e.erasesReturnData = true
|
||||
#var a: ref VMError
|
||||
#new(a)
|
||||
|
||||
# proc makeRevert*(): Revert =
|
||||
# result.burnsGas = false
|
||||
# result.erasesReturnData = false
|
||||
|
||||
#var e = VMError()
|
||||
#raise makeVMError()
|
||||
#var e: ref VMError
|
||||
#new(e)
|
||||
#echo e[]
|
||||
#proc x* =
|
||||
# raise newException(VMError, "")
|
||||
#var e = makeVMError()
|
||||
#echo e[]
|
||||
|
||||
|
||||
#x()
|
@ -1,5 +1,5 @@
|
||||
import
|
||||
../constants, ../errors, ../computation, .. / db / state_db, .. / vm / [stack, gas_meter, message]
|
||||
../constants, ../errors, ../computation, .. / db / state_db, .. / vm / [stack, gas_meter, message], strformat
|
||||
|
||||
{.this: computation.}
|
||||
{.experimental.}
|
||||
@ -9,8 +9,10 @@ using
|
||||
|
||||
proc sstore*(computation) =
|
||||
let (slot, value) = stack.popInt(2)
|
||||
|
||||
# TODO: stateDB
|
||||
#if value != 0: #and slot == 0:
|
||||
computation.gasMeter.consumeGas(GAS_SSET, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} (TODO)")
|
||||
#else:
|
||||
#computation.gasMeter.consumeGas(GAS_SRESET, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} (TODO)")
|
||||
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||
# current_value = state_db.get_storage(
|
||||
# address=computation.msg.storage_address,
|
||||
|
111
src/opcode_table.nim
Normal file
111
src/opcode_table.nim
Normal file
@ -0,0 +1,111 @@
|
||||
import
|
||||
strformat, strutils, tables, macros,
|
||||
constants, ttmath, errors, logging, vm_state,
|
||||
vm / [gas_meter, stack, code_stream, memory, message, value, gas_costs], 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]
|
||||
|
||||
var OPCODE_TABLE* = initOpcodes:
|
||||
# arithmetic
|
||||
Op.Add: GAS_VERY_LOW add
|
||||
Op.Mul: GAS_LOW mul
|
||||
Op.Sub: GAS_VERY_LOW sub
|
||||
Op.Div: GAS_LOW divide
|
||||
Op.SDiv: GAS_LOW sdiv
|
||||
Op.Mod: GAS_LOW modulo
|
||||
Op.SMod: GAS_LOW smod
|
||||
Op.AddMod: GAS_MID addmod
|
||||
Op.MulMod: GAS_MID mulmod
|
||||
Op.Exp: expGasCost arithmetic.exp
|
||||
Op.SignExtend: GAS_LOW signextend
|
||||
|
||||
|
||||
# comparison
|
||||
Op.Lt: GAS_VERY_LOW lt
|
||||
Op.Gt: GAS_VERY_LOW gt
|
||||
Op.SLt: GAS_VERY_LOW slt
|
||||
Op.SGt: GAS_VERY_LOW sgt
|
||||
Op.Eq: GAS_VERY_LOW eq
|
||||
Op.IsZero: GAS_VERY_LOW iszero
|
||||
Op.And: GAS_VERY_LOW andOp
|
||||
Op.Or: GAS_VERY_LOW orOp
|
||||
Op.Xor: GAS_VERY_LOW xorOp
|
||||
Op.Not: GAS_VERY_LOW notOp
|
||||
Op.Byte: GAS_VERY_LOW byteOp
|
||||
|
||||
|
||||
# sha3
|
||||
Op.SHA3: GAS_SHA3 sha3op
|
||||
|
||||
|
||||
# context
|
||||
Op.Address: GAS_BASE context.address
|
||||
Op.Balance: GAS_COST_BALANCE balance
|
||||
Op.Origin: GAS_BASE origin
|
||||
Op.Caller: GAS_BASE caller
|
||||
Op.CallValue: GAS_BASE callValue
|
||||
Op.CallDataLoad: GAS_VERY_LOW callDataLoad
|
||||
Op.CallDataSize: GAS_BASE callDataSize
|
||||
Op.CallDataCopy: GAS_BASE callDataCopy
|
||||
Op.CodeSize: GAS_BASE codesize
|
||||
Op.CodeCopy: GAS_BASE codecopy
|
||||
Op.ExtCodeSize: GAS_EXT_CODE_COST extCodeSize
|
||||
Op.ExtCodeCopy: GAS_EXT_CODE_COST extCodeCopy
|
||||
|
||||
|
||||
# block
|
||||
Op.Blockhash: GAS_BASE block_ops.blockhash
|
||||
Op.Coinbase: GAS_COINBASE coinbase
|
||||
Op.Timestamp: GAS_BASE timestamp
|
||||
Op.Number: GAS_BASE number
|
||||
Op.Difficulty: GAS_BASE difficulty
|
||||
Op.GasLimit: GAS_BASE gaslimit
|
||||
|
||||
|
||||
# stack
|
||||
Op.Pop: GAS_BASE stack_ops.pop
|
||||
1..32 Op.PushXX: GAS_VERY_LOW pushXX # XX replaced by macro
|
||||
1..16 Op.DupXX: GAS_VERY_LOW dupXX
|
||||
1..16 Op.SwapXX: GAS_VERY_LOW swapXX
|
||||
|
||||
|
||||
# memory
|
||||
Op.MLoad: GAS_VERY_LOW mload
|
||||
Op.MStore: GAS_VERY_LOW mstore
|
||||
Op.MStore8: GAS_VERY_LOW mstore8
|
||||
Op.MSize: GAS_BASE msize
|
||||
|
||||
# storage
|
||||
Op.SLoad: GAS_SLOAD_COST sload
|
||||
Op.SStore: GAS_ZERO sstore
|
||||
|
||||
|
||||
# flow
|
||||
Op.Jump: GAS_MID jump
|
||||
Op.JumpI: GAS_MID jumpi
|
||||
Op.PC: GAS_HIGH pc
|
||||
Op.Gas: GAS_BASE flow.gas
|
||||
Op.JumpDest: GAS_JUMP_DEST jumpdest
|
||||
Op.Stop: GAS_ZERO stop
|
||||
|
||||
|
||||
# logging
|
||||
0..4 Op.LogXX: GAS_IN_HANDLER logXX
|
||||
|
||||
|
||||
# invalid
|
||||
Op.Invalid: GAS_ZERO invalidOp
|
||||
|
||||
|
||||
# system
|
||||
Op.Return: 0.i256 returnOp
|
||||
Op.SelfDestruct: GAS_SELF_DESTRUCT_COST selfdestruct
|
||||
|
||||
|
||||
# call
|
||||
OPCODE_TABLE[Op.Call] = Call(kind: Op.Call)
|
||||
OPCODE_TABLE[Op.CallCode] = CallCode(kind: Op.CallCode)
|
||||
OPCODE_TABLE[Op.DelegateCall] = DelegateCall(kind: Op.DelegateCall)
|
||||
|
||||
|
||||
# system
|
||||
OPCODE_TABLE[Op.Create] = Create(kind: Op.Create)
|
110
src/runner.nim
110
src/runner.nim
@ -1,115 +1,9 @@
|
||||
import
|
||||
strformat, strutils, tables, macros,
|
||||
constants, ttmath, errors, logging, vm_state,
|
||||
constants, ttmath, errors, logging, vm_state, opcode_table,
|
||||
vm / [gas_meter, stack, code_stream, memory, message, value, gas_costs], 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 opcodes = initOpcodes:
|
||||
# arithmetic
|
||||
Op.Add: GAS_VERY_LOW add
|
||||
Op.Mul: GAS_LOW mul
|
||||
Op.Sub: GAS_VERY_LOW sub
|
||||
Op.Div: GAS_LOW divide
|
||||
Op.SDiv: GAS_LOW sdiv
|
||||
Op.Mod: GAS_LOW modulo
|
||||
Op.SMod: GAS_LOW smod
|
||||
Op.AddMod: GAS_MID addmod
|
||||
Op.MulMod: GAS_MID mulmod
|
||||
Op.Exp: expGasCost arithmetic.exp
|
||||
Op.SignExtend: GAS_LOW signextend
|
||||
|
||||
|
||||
# comparison
|
||||
Op.Lt: GAS_VERY_LOW lt
|
||||
Op.Gt: GAS_VERY_LOW gt
|
||||
Op.SLt: GAS_VERY_LOW slt
|
||||
Op.SGt: GAS_VERY_LOW sgt
|
||||
Op.Eq: GAS_VERY_LOW eq
|
||||
Op.IsZero: GAS_VERY_LOW iszero
|
||||
Op.And: GAS_VERY_LOW andOp
|
||||
Op.Or: GAS_VERY_LOW orOp
|
||||
Op.Xor: GAS_VERY_LOW xorOp
|
||||
Op.Not: GAS_VERY_LOW notOp
|
||||
Op.Byte: GAS_VERY_LOW byteOp
|
||||
|
||||
|
||||
# sha3
|
||||
Op.SHA3: GAS_SHA3 sha3op
|
||||
|
||||
|
||||
# context
|
||||
Op.Address: GAS_BASE context.address
|
||||
Op.Balance: GAS_COST_BALANCE balance
|
||||
Op.Origin: GAS_BASE origin
|
||||
Op.Caller: GAS_BASE caller
|
||||
Op.CallValue: GAS_BASE callValue
|
||||
Op.CallDataLoad: GAS_VERY_LOW callDataLoad
|
||||
Op.CallDataSize: GAS_BASE callDataSize
|
||||
Op.CallDataCopy: GAS_BASE callDataCopy
|
||||
Op.CodeSize: GAS_BASE codesize
|
||||
Op.CodeCopy: GAS_BASE codecopy
|
||||
Op.ExtCodeSize: GAS_EXT_CODE_COST extCodeSize
|
||||
Op.ExtCodeCopy: GAS_EXT_CODE_COST extCodeCopy
|
||||
|
||||
|
||||
# block
|
||||
Op.Blockhash: GAS_BASE block_ops.blockhash
|
||||
Op.Coinbase: GAS_COINBASE coinbase
|
||||
Op.Timestamp: GAS_BASE timestamp
|
||||
Op.Number: GAS_BASE number
|
||||
Op.Difficulty: GAS_BASE difficulty
|
||||
Op.GasLimit: GAS_BASE gaslimit
|
||||
|
||||
|
||||
# stack
|
||||
Op.Pop: GAS_BASE stack_ops.pop
|
||||
1..32 Op.PushXX: GAS_VERY_LOW pushXX # XX replaced by macro
|
||||
1..16 Op.DupXX: GAS_VERY_LOW dupXX
|
||||
1..16 Op.SwapXX: GAS_VERY_LOW swapXX
|
||||
|
||||
|
||||
# memory
|
||||
Op.MLoad: GAS_VERY_LOW mload
|
||||
Op.MStore: GAS_VERY_LOW mstore
|
||||
Op.MStore8: GAS_VERY_LOW mstore8
|
||||
Op.MSize: GAS_BASE msize
|
||||
|
||||
# storage
|
||||
Op.SLoad: GAS_SLOAD_COST sload
|
||||
Op.SStore: GAS_ZERO sstore
|
||||
|
||||
|
||||
# flow
|
||||
Op.Jump: GAS_MID jump
|
||||
Op.JumpI: GAS_MID jumpi
|
||||
Op.PC: GAS_HIGH pc
|
||||
Op.Gas: GAS_BASE flow.gas
|
||||
Op.JumpDest: GAS_JUMP_DEST jumpdest
|
||||
Op.Stop: GAS_ZERO stop
|
||||
|
||||
|
||||
# logging
|
||||
0..4 Op.LogXX: GAS_IN_HANDLER logXX
|
||||
|
||||
|
||||
# invalid
|
||||
Op.Invalid: GAS_ZERO invalidOp
|
||||
|
||||
|
||||
# system
|
||||
Op.Return: 0.i256 returnOp
|
||||
Op.SelfDestruct: GAS_SELF_DESTRUCT_COST selfdestruct
|
||||
|
||||
|
||||
# call
|
||||
opcodes[Op.Call] = Call(kind: Op.Call)
|
||||
opcodes[Op.CallCode] = CallCode(kind: Op.CallCode)
|
||||
opcodes[Op.DelegateCall] = DelegateCall(kind: Op.DelegateCall)
|
||||
|
||||
|
||||
# system
|
||||
opcodes[Op.Create] = Create(kind: Op.Create)
|
||||
|
||||
var mem = newMemory(pow(1024.int256, 2))
|
||||
|
||||
var to = toCanonicalAddress("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
|
||||
@ -146,7 +40,7 @@ var c = BaseComputation(
|
||||
logEntries: @[],
|
||||
shouldEraseReturnData: false,
|
||||
accountsToDelete: initTable[string, string](),
|
||||
opcodes: opcodes,
|
||||
opcodes: OPCODE_TABLE,
|
||||
precompiles: initTable[string, Opcode]())
|
||||
|
||||
# var c2 = c.applyComputation(c.vmState, c.msg)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import ../constants, ttmath
|
||||
import ../constants, ttmath, strformat
|
||||
|
||||
type
|
||||
Header* = ref object
|
||||
@ -12,6 +12,12 @@ type
|
||||
|
||||
# TODO
|
||||
|
||||
proc `$`*(header: Header): string =
|
||||
if header.isNil:
|
||||
result = "nil"
|
||||
else:
|
||||
result = &"Header(timestamp: {header.timestamp} difficulty: {header.difficulty} blockNumber: {header.blockNumber} gasLimit: {header.gasLimit})"
|
||||
|
||||
proc generateHeaderFromParentHeader*(
|
||||
computeDifficultyFn: proc(parentHeader: Header, timestamp: int): int,
|
||||
parentHeader: Header,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import
|
||||
strformat, strutils, sequtils, sets, macros,
|
||||
strformat, strutils, sequtils, parseutils, sets, macros,
|
||||
../logging, ../constants, ../opcode_values
|
||||
|
||||
type
|
||||
@ -24,6 +24,16 @@ proc newCodeStream*(codeBytes: seq[byte]): CodeStream =
|
||||
proc newCodeStream*(codeBytes: string): CodeStream =
|
||||
newCodeStream(codeBytes.mapIt(it.byte))
|
||||
|
||||
proc newCodeStreamFromUnescaped*(code: string): CodeStream =
|
||||
# from 0xunescaped
|
||||
var codeBytes: seq[byte] = @[]
|
||||
for z, c in code[2..^1]:
|
||||
if z mod 2 == 1:
|
||||
var value: int
|
||||
discard parseHex(&"0x{code[z+1..z+2]}", value)
|
||||
codeBytes.add(value.byte)
|
||||
newCodeStream(codeBytes)
|
||||
|
||||
proc read*(c: var CodeStream, size: int): seq[byte] =
|
||||
if c.pc + size - 1 < c.bytes.len:
|
||||
result = c.bytes[c.pc .. c.pc + size - 1]
|
||||
|
@ -2,11 +2,18 @@ import
|
||||
logging, constants, errors, vm_state, utils/header, db/db_chain
|
||||
|
||||
type
|
||||
FrontierVMState* = object of BaseVMState
|
||||
FrontierVMState* = ref object of BaseVMState
|
||||
# receipts*:
|
||||
# computationClass*: Any
|
||||
# accessLogs*: AccessLogs
|
||||
|
||||
proc newFrontierVMState*: FrontierVMState =
|
||||
new(result)
|
||||
result.prevHeaders = @[]
|
||||
result.name = "FrontierVM"
|
||||
result.accessLogs = newAccessLogs()
|
||||
result.blockHeader = Header(hash: "TODO", coinbase: "TODO", stateRoot: "TODO")
|
||||
|
||||
# import
|
||||
# py2nim_helpers, __future__, rlp, evm, evm.constants, evm.exceptions, evm.rlp.logs,
|
||||
# evm.rlp.receipts, evm.vm.message, evm.vm_state, evm.utils.address,
|
||||
|
@ -23,3 +23,4 @@ proc newFrontierVM*(header: Header, chainDB: BaseChainDB): FrontierVM =
|
||||
new(result)
|
||||
result.chainDB = chainDB
|
||||
result.isStateless = true
|
||||
result.state = newFrontierVMState()
|
||||
|
@ -22,11 +22,18 @@ proc update*[K, V](t: var Table[K, V], elements: Table[K, V]) =
|
||||
for k, v in elements:
|
||||
t[k] = v
|
||||
|
||||
proc `$`*(vmState: BaseVMState): string =
|
||||
if vmState.isNil:
|
||||
result = "nil"
|
||||
else:
|
||||
result = &"VMState {vmState.name}:\n header: {vmState.blockHeader}\n chaindb: {vmState.chaindb}"
|
||||
|
||||
proc newBaseVMState*: BaseVMState =
|
||||
new(result)
|
||||
result.prevHeaders = @[]
|
||||
result.name = "BaseVM"
|
||||
result.accessLogs = newAccessLogs()
|
||||
result.blockHeader = Header(hash: "TODO", coinbase: "TODO", stateRoot: "TODO")
|
||||
|
||||
method logger*(vmState: BaseVMState): Logger =
|
||||
logging.getLogger(&"evm.vmState.{vmState.name}")
|
||||
@ -65,6 +72,7 @@ macro db*(vmState: untyped, readOnly: untyped, handler: untyped): untyped =
|
||||
let db = ident("db")
|
||||
result = quote:
|
||||
block:
|
||||
echo `vmState`
|
||||
var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`)
|
||||
`handler`
|
||||
if `readOnly`:
|
||||
|
@ -1,5 +1,5 @@
|
||||
import
|
||||
os, macros, json, strformat, strutils, ttmath, utils / [hexadecimal, address], chain, vm_state, constants, db / [db_chain, state_db], vm / forks / frontier / vm
|
||||
os, macros, json, strformat, strutils, ttmath, utils / [hexadecimal, address], chain, vm_state, constants, db / [db_chain, state_db], vm / forks / frontier / vm, parseutils
|
||||
|
||||
# TODO
|
||||
# This block is a child of the genesis defined in the chain fixture above and contains a single tx
|
||||
@ -56,14 +56,21 @@ import
|
||||
#
|
||||
proc generateTest(filename: string, handler: NimNode): NimNode =
|
||||
echo filename
|
||||
let testStatusIMPL = ident("testStatusIMPL")
|
||||
result = quote:
|
||||
test `filename`:
|
||||
`handler`(parseJSON(readFile(`filename`)))
|
||||
`handler`(parseJSON(readFile(`filename`)), `testStatusIMPL`)
|
||||
|
||||
macro jsonTest*(s: static[string], handler: untyped): untyped =
|
||||
result = nnkStmtList.newTree()
|
||||
for filename in walkDir(&"tests/{s}", relative=true):
|
||||
result.add(generateTest(filename.path, handler))
|
||||
echo &"tests/fixtures/{s}"
|
||||
var z = 0
|
||||
for filename in walkDirRec(&"tests/fixtures/{s}"):
|
||||
if "mul" in filename:
|
||||
if z >= 4:
|
||||
break
|
||||
result.add(generateTest(filename, handler))
|
||||
z += 1
|
||||
|
||||
proc setupStateDB*(desiredState: JsonNode, stateDB: var AccountStateDB) =
|
||||
for account, accountData in desiredState:
|
||||
@ -77,3 +84,6 @@ proc setupStateDB*(desiredState: JsonNode, stateDB: var AccountStateDB) =
|
||||
stateDB.setNonce(account, nonce)
|
||||
stateDB.setCode(account, code)
|
||||
stateDB.setBalance(account, balance)
|
||||
|
||||
proc getHexadecimalInt*(j: JsonNode): int =
|
||||
discard parseHex(j.getStr, result)
|
@ -1,38 +1,55 @@
|
||||
import
|
||||
unittest, strformat, strutils, sequtils, tables, ttmath, json,
|
||||
helpers, constants, errors, logging,
|
||||
chain, vm_state, computation, opcode, utils / header, vm / [gas_meter, message, code_stream], vm / forks / frontier / vm, db / [db_chain, state_db], db / backends / memory_backend
|
||||
test_helpers, constants, errors, logging,
|
||||
chain, vm_state, computation, opcode, opcode_table, utils / header, vm / [gas_meter, message, code_stream], vm / forks / frontier / vm, db / [db_chain, state_db], db / backends / memory_backend
|
||||
|
||||
|
||||
proc testFixture(fixture: JsonNode)
|
||||
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus)
|
||||
|
||||
suite "vm json tests":
|
||||
jsonTest("VMTests", testFixture)
|
||||
|
||||
proc testFixture(fixture: JsonNode) =
|
||||
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
||||
var fixture: JsonNode
|
||||
for label, child in fixtures:
|
||||
fixture = child
|
||||
break
|
||||
var vm = newFrontierVM(Header(), newBaseChainDB(newMemoryDB()))
|
||||
let header = Header(
|
||||
coinbase: fixture{"env"}{"currentCoinbase"}.getStr,
|
||||
difficulty: fixture{"env"}{"currentDifficulty"}.getInt.i256,
|
||||
blockNumber: fixture{"env"}{"currentNumber"}.getInt.i256,
|
||||
gasLimit: fixture{"env"}{"currentGasLimit"}.getInt.i256,
|
||||
timestamp: fixture{"env"}{"currentTimestamp"}.getInt)
|
||||
difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.i256,
|
||||
blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.i256,
|
||||
gasLimit: fixture{"env"}{"currentGasLimit"}.getHexadecimalInt.i256,
|
||||
timestamp: fixture{"env"}{"currentTimestamp"}.getHexadecimalInt)
|
||||
|
||||
var code = ""
|
||||
vm.state.db(readOnly=false):
|
||||
setupStateDB(fixture{"pre"}, db)
|
||||
code = db.getCode(fixture{"exec"}{"address"}.getStr)
|
||||
|
||||
code = fixture{"exec"}{"code"}.getStr
|
||||
let message = newMessage(
|
||||
to=fixture{"exec"}{"address"}.getStr,
|
||||
sender=fixture{"exec"}{"caller"}.getStr,
|
||||
value=fixture{"exec"}{"value"}.getInt.i256,
|
||||
value=fixture{"exec"}{"value"}.getHexadecimalInt.i256,
|
||||
data=fixture{"exec"}{"data"}.getStr.mapIt(it.byte),
|
||||
code=code,
|
||||
gas=fixture{"exec"}{"gas"}.getInt.i256,
|
||||
gasPrice=fixture{"exec"}{"gasPrice"}.getInt.i256,
|
||||
gas=fixture{"exec"}{"gas"}.getHexadecimalInt.i256,
|
||||
gasPrice=fixture{"exec"}{"gasPrice"}.getHexadecimalInt.i256,
|
||||
options=newMessageOptions(origin=fixture{"exec"}{"origin"}.getStr))
|
||||
let computation = newBaseComputation(vm.state, message).applyComputation(vm.state, message)
|
||||
|
||||
echo fixture{"exec"}
|
||||
var c = newCodeStreamFromUnescaped(code)
|
||||
var opcodes = c.decompile
|
||||
for opcode in opcodes:
|
||||
echo opcode[0], " ", opcode[1], " ", opcode[2]
|
||||
|
||||
var computation = newBaseComputation(vm.state, message)
|
||||
computation.accountsToDelete = initTable[string, string]()
|
||||
computation.opcodes = OPCODE_TABLE
|
||||
computation.precompiles = initTable[string, Opcode]()
|
||||
|
||||
computation = computation.applyComputation(vm.state, message)
|
||||
|
||||
if not fixture{"post"}.isNil:
|
||||
# Success checks
|
||||
@ -50,10 +67,9 @@ proc testFixture(fixture: JsonNode) =
|
||||
|
||||
let expectedOutput = fixture{"out"}.getStr
|
||||
check(computation.output == expectedOutput)
|
||||
|
||||
let gasMeter = computation.gasMeter
|
||||
|
||||
let expectedGasRemaining = fixture{"gas"}.getInt.i256
|
||||
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt.i256
|
||||
let actualGasRemaining = gasMeter.gasRemaining
|
||||
let gasDelta = actualGasRemaining - expectedGasRemaining
|
||||
check(gasDelta == 0)
|
||||
@ -69,8 +85,8 @@ proc testFixture(fixture: JsonNode) =
|
||||
var (childComputation, createdCall) = child
|
||||
let toAddress = createdCall{"destination"}.getStr
|
||||
let data = createdCall{"data"}.getStr.mapIt(it.byte)
|
||||
let gasLimit = createdCall{"gasLimit"}.getInt.i256
|
||||
let value = createdCall{"value"}.getInt.i256
|
||||
let gasLimit = createdCall{"gasLimit"}.getHexadecimalInt.i256
|
||||
let value = createdCall{"value"}.getHexadecimalInt.i256
|
||||
|
||||
check(childComputation.msg.to == toAddress)
|
||||
check(data == childComputation.msg.data or childComputation.msg.code.len > 0)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import
|
||||
unittest,
|
||||
helpers, .. / src / [db / backends / memory, db / chain, constants, utils / hexadecimal]
|
||||
test_helpers, .. / src / [db / backends / memory, db / chain, constants, utils / hexadecimal]
|
||||
|
||||
suite "vm":
|
||||
test "apply no validation":
|
||||
|
Loading…
x
Reference in New Issue
Block a user