general-state testsuite changes and better fork selection
- skipped the tests in allowedFailInCurrentBuild() - replaced doAssert() with check() in testFixtureIndexes() so we can see both hash values on failure - checking filename extension for JSON tests to avoid editor swap files - replaced the duplicated block values in the main net's ChainConfig with values from forkBlocks - allowed overriding the current fork in computations, because the old strategy of only looking at the block number doesn't work with JSON tests where the block number is usually 1 - explicitly pass the fork to gasCosts() and use it for conditional cost calculation - fixed a logic error in the CREATE opcode - fixed VM selection based on current fork in updateOpcodeExec() - single point of control for supported forks in tests (just one fork, at the moment) - 44 new test failures (that were probably passing for the wrong reasons)
This commit is contained in:
parent
8f0a78e52f
commit
115843487c
1142
GeneralStateTests.md
1142
GeneralStateTests.md
File diff suppressed because it is too large
Load Diff
|
@ -9,7 +9,8 @@
|
|||
|
||||
import
|
||||
parseopt, strutils, macros, os,
|
||||
asyncdispatch2, eth_keys, eth_p2p, eth_common, chronicles, nimcrypto/hash
|
||||
asyncdispatch2, eth_keys, eth_p2p, eth_common, chronicles, nimcrypto/hash,
|
||||
./vm/interpreter/vm_forks
|
||||
|
||||
const
|
||||
NimbusName* = "Nimbus"
|
||||
|
@ -175,14 +176,14 @@ proc publicChainConfig*(id: PublicNetwork): ChainConfig =
|
|||
of MainNet:
|
||||
ChainConfig(
|
||||
chainId: MainNet.uint,
|
||||
homesteadBlock: 1150000.u256,
|
||||
daoForkBlock: 1920000.u256,
|
||||
homesteadBlock: forkBlocks[FkHomestead],
|
||||
daoForkBlock: forkBlocks[FkDao],
|
||||
daoForkSupport: true,
|
||||
eip150Block: 2463000.u256,
|
||||
eip150Block: forkBlocks[FkTangerine],
|
||||
eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
|
||||
eip155Block: 2675000.u256,
|
||||
eip158Block: 2675000.u256,
|
||||
byzantiumBlock: 4370000.u256
|
||||
eip155Block: forkBlocks[FkSpurious],
|
||||
eip158Block: forkBlocks[FkSpurious],
|
||||
byzantiumBlock: forkBlocks[FkByzantium]
|
||||
)
|
||||
of RopstenNet:
|
||||
ChainConfig(
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
chronicles, strformat, strutils, sequtils, macros, terminal, math, tables,
|
||||
chronicles, strformat, strutils, sequtils, macros, terminal, math, tables, options,
|
||||
eth_common,
|
||||
../constants, ../errors, ../validation, ../vm_state, ../vm_types,
|
||||
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
||||
|
@ -17,7 +17,7 @@ import
|
|||
logScope:
|
||||
topics = "vm computation"
|
||||
|
||||
proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Message): BaseComputation =
|
||||
proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Message, forkOverride=none(Fork)): BaseComputation =
|
||||
new result
|
||||
result.vmState = vmState
|
||||
result.msg = message
|
||||
|
@ -29,7 +29,12 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me
|
|||
result.logEntries = @[]
|
||||
result.code = newCodeStream(message.code)
|
||||
# result.rawOutput = "0x"
|
||||
result.gasCosts = blockNumber.toFork.forkToSchedule
|
||||
result.gasCosts =
|
||||
if forkOverride.isSome:
|
||||
forkOverride.get.forkToSchedule
|
||||
else:
|
||||
blockNumber.toFork.forkToSchedule
|
||||
result.forkOverride = forkOverride
|
||||
|
||||
proc isOriginComputation*(c: BaseComputation): bool =
|
||||
# Is this computation the computation initiated by a transaction
|
||||
|
@ -209,7 +214,8 @@ proc generateChildComputation*(fork: Fork, computation: BaseComputation, childMs
|
|||
var childComp = newBaseComputation(
|
||||
computation.vmState,
|
||||
computation.vmState.blockHeader.blockNumber,
|
||||
childMsg)
|
||||
childMsg,
|
||||
some(fork))
|
||||
|
||||
# Copy the fork op code executor proc (assumes child computation is in the same fork)
|
||||
childComp.opCodeExec = computation.opCodeExec
|
||||
|
@ -235,9 +241,16 @@ proc addChildComputation(fork: Fork, computation: BaseComputation, child: BaseCo
|
|||
computation.returnData = child.output
|
||||
computation.children.add(child)
|
||||
|
||||
proc getFork*(computation: BaseComputation): Fork =
|
||||
result =
|
||||
if computation.forkOverride.isSome:
|
||||
computation.forkOverride.get
|
||||
else:
|
||||
computation.vmState.blockHeader.blockNumber.toFork
|
||||
|
||||
proc applyChildComputation*(computation: BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
|
||||
## Apply the vm message childMsg as a child computation.
|
||||
let fork = computation.vmState.blockHeader.blockNumber.toFork
|
||||
let fork = computation.getFork
|
||||
result = fork.generateChildComputation(computation, childMsg, opCode)
|
||||
fork.addChildComputation(computation, result)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import
|
|||
# Gas Fee Schedule
|
||||
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf
|
||||
type
|
||||
GasFeeKind = enum
|
||||
GasFeeKind* = enum
|
||||
GasZero, # Nothing paid for operations of the set Wzero.
|
||||
GasBase, # Amount of gas to pay for operations of the set Wbase.
|
||||
GasVeryLow, # Amount of gas to pay for operations of the set Wverylow.
|
||||
|
@ -102,11 +102,13 @@ type
|
|||
|
||||
GasCosts* = array[Op, GasCost]
|
||||
|
||||
template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyped) =
|
||||
template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||
|
||||
## Generate the gas cost for each forks and store them in a const
|
||||
## named `ResultGasCostsName`
|
||||
|
||||
const FeeSchedule = gasFees[fork]
|
||||
|
||||
# ############### Helper functions ##############################
|
||||
|
||||
func `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength: Natural): GasInt {.inline.} =
|
||||
|
@ -151,7 +153,6 @@ template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyp
|
|||
## Computes all but 1/64th
|
||||
## L(n) ≡ n − ⌊n/64⌋ - (floored(n/64))
|
||||
# Introduced in EIP-150 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
||||
# TODO: deactivate it pre-EIP150
|
||||
|
||||
# Note: The all-but-one-64th calculation should occur after the memory expansion fee is taken
|
||||
# https://github.com/ethereum/yellowpaper/pull/442
|
||||
|
@ -291,10 +292,17 @@ template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyp
|
|||
gasParams.c_memLength
|
||||
)
|
||||
|
||||
# Cnew_account - TODO - pre-EIP158 zero-value call consumed 25000 gas
|
||||
# https://github.com/ethereum/eips/issues/158
|
||||
if gasParams.c_isNewAccount and not value.isZero:
|
||||
result.gasCost += static(FeeSchedule[GasNewAccount])
|
||||
# Cnew_account
|
||||
if gasParams.c_isNewAccount:
|
||||
if fork < FkSpurious:
|
||||
# Pre-EIP161 all account creation calls consumed 25000 gas.
|
||||
result.gasCost += static(FeeSchedule[GasNewAccount])
|
||||
else:
|
||||
# Afterwards, only those transfering value:
|
||||
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-158.md
|
||||
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md
|
||||
if not value.isZero:
|
||||
result.gasCost += static(FeeSchedule[GasNewAccount])
|
||||
|
||||
# Cxfer
|
||||
if not value.isZero:
|
||||
|
@ -305,13 +313,16 @@ template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyp
|
|||
let cextra = result.gasCost
|
||||
|
||||
# Cgascap
|
||||
result.gasCost = if gasParams.c_gasBalance >= result.gasCost:
|
||||
min(
|
||||
`prefix all_but_one_64th`(gasParams.c_gasBalance - result.gasCost),
|
||||
gasParams.c_contract_gas
|
||||
)
|
||||
else:
|
||||
gasParams.c_contract_gas
|
||||
if fork >= FkTangerine:
|
||||
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
||||
result.gasCost =
|
||||
if gasParams.c_gasBalance >= result.gasCost:
|
||||
min(
|
||||
`prefix all_but_one_64th`(gasParams.c_gasBalance - result.gasCost),
|
||||
gasParams.c_contract_gas
|
||||
)
|
||||
else:
|
||||
gasParams.c_contract_gas
|
||||
|
||||
# Ccallgas - Gas sent to the child message
|
||||
result.gasRefund = result.gasCost
|
||||
|
@ -574,9 +585,20 @@ const
|
|||
TangerineGasFees = HomesteadGasFees.tangerineGasFees
|
||||
SpuriousGasFees = TangerineGasFees.spuriousGasFees
|
||||
|
||||
gasCosts(BaseGasFees, base, BaseGasCosts)
|
||||
gasCosts(HomesteadGasFees, homestead, HomesteadGasCosts)
|
||||
gasCosts(TangerineGasFees, tangerine, TangerineGasCosts)
|
||||
gasFees*: array[Fork, GasFeeSchedule] = [
|
||||
FkFrontier: BaseGasFees,
|
||||
FkThawing: BaseGasFees,
|
||||
FkHomestead: HomesteadGasFees,
|
||||
FkDao: HomesteadGasFees,
|
||||
FkTangerine: TangerineGasFees,
|
||||
FkSpurious: SpuriousGasFees,
|
||||
FkByzantium: SpuriousGasFees, # not supported yet
|
||||
]
|
||||
|
||||
|
||||
gasCosts(FkFrontier, base, BaseGasCosts)
|
||||
gasCosts(FkHomestead, homestead, HomesteadGasCosts)
|
||||
gasCosts(FkTangerine, tangerine, TangerineGasCosts)
|
||||
|
||||
proc forkToSchedule*(fork: Fork): GasCosts =
|
||||
if fork < FkHomestead:
|
||||
|
|
|
@ -522,7 +522,7 @@ op create, inline = false, value, startPosition, size:
|
|||
computation.vmState.blockHeader.rlphash, false).
|
||||
getBalance(computation.msg.sender)
|
||||
|
||||
if senderBalance >= value:
|
||||
if senderBalance < value:
|
||||
debug "Computation Failure", reason = "Insufficient funds available to transfer", required = computation.msg.value, balance = senderBalance
|
||||
push: 0
|
||||
return
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
# * 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
|
||||
import stint
|
||||
|
||||
type
|
||||
Fork* = enum
|
||||
# FkGenesis
|
||||
FkFrontier,
|
||||
FkThawing,
|
||||
FkHomestead,
|
||||
|
@ -18,17 +17,16 @@ type
|
|||
FkSpurious,
|
||||
FkByzantium
|
||||
|
||||
UInt256Pair = tuple[a: Uint256, b: Uint256]
|
||||
|
||||
let forkBlocks: array[Fork, Uint256] = [
|
||||
FkFrontier: 1.u256, # 30/07/2015 19:26:28
|
||||
FkThawing: 200_000.u256, # 08/09/2015 01:33:09
|
||||
FkHomestead: 1_150_000.u256, # 14/03/2016 20:49:53
|
||||
FkDao: 1_920_000.u256, # 20/07/2016 17:20:40
|
||||
FkTangerine: 2_463_000.u256, # 18/10/2016 17:19:31
|
||||
FkSpurious: 2_675_000.u256, # 22/11/2016 18:15:44
|
||||
FkByzantium: 4_370_000.u256 # 16/10/2017 09:22:11
|
||||
]
|
||||
const
|
||||
forkBlocks*: array[Fork, Uint256] = [
|
||||
FkFrontier: 1.u256, # 30/07/2015 19:26:28
|
||||
FkThawing: 200_000.u256, # 08/09/2015 01:33:09
|
||||
FkHomestead: 1_150_000.u256, # 14/03/2016 20:49:53
|
||||
FkDao: 1_920_000.u256, # 20/07/2016 17:20:40
|
||||
FkTangerine: 2_463_000.u256, # 18/10/2016 17:19:31
|
||||
FkSpurious: 2_675_000.u256, # 22/11/2016 18:15:44
|
||||
FkByzantium: 4_370_000.u256 # 16/10/2017 09:22:11
|
||||
]
|
||||
|
||||
proc toFork*(blockNumber: UInt256): Fork =
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ func invalidInstruction*(computation: var BaseComputation) {.inline.} =
|
|||
raise newException(ValueError, "Invalid instruction, received an opcode not implemented in the current fork.")
|
||||
|
||||
let FrontierOpDispatch {.compileTime.}: array[Op, NimNode] = block:
|
||||
fill_enum_table_holes(Op, newIdentNode"invalidInstruction"):
|
||||
fill_enum_table_holes(Op, newIdentNode("invalidInstruction")):
|
||||
[
|
||||
Stop: newIdentNode "toBeReplacedByBreak",
|
||||
Add: newIdentNode "add",
|
||||
|
@ -235,20 +235,22 @@ proc frontierVM(computation: var BaseComputation) =
|
|||
|
||||
proc updateOpcodeExec*(computation: var BaseComputation, fork: Fork) =
|
||||
case fork
|
||||
of FkFrontier:
|
||||
of FkFrontier..FkSpurious:
|
||||
computation.opCodeExec = frontierVM
|
||||
computation.frontierVM()
|
||||
else:
|
||||
raise newException(VMError, "Unknown or not implemented fork: " & $fork)
|
||||
|
||||
proc updateOpcodeExec*(computation: var BaseComputation) =
|
||||
let fork = computation.vmState.blockHeader.blockNumber.toFork
|
||||
let fork = computation.getFork
|
||||
computation.updateOpcodeExec(fork)
|
||||
|
||||
proc executeOpcodes*(computation: var BaseComputation) =
|
||||
# TODO: Optimise getting fork and updating opCodeExec only when necessary
|
||||
let fork = computation.vmState.blockHeader.blockNumber.toFork
|
||||
let fork = computation.getFork
|
||||
try:
|
||||
computation.updateOpcodeExec(fork)
|
||||
except VMError:
|
||||
computation.error = Error(info: getCurrentExceptionMsg())
|
||||
debug "executeOpcodes() failed", error = getCurrentExceptionMsg()
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
ranges/typedranges, sequtils, strformat, tables,
|
||||
ranges/typedranges, sequtils, strformat, tables, options,
|
||||
eth_common, chronicles,
|
||||
./constants, ./errors, ./vm/computation,
|
||||
./transaction, ./vm_types, ./vm_state, ./block_types, ./db/[db_chain, state_db], ./utils/header,
|
||||
./vm/interpreter, ./utils/addresses
|
||||
./vm/interpreter, ./vm/interpreter/gas_costs, ./utils/addresses
|
||||
|
||||
func intrinsicGas*(data: openarray[byte]): GasInt =
|
||||
result = 21_000
|
||||
|
@ -33,7 +33,7 @@ proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender
|
|||
transaction.accountNonce == readOnlyDB.getNonce(sender) and
|
||||
readOnlyDB.getBalance(sender) >= gas_cost
|
||||
|
||||
proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: Transaction, sender: EthAddress) : BaseComputation =
|
||||
proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: Transaction, sender: EthAddress, forkOverride=none(Fork)) : BaseComputation =
|
||||
let message = newMessage(
|
||||
gas = transaction.gasLimit - transaction.payload.intrinsicGas,
|
||||
gasPrice = transaction.gasPrice,
|
||||
|
@ -45,7 +45,7 @@ proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: T
|
|||
options = newMessageOptions(origin = sender,
|
||||
createAddress = transaction.to))
|
||||
|
||||
result = newBaseComputation(vmState, header.blockNumber, message)
|
||||
result = newBaseComputation(vmState, header.blockNumber, message, forkOverride)
|
||||
doAssert result.isOriginComputation
|
||||
|
||||
proc execComputation*(computation: var BaseComputation): bool =
|
||||
|
@ -59,19 +59,24 @@ proc execComputation*(computation: var BaseComputation): bool =
|
|||
except ValueError:
|
||||
result = false
|
||||
|
||||
proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: BaseVMState, sender: EthAddress, useHomestead: bool = false): UInt256 =
|
||||
proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: BaseVMState, sender: EthAddress, forkOverride=none(Fork)): UInt256 =
|
||||
doAssert t.isContractCreation
|
||||
# TODO: clean up params
|
||||
trace "Contract creation"
|
||||
|
||||
let gasUsed = t.payload.intrinsicGas.GasInt + (if useHomestead: 32000 else: 0)
|
||||
let fork =
|
||||
if forkOverride.isSome:
|
||||
forkOverride.get
|
||||
else:
|
||||
vmState.blockNumber.toFork
|
||||
let gasUsed = t.payload.intrinsicGas.GasInt + gasFees[fork][GasTXCreate]
|
||||
|
||||
# TODO: setupComputation refactoring
|
||||
let contractAddress = generateAddress(sender, t.accountNonce)
|
||||
let msg = newMessage(t.gasLimit - gasUsed, t.gasPrice, t.to, sender, t.value, @[], t.payload,
|
||||
options = newMessageOptions(origin = sender,
|
||||
createAddress = contractAddress))
|
||||
var c = newBaseComputation(vmState, vmState.blockNumber, msg)
|
||||
var c = newBaseComputation(vmState, vmState.blockNumber, msg, forkOverride)
|
||||
|
||||
if execComputation(c):
|
||||
db.addBalance(contractAddress, t.value)
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
tables, eth_common,
|
||||
tables, eth_common, options,
|
||||
./constants, json,
|
||||
./vm/[memory, stack, code_stream],
|
||||
./vm/interpreter/[gas_costs, opcode_values], # TODO - will be hidden at a lower layer
|
||||
./vm/interpreter/[gas_costs, opcode_values, vm_forks], # TODO - will be hidden at a lower layer
|
||||
./db/db_chain
|
||||
|
||||
type
|
||||
|
@ -60,6 +60,7 @@ type
|
|||
gasCosts*: GasCosts # TODO - will be hidden at a lower layer
|
||||
opCodeExec*: OpcodeExecutor
|
||||
lastOpCodeHasRetVal*: bool
|
||||
forkOverride*: Option[Fork]
|
||||
|
||||
Error* = ref object
|
||||
info*: string
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
|
||||
import
|
||||
unittest, strformat, strutils, tables, json, ospaths, times,
|
||||
byteutils, ranges/typedranges, nimcrypto/[keccak, hash],
|
||||
byteutils, ranges/typedranges, nimcrypto/[keccak, hash], options,
|
||||
rlp, eth_trie/db, eth_common,
|
||||
eth_keys,
|
||||
./test_helpers,
|
||||
|
@ -23,14 +23,15 @@ suite "generalstate json tests":
|
|||
jsonTest("GeneralStateTests", testFixture)
|
||||
|
||||
|
||||
proc testFixtureIndexes(header: BlockHeader, pre: JsonNode, transaction: Transaction, sender: EthAddress, expectedHash: string) =
|
||||
proc testFixtureIndexes(header: BlockHeader, pre: JsonNode, transaction: Transaction, sender: EthAddress, expectedHash: string, testStatusIMPL: var TestStatus, fork: Fork) =
|
||||
var vmState = newBaseVMState(header, newBaseChainDB(newMemoryDb()))
|
||||
vmState.mutateStateDB:
|
||||
setupStateDB(pre, db)
|
||||
|
||||
defer:
|
||||
#echo vmState.readOnlyStateDB.dumpAccount("c94f5374fce5edbc8e2a8697c15331677e6ebf0b")
|
||||
doAssert "0x" & `$`(vmState.readOnlyStateDB.rootHash).toLowerAscii == expectedHash
|
||||
let obtainedHash = "0x" & `$`(vmState.readOnlyStateDB.rootHash).toLowerAscii
|
||||
check obtainedHash == expectedHash
|
||||
|
||||
if not validateTransaction(vmState, transaction, sender):
|
||||
vmState.mutateStateDB:
|
||||
|
@ -56,10 +57,10 @@ proc testFixtureIndexes(header: BlockHeader, pre: JsonNode, transaction: Transac
|
|||
# fixtures/GeneralStateTests/stTransactionTest/TransactionSendingToEmpty.json
|
||||
#db.addBalance(generateAddress(sender, transaction.accountNonce), transaction.value)
|
||||
|
||||
let createGasUsed = applyCreateTransaction(db, transaction, vmState, sender, true)
|
||||
let createGasUsed = applyCreateTransaction(db, transaction, vmState, sender, some(fork))
|
||||
db.addBalance(header.coinbase, createGasUsed)
|
||||
return
|
||||
var computation = setupComputation(header, vmState, transaction, sender)
|
||||
var computation = setupComputation(header, vmState, transaction, sender, some(fork))
|
||||
|
||||
vmState.mutateStateDB:
|
||||
# contract creation transaction.to == 0, so ensure happens after
|
||||
|
@ -108,13 +109,16 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
|||
)
|
||||
|
||||
let ftrans = fixture["transaction"]
|
||||
for expectation in fixture["post"]["Homestead"]:
|
||||
let
|
||||
expectedHash = expectation["hash"].getStr
|
||||
indexes = expectation["indexes"]
|
||||
dataIndex = indexes["data"].getInt
|
||||
gasIndex = indexes["gas"].getInt
|
||||
valueIndex = indexes["value"].getInt
|
||||
let transaction = ftrans.getFixtureTransaction(dataIndex, gasIndex, valueIndex)
|
||||
let sender = ftrans.getFixtureTransactionSender
|
||||
testFixtureIndexes(header, fixture["pre"], transaction, sender, expectedHash)
|
||||
for fork in supportedForks:
|
||||
if fixture["post"].has_key(forkNames[fork]):
|
||||
# echo "[fork: ", forkNames[fork], "]"
|
||||
for expectation in fixture["post"][forkNames[fork]]:
|
||||
let
|
||||
expectedHash = expectation["hash"].getStr
|
||||
indexes = expectation["indexes"]
|
||||
dataIndex = indexes["data"].getInt
|
||||
gasIndex = indexes["gas"].getInt
|
||||
valueIndex = indexes["value"].getInt
|
||||
let transaction = ftrans.getFixtureTransaction(dataIndex, gasIndex, valueIndex)
|
||||
let sender = ftrans.getFixtureTransactionSender
|
||||
testFixtureIndexes(header, fixture["pre"], transaction, sender, expectedHash, testStatusIMPL, fork)
|
||||
|
|
|
@ -14,23 +14,36 @@ import
|
|||
../nimbus/vm/interpreter/[gas_costs, vm_forks],
|
||||
../tests/test_generalstate_failing
|
||||
|
||||
const
|
||||
# from https://ethereum-tests.readthedocs.io/en/latest/test_types/state_tests.html
|
||||
forkNames* = {
|
||||
FkFrontier: "Frontier",
|
||||
FkHomestead: "Homestead",
|
||||
FkTangerine: "EIP150",
|
||||
FkSpurious: "EIP158",
|
||||
FkByzantium: "Byzantium",
|
||||
}.toTable
|
||||
|
||||
supportedForks* = [FkHomestead]
|
||||
|
||||
type
|
||||
Status* {.pure.} = enum OK, Fail, Skip
|
||||
|
||||
func slowTest*(folder: string, name: string): bool =
|
||||
# TODO: add vmPerformance and loop check here
|
||||
result = folder == "stQuadraticComplexityTest" or
|
||||
name in @["randomStatetest352.json", "randomStatetest1.json",
|
||||
"randomStatetest32.json", "randomStatetest347.json",
|
||||
"randomStatetest393.json", "randomStatetest626.json",
|
||||
"CALLCODE_Bounds.json", "DELEGATECALL_Bounds3.json",
|
||||
"CALLCODE_Bounds4.json", "CALL_Bounds.json",
|
||||
"DELEGATECALL_Bounds2.json", "CALL_Bounds3.json",
|
||||
"CALLCODE_Bounds2.json", "CALLCODE_Bounds3.json",
|
||||
"DELEGATECALL_Bounds.json", "CALL_Bounds2a.json",
|
||||
"CALL_Bounds2.json",
|
||||
"CallToNameRegistratorMemOOGAndInsufficientBalance.json",
|
||||
"CallToNameRegistratorTooMuchMemory0.json"]
|
||||
result =
|
||||
(folder == "vmPerformance" and "loop" in name) or
|
||||
folder == "stQuadraticComplexityTest" or
|
||||
name in @["randomStatetest352.json", "randomStatetest1.json",
|
||||
"randomStatetest32.json", "randomStatetest347.json",
|
||||
"randomStatetest393.json", "randomStatetest626.json",
|
||||
"CALLCODE_Bounds.json", "DELEGATECALL_Bounds3.json",
|
||||
"CALLCODE_Bounds4.json", "CALL_Bounds.json",
|
||||
"DELEGATECALL_Bounds2.json", "CALL_Bounds3.json",
|
||||
"CALLCODE_Bounds2.json", "CALLCODE_Bounds3.json",
|
||||
"DELEGATECALL_Bounds.json", "CALL_Bounds2a.json",
|
||||
"CALL_Bounds2.json",
|
||||
"CallToNameRegistratorMemOOGAndInsufficientBalance.json",
|
||||
"CallToNameRegistratorTooMuchMemory0.json"]
|
||||
|
||||
func failIn32Bits(folder, name: string): bool =
|
||||
return name in @[
|
||||
|
@ -82,7 +95,7 @@ func failIn32Bits(folder, name: string): bool =
|
|||
"returndatasize_initial_zero_read.json",
|
||||
"call_then_create_successful_then_returndatasize.json",
|
||||
"call_outsize_then_create_successful_then_returndatasize.json",
|
||||
|
||||
|
||||
"returndatacopy_following_create.json",
|
||||
"returndatacopy_following_revert_in_create.json",
|
||||
"returndatacopy_following_successful_create.json",
|
||||
|
@ -99,26 +112,29 @@ func allowedFailInCurrentBuild(folder, name: string): bool =
|
|||
return allowedFailingGeneralStateTest(folder, name)
|
||||
|
||||
func validTest*(folder: string, name: string): bool =
|
||||
# tests we want to skip or which segfault will be skipped here
|
||||
result = (folder != "vmPerformance" or "loop" notin name) and
|
||||
not slowTest(folder, name)
|
||||
# we skip tests that are slow or expected to fail for now
|
||||
result =
|
||||
not slowTest(folder, name) and
|
||||
not allowedFailInCurrentBuild(folder, name)
|
||||
|
||||
proc lacksHomesteadPostStates*(filename: string): bool =
|
||||
proc lacksSupportedForks*(filename: string): bool =
|
||||
# XXX: Until Nimbus supports Byzantine or newer forks, as opposed
|
||||
# to Homestead, ~1k of ~2.5k GeneralStateTests won't work. Nimbus
|
||||
# supporting Byzantine should trigger removal of this function. A
|
||||
# possible alternate approach of avoiding double-reading fixtures
|
||||
# seemed less than ideal, as by the time that happens, output has
|
||||
# already appeared. Compatible with non-GST fixtures. Will become
|
||||
# expensive once BlockchainTests appear, so try to remove first.
|
||||
# to Homestead, ~1k of ~2.5k GeneralStateTests won't work.
|
||||
let fixtures = parseJSON(readFile(filename))
|
||||
var fixture: JsonNode
|
||||
for label, child in fixtures:
|
||||
fixture = child
|
||||
break
|
||||
|
||||
return fixture.kind == JObject and fixture.has_key("transaction") and
|
||||
(fixture.has_key("post") and not fixture["post"].has_key("Homestead"))
|
||||
# not all fixtures make a distinction between forks, so default to accepting
|
||||
# them all, until we find the ones that specify forks in their "post" section
|
||||
result = false
|
||||
if fixture.kind == JObject and fixture.has_key("transaction") and fixture.has_key("post"):
|
||||
result = true
|
||||
for fork in supportedForks:
|
||||
if fixture["post"].has_key(forkNames[fork]):
|
||||
result = false
|
||||
break
|
||||
|
||||
macro jsonTest*(s: static[string], handler: untyped): untyped =
|
||||
let
|
||||
|
@ -128,30 +144,29 @@ macro jsonTest*(s: static[string], handler: untyped): untyped =
|
|||
final = newIdentNode"final"
|
||||
name = newIdentNode"name"
|
||||
formatted = newStrLitNode"{symbol[final]} {name:<64}{$final}{'\n'}"
|
||||
|
||||
result = quote:
|
||||
var filenames: seq[(string, string, string)] = @[]
|
||||
var status = initOrderedTable[string, OrderedTable[string, Status]]()
|
||||
for filename in walkDirRec("tests" / "fixtures" / `s`):
|
||||
if not filename.endsWith(".json"):
|
||||
continue
|
||||
var (folder, name) = filename.splitPath()
|
||||
let last = folder.splitPath().tail
|
||||
if not status.hasKey(last):
|
||||
status[last] = initOrderedTable[string, Status]()
|
||||
status[last][name] = Status.Skip
|
||||
if last.validTest(name) and not filename.lacksHomesteadPostStates:
|
||||
if last.validTest(name) and not filename.lacksSupportedForks:
|
||||
filenames.add((filename, last, name))
|
||||
for child in filenames:
|
||||
let (filename, folder, name) = child
|
||||
# we set this here because exceptions might be raised in the handler:
|
||||
status[folder][name] = Status.Fail
|
||||
test filename:
|
||||
echo folder / name
|
||||
status[folder][name] = Status.FAIL
|
||||
try:
|
||||
`handler`(parseJSON(readFile(filename)), `testStatusIMPL`)
|
||||
if `testStatusIMPL` == OK:
|
||||
status[folder][name] = Status.OK
|
||||
except AssertionError:
|
||||
status[folder][name] = Status.FAIL
|
||||
if not allowedFailInCurrentBuild(folder, name):
|
||||
raise
|
||||
`handler`(parseJSON(readFile(filename)), `testStatusIMPL`)
|
||||
if `testStatusIMPL` == OK:
|
||||
status[folder][name] = Status.OK
|
||||
|
||||
status.sort do (a: (string, OrderedTable[string, Status]),
|
||||
b: (string, OrderedTable[string, Status])) -> int: cmp(a[0], b[0])
|
||||
|
|
Loading…
Reference in New Issue