mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-03 15:55:47 +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.children = @[]
|
||||||
result.accountsToDelete = initTable[string, string]()
|
result.accountsToDelete = initTable[string, string]()
|
||||||
result.logEntries = @[]
|
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 =
|
method logger*(computation: BaseComputation): Logger =
|
||||||
logging.getLogger("vm.computation.BaseComputation")
|
logging.getLogger("vm.computation.BaseComputation")
|
||||||
@ -264,7 +265,7 @@ template inComputation*(c: untyped, handler: untyped): untyped =
|
|||||||
|
|
||||||
|
|
||||||
method getOpcodeFn*(computation: var BaseComputation, op: Op): Opcode =
|
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]
|
computation.opcodes[op]
|
||||||
else:
|
else:
|
||||||
raise newException(InvalidInstruction,
|
raise newException(InvalidInstruction,
|
||||||
@ -279,7 +280,7 @@ macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
|
|||||||
result = quote:
|
result = quote:
|
||||||
block:
|
block:
|
||||||
var res: `typName`
|
var res: `typName`
|
||||||
var c = `name`(`vmState`, `message`)
|
var c = `t` # `name`(`vmState`, `message`)
|
||||||
var handler = proc: `typName` =
|
var handler = proc: `typName` =
|
||||||
# TODO
|
# TODO
|
||||||
# if `message`.codeAddress in c.precompiles:
|
# if `message`.codeAddress in c.precompiles:
|
||||||
@ -290,7 +291,6 @@ macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
|
|||||||
var opcode = c.getOpcodeFn(op)
|
var opcode = c.getOpcodeFn(op)
|
||||||
c.logger.trace(
|
c.logger.trace(
|
||||||
"OPCODE: 0x$1 ($2) | pc: $3" % [opcode.kind.int.toHex(2), $opcode.kind, $max(0, c.code.pc - 1)])
|
"OPCODE: 0x$1 ($2) | pc: $3" % [opcode.kind.int.toHex(2), $opcode.kind, $max(0, c.code.pc - 1)])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opcode.run(c)
|
opcode.run(c)
|
||||||
except Halt:
|
except Halt:
|
||||||
@ -298,4 +298,4 @@ macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
|
|||||||
return c
|
return c
|
||||||
inComputation(c):
|
inComputation(c):
|
||||||
res = handler()
|
res = handler()
|
||||||
res
|
c
|
||||||
|
@ -6,11 +6,15 @@ type
|
|||||||
# TODO db*: JournalDB
|
# TODO db*: JournalDB
|
||||||
|
|
||||||
proc newBaseChainDB*(db: MemoryDB): BaseChainDB =
|
proc newBaseChainDB*(db: MemoryDB): BaseChainDB =
|
||||||
|
new(result)
|
||||||
result.db = db
|
result.db = db
|
||||||
|
|
||||||
proc exists*(self: BaseChainDB; key: string): bool =
|
proc exists*(self: BaseChainDB; key: string): bool =
|
||||||
return self.db.exists(key)
|
return self.db.exists(key)
|
||||||
|
|
||||||
|
proc `$`*(db: BaseChainDB): string =
|
||||||
|
result = "BaseChainDB"
|
||||||
|
|
||||||
# proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
|
# proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
|
||||||
# if notself.exists(CANONICALHEADHASHDBKEY):
|
# if notself.exists(CANONICALHEADHASHDBKEY):
|
||||||
# raise newException(CanonicalHeadNotFound,
|
# raise newException(CanonicalHeadNotFound,
|
||||||
|
@ -67,11 +67,27 @@ type
|
|||||||
NotImplementedError* = object of VMError
|
NotImplementedError* = object of VMError
|
||||||
## Not implemented error
|
## Not implemented error
|
||||||
|
|
||||||
# proc makeVMError*(): VMError =
|
#proc makeVMError*: ref VMError =
|
||||||
# result.burnsGas = true
|
# var e: ref VMError
|
||||||
# result.erasesReturnData = true
|
# new(e)
|
||||||
|
# e.burnsGas = true
|
||||||
|
# e.erasesReturnData = true
|
||||||
|
#var a: ref VMError
|
||||||
|
#new(a)
|
||||||
|
|
||||||
# proc makeRevert*(): Revert =
|
# proc makeRevert*(): Revert =
|
||||||
# result.burnsGas = false
|
# result.burnsGas = false
|
||||||
# result.erasesReturnData = 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
|
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.}
|
{.this: computation.}
|
||||||
{.experimental.}
|
{.experimental.}
|
||||||
@ -9,8 +9,10 @@ using
|
|||||||
|
|
||||||
proc sstore*(computation) =
|
proc sstore*(computation) =
|
||||||
let (slot, value) = stack.popInt(2)
|
let (slot, value) = stack.popInt(2)
|
||||||
|
#if value != 0: #and slot == 0:
|
||||||
# TODO: stateDB
|
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:
|
# with computation.vm_state.state_db(read_only=True) as state_db:
|
||||||
# current_value = state_db.get_storage(
|
# current_value = state_db.get_storage(
|
||||||
# address=computation.msg.storage_address,
|
# 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
|
import
|
||||||
strformat, strutils, tables, macros,
|
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],
|
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]
|
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 mem = newMemory(pow(1024.int256, 2))
|
||||||
|
|
||||||
var to = toCanonicalAddress("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
|
var to = toCanonicalAddress("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
|
||||||
@ -146,7 +40,7 @@ var c = BaseComputation(
|
|||||||
logEntries: @[],
|
logEntries: @[],
|
||||||
shouldEraseReturnData: false,
|
shouldEraseReturnData: false,
|
||||||
accountsToDelete: initTable[string, string](),
|
accountsToDelete: initTable[string, string](),
|
||||||
opcodes: opcodes,
|
opcodes: OPCODE_TABLE,
|
||||||
precompiles: initTable[string, Opcode]())
|
precompiles: initTable[string, Opcode]())
|
||||||
|
|
||||||
# var c2 = c.applyComputation(c.vmState, c.msg)
|
# var c2 = c.applyComputation(c.vmState, c.msg)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import ../constants, ttmath
|
import ../constants, ttmath, strformat
|
||||||
|
|
||||||
type
|
type
|
||||||
Header* = ref object
|
Header* = ref object
|
||||||
@ -12,6 +12,12 @@ type
|
|||||||
|
|
||||||
# TODO
|
# 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*(
|
proc generateHeaderFromParentHeader*(
|
||||||
computeDifficultyFn: proc(parentHeader: Header, timestamp: int): int,
|
computeDifficultyFn: proc(parentHeader: Header, timestamp: int): int,
|
||||||
parentHeader: Header,
|
parentHeader: Header,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import
|
import
|
||||||
strformat, strutils, sequtils, sets, macros,
|
strformat, strutils, sequtils, parseutils, sets, macros,
|
||||||
../logging, ../constants, ../opcode_values
|
../logging, ../constants, ../opcode_values
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -24,6 +24,16 @@ proc newCodeStream*(codeBytes: seq[byte]): CodeStream =
|
|||||||
proc newCodeStream*(codeBytes: string): CodeStream =
|
proc newCodeStream*(codeBytes: string): CodeStream =
|
||||||
newCodeStream(codeBytes.mapIt(it.byte))
|
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] =
|
proc read*(c: var CodeStream, size: int): seq[byte] =
|
||||||
if c.pc + size - 1 < c.bytes.len:
|
if c.pc + size - 1 < c.bytes.len:
|
||||||
result = c.bytes[c.pc .. c.pc + size - 1]
|
result = c.bytes[c.pc .. c.pc + size - 1]
|
||||||
|
@ -2,11 +2,18 @@ import
|
|||||||
logging, constants, errors, vm_state, utils/header, db/db_chain
|
logging, constants, errors, vm_state, utils/header, db/db_chain
|
||||||
|
|
||||||
type
|
type
|
||||||
FrontierVMState* = object of BaseVMState
|
FrontierVMState* = ref object of BaseVMState
|
||||||
# receipts*:
|
# receipts*:
|
||||||
# computationClass*: Any
|
# computationClass*: Any
|
||||||
# accessLogs*: AccessLogs
|
# 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
|
# import
|
||||||
# py2nim_helpers, __future__, rlp, evm, evm.constants, evm.exceptions, evm.rlp.logs,
|
# py2nim_helpers, __future__, rlp, evm, evm.constants, evm.exceptions, evm.rlp.logs,
|
||||||
# evm.rlp.receipts, evm.vm.message, evm.vm_state, evm.utils.address,
|
# 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)
|
new(result)
|
||||||
result.chainDB = chainDB
|
result.chainDB = chainDB
|
||||||
result.isStateless = true
|
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:
|
for k, v in elements:
|
||||||
t[k] = v
|
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 =
|
proc newBaseVMState*: BaseVMState =
|
||||||
new(result)
|
new(result)
|
||||||
result.prevHeaders = @[]
|
result.prevHeaders = @[]
|
||||||
result.name = "BaseVM"
|
result.name = "BaseVM"
|
||||||
result.accessLogs = newAccessLogs()
|
result.accessLogs = newAccessLogs()
|
||||||
|
result.blockHeader = Header(hash: "TODO", coinbase: "TODO", stateRoot: "TODO")
|
||||||
|
|
||||||
method logger*(vmState: BaseVMState): Logger =
|
method logger*(vmState: BaseVMState): Logger =
|
||||||
logging.getLogger(&"evm.vmState.{vmState.name}")
|
logging.getLogger(&"evm.vmState.{vmState.name}")
|
||||||
@ -65,6 +72,7 @@ macro db*(vmState: untyped, readOnly: untyped, handler: untyped): untyped =
|
|||||||
let db = ident("db")
|
let db = ident("db")
|
||||||
result = quote:
|
result = quote:
|
||||||
block:
|
block:
|
||||||
|
echo `vmState`
|
||||||
var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`)
|
var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`)
|
||||||
`handler`
|
`handler`
|
||||||
if `readOnly`:
|
if `readOnly`:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import
|
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
|
# TODO
|
||||||
# This block is a child of the genesis defined in the chain fixture above and contains a single tx
|
# 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 =
|
proc generateTest(filename: string, handler: NimNode): NimNode =
|
||||||
echo filename
|
echo filename
|
||||||
|
let testStatusIMPL = ident("testStatusIMPL")
|
||||||
result = quote:
|
result = quote:
|
||||||
test `filename`:
|
test `filename`:
|
||||||
`handler`(parseJSON(readFile(`filename`)))
|
`handler`(parseJSON(readFile(`filename`)), `testStatusIMPL`)
|
||||||
|
|
||||||
macro jsonTest*(s: static[string], handler: untyped): untyped =
|
macro jsonTest*(s: static[string], handler: untyped): untyped =
|
||||||
result = nnkStmtList.newTree()
|
result = nnkStmtList.newTree()
|
||||||
for filename in walkDir(&"tests/{s}", relative=true):
|
echo &"tests/fixtures/{s}"
|
||||||
result.add(generateTest(filename.path, handler))
|
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) =
|
proc setupStateDB*(desiredState: JsonNode, stateDB: var AccountStateDB) =
|
||||||
for account, accountData in desiredState:
|
for account, accountData in desiredState:
|
||||||
@ -77,3 +84,6 @@ proc setupStateDB*(desiredState: JsonNode, stateDB: var AccountStateDB) =
|
|||||||
stateDB.setNonce(account, nonce)
|
stateDB.setNonce(account, nonce)
|
||||||
stateDB.setCode(account, code)
|
stateDB.setCode(account, code)
|
||||||
stateDB.setBalance(account, balance)
|
stateDB.setBalance(account, balance)
|
||||||
|
|
||||||
|
proc getHexadecimalInt*(j: JsonNode): int =
|
||||||
|
discard parseHex(j.getStr, result)
|
@ -1,38 +1,55 @@
|
|||||||
import
|
import
|
||||||
unittest, strformat, strutils, sequtils, tables, ttmath, json,
|
unittest, strformat, strutils, sequtils, tables, ttmath, json,
|
||||||
helpers, constants, errors, logging,
|
test_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
|
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":
|
suite "vm json tests":
|
||||||
jsonTest("VMTests", testFixture)
|
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()))
|
var vm = newFrontierVM(Header(), newBaseChainDB(newMemoryDB()))
|
||||||
let header = Header(
|
let header = Header(
|
||||||
coinbase: fixture{"env"}{"currentCoinbase"}.getStr,
|
coinbase: fixture{"env"}{"currentCoinbase"}.getStr,
|
||||||
difficulty: fixture{"env"}{"currentDifficulty"}.getInt.i256,
|
difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.i256,
|
||||||
blockNumber: fixture{"env"}{"currentNumber"}.getInt.i256,
|
blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.i256,
|
||||||
gasLimit: fixture{"env"}{"currentGasLimit"}.getInt.i256,
|
gasLimit: fixture{"env"}{"currentGasLimit"}.getHexadecimalInt.i256,
|
||||||
timestamp: fixture{"env"}{"currentTimestamp"}.getInt)
|
timestamp: fixture{"env"}{"currentTimestamp"}.getHexadecimalInt)
|
||||||
|
|
||||||
var code = ""
|
var code = ""
|
||||||
vm.state.db(readOnly=false):
|
vm.state.db(readOnly=false):
|
||||||
setupStateDB(fixture{"pre"}, db)
|
setupStateDB(fixture{"pre"}, db)
|
||||||
code = db.getCode(fixture{"exec"}{"address"}.getStr)
|
code = db.getCode(fixture{"exec"}{"address"}.getStr)
|
||||||
|
|
||||||
|
code = fixture{"exec"}{"code"}.getStr
|
||||||
let message = newMessage(
|
let message = newMessage(
|
||||||
to=fixture{"exec"}{"address"}.getStr,
|
to=fixture{"exec"}{"address"}.getStr,
|
||||||
sender=fixture{"exec"}{"caller"}.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),
|
data=fixture{"exec"}{"data"}.getStr.mapIt(it.byte),
|
||||||
code=code,
|
code=code,
|
||||||
gas=fixture{"exec"}{"gas"}.getInt.i256,
|
gas=fixture{"exec"}{"gas"}.getHexadecimalInt.i256,
|
||||||
gasPrice=fixture{"exec"}{"gasPrice"}.getInt.i256,
|
gasPrice=fixture{"exec"}{"gasPrice"}.getHexadecimalInt.i256,
|
||||||
options=newMessageOptions(origin=fixture{"exec"}{"origin"}.getStr))
|
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:
|
if not fixture{"post"}.isNil:
|
||||||
# Success checks
|
# Success checks
|
||||||
@ -50,10 +67,9 @@ proc testFixture(fixture: JsonNode) =
|
|||||||
|
|
||||||
let expectedOutput = fixture{"out"}.getStr
|
let expectedOutput = fixture{"out"}.getStr
|
||||||
check(computation.output == expectedOutput)
|
check(computation.output == expectedOutput)
|
||||||
|
|
||||||
let gasMeter = computation.gasMeter
|
let gasMeter = computation.gasMeter
|
||||||
|
|
||||||
let expectedGasRemaining = fixture{"gas"}.getInt.i256
|
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt.i256
|
||||||
let actualGasRemaining = gasMeter.gasRemaining
|
let actualGasRemaining = gasMeter.gasRemaining
|
||||||
let gasDelta = actualGasRemaining - expectedGasRemaining
|
let gasDelta = actualGasRemaining - expectedGasRemaining
|
||||||
check(gasDelta == 0)
|
check(gasDelta == 0)
|
||||||
@ -69,8 +85,8 @@ proc testFixture(fixture: JsonNode) =
|
|||||||
var (childComputation, createdCall) = child
|
var (childComputation, createdCall) = child
|
||||||
let toAddress = createdCall{"destination"}.getStr
|
let toAddress = createdCall{"destination"}.getStr
|
||||||
let data = createdCall{"data"}.getStr.mapIt(it.byte)
|
let data = createdCall{"data"}.getStr.mapIt(it.byte)
|
||||||
let gasLimit = createdCall{"gasLimit"}.getInt.i256
|
let gasLimit = createdCall{"gasLimit"}.getHexadecimalInt.i256
|
||||||
let value = createdCall{"value"}.getInt.i256
|
let value = createdCall{"value"}.getHexadecimalInt.i256
|
||||||
|
|
||||||
check(childComputation.msg.to == toAddress)
|
check(childComputation.msg.to == toAddress)
|
||||||
check(data == childComputation.msg.data or childComputation.msg.code.len > 0)
|
check(data == childComputation.msg.data or childComputation.msg.code.len > 0)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import
|
import
|
||||||
unittest,
|
unittest,
|
||||||
helpers, .. / src / [db / backends / memory, db / chain, constants, utils / hexadecimal]
|
test_helpers, .. / src / [db / backends / memory, db / chain, constants, utils / hexadecimal]
|
||||||
|
|
||||||
suite "vm":
|
suite "vm":
|
||||||
test "apply no validation":
|
test "apply no validation":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user