nimbus-eth1/tests/test_blockchain_json.nim

628 lines
20 KiB
Nim
Raw Normal View History

2019-09-03 15:06:43 +00:00
# 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
unittest2, json, os, tables, strutils, sets,
2019-09-03 15:06:43 +00:00
options,
2020-04-15 11:09:49 +00:00
eth/[common, rlp], eth/trie/[db, trie_defs],
stew/[endians2, byteutils],
2019-12-07 15:37:31 +00:00
./test_helpers, ./test_allowed_to_fail,
../premix/parser, test_config,
Feature/goerli replay clique poa (#743) * extract unused clique/mining support into separate file why: mining is currently unsupported by nimbus * Replay first 51840 transactions from Goerli block chain why: Currently Goerli is loaded but the block headers are not verified. Replaying allows real data PoA development. details: Simple stupid gzipped dump/undump layer for debugging based on the zlib module (no nim-faststream support.) This is a replay running against p2p/chain.persistBlocks() where the data were captured from. * prepare stubs for PoA engine * split executor source into sup-modules why: make room for updates, clique integration should go into executor/update_poastate.nim * Simplify p2p/executor.processBlock() function prototype why: vmState argument always wraps basicChainDB * split processBlock() into sub-functions why: isolate the part where it will support clique/poa * provided additional processTransaction() function prototype without _fork_ argument why: with the exception of some tests, the _fork_ argument is always derived from the other prototype argument _vmState_ details: similar situation with makeReceipt() * provide new processBlock() version explicitly supporting PoA details: The new processBlock() version supporting PoA is the general one also supporting non-PoA networks, it needs an additional _Clique_ descriptor function argument for PoA state (if any.) The old processBlock() function without the _Clique_ descriptor argument retorns an error on PoA networgs (e.g. Goerli.) * re-implemented Clique descriptor as _ref object_ why: gives more flexibility when moving around the descriptor object details: also cleaned up a bit the clique sources * comments for clarifying handling of Clique/PoA state descriptor
2021-07-06 13:14:45 +00:00
../nimbus/[vm_state, utils, vm_types, errors, constants, forks],
../nimbus/db/[db_chain, accounts_cache],
2019-09-07 08:38:44 +00:00
../nimbus/utils/header,
../nimbus/p2p/[executor, validate],
../nimbus/chain_config,
../stateless/[tree_from_witness, witness_types]
2019-09-03 15:06:43 +00:00
type
SealEngine = enum
NoProof
Ethash
TestBlock = object
goodBlock: bool
blockRLP : Blob
hasException: bool
2019-09-03 15:06:43 +00:00
Tester = object
lastBlockHash: Hash256
genesisHeader: BlockHeader
blocks : seq[TestBlock]
sealEngine : Option[SealEngine]
debugMode : bool
trace : bool
vmState : BaseVMState
debugData : JsonNode
network : string
postStateHash: Hash256
2019-09-03 15:06:43 +00:00
var pow = PowRef.new
2019-09-10 12:52:36 +00:00
proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = false, trace = false)
2019-09-03 15:06:43 +00:00
func normalizeNumber(n: JsonNode): JsonNode =
let str = n.getStr
if str == "0x":
result = newJString("0x0")
elif str == "0x0":
result = n
elif str == "0x00":
2019-09-03 15:06:43 +00:00
result = newJString("0x0")
elif str[2] == '0':
var i = 2
while str[i] == '0':
inc i
result = newJString("0x" & str.substr(i))
else:
result = n
func normalizeData(n: JsonNode): JsonNode =
if n.getStr() == "":
result = newJString("0x")
else:
result = n
func normalizeBlockHeader(node: JsonNode): JsonNode =
for k, v in node:
case k
of "bloom": node["logsBloom"] = v
of "coinbase": node["miner"] = v
of "uncleHash": node["sha3Uncles"] = v
of "receiptTrie": node["receiptsRoot"] = v
of "transactionsTrie": node["transactionsRoot"] = v
of "number", "difficulty", "gasUsed",
"gasLimit", "timestamp", "baseFeePerGas":
2019-09-03 15:06:43 +00:00
node[k] = normalizeNumber(v)
of "extraData":
node[k] = normalizeData(v)
else: discard
result = node
proc parseHeader(blockHeader: JsonNode, testStatusIMPL: var TestStatus): BlockHeader =
result = normalizeBlockHeader(blockHeader).parseBlockHeader
var blockHash: Hash256
blockHeader.fromJson "hash", blockHash
check blockHash == hash(result)
proc parseBlocks(blocks: JsonNode): seq[TestBlock] =
2019-09-03 15:06:43 +00:00
for fixture in blocks:
var t: TestBlock
2019-09-03 15:06:43 +00:00
for key, value in fixture:
case key
of "blockHeader":
# header is absent in bad block
t.goodBlock = true
2019-09-03 15:06:43 +00:00
of "rlp":
fixture.fromJson "rlp", t.blockRLP
of "transactions", "uncleHeaders",
"blocknumber", "chainname", "chainnetwork":
discard
of "transactionSequence":
var noError = true
for tx in value:
let valid = tx["valid"].getStr == "true"
noError = noError and valid
doAssert(noError == false, "NOT A VALID TEST CASE")
else:
doAssert("expectException" in key, key)
t.hasException = true
2019-09-03 15:06:43 +00:00
result.add t
func vmConfiguration(network: string, c: var ChainConfig) =
const
H = high(BlockNumber)
Zero = 0.toBlockNumber
Five = 5.toBlockNumber
proc assignNumber(c: var ChainConfig,
fork: Fork, n: BlockNumber) =
var number: array[Fork, BlockNumber]
var z = low(Fork)
while z < fork:
number[z] = Zero
z = z.succ
number[fork] = n
z = high(Fork)
while z > fork:
number[z] = H
z = z.pred
c.daoForkSupport = false
c.homesteadBlock = number[FkHomestead]
c.daoForkBlock = number[FkHomestead]
c.eip150Block = number[FkTangerine]
c.eip155Block = number[FkSpurious]
c.eip158Block = number[FkSpurious]
c.byzantiumBlock = number[FkByzantium]
c.constantinopleBlock = number[FkConstantinople]
c.petersburgBlock = number[FkPetersburg]
c.istanbulBlock = number[FkIstanbul]
c.muirGlacierBlock = number[FkBerlin]
c.berlinBlock = number[FkBerlin]
c.londonBlock = number[FkLondon]
c.arrowGlacierBlock = number[FkLondon]
c.mergeForkBlock = some(number[FkParis])
2020-04-12 12:02:03 +00:00
c.terminalTotalDifficulty = none(UInt256)
2019-09-04 07:32:17 +00:00
case network
2020-04-12 12:02:03 +00:00
of "EIP150":
c.assignNumber(FkTangerine, Zero)
2020-04-12 12:02:03 +00:00
of "ConstantinopleFix":
c.assignNumber(FkPetersburg, Zero)
2020-04-12 12:02:03 +00:00
of "Homestead":
c.assignNumber(FkHomestead, Zero)
2020-04-12 12:02:03 +00:00
of "Frontier":
c.assignNumber(FkFrontier, Zero)
2020-04-12 12:02:03 +00:00
of "Byzantium":
c.assignNumber(FkByzantium, Zero)
2020-04-12 12:02:03 +00:00
of "EIP158ToByzantiumAt5":
c.assignNumber(FkByzantium, Five)
2020-04-12 12:02:03 +00:00
of "EIP158":
c.assignNumber(FkSpurious, Zero)
2020-04-12 12:02:03 +00:00
of "HomesteadToDaoAt5":
c.assignNumber(FkHomestead, Zero)
c.daoForkBlock = Five
2020-04-12 12:02:03 +00:00
c.daoForkSupport = true
of "Constantinople":
c.assignNumber(FkConstantinople, Zero)
2020-04-12 12:02:03 +00:00
of "HomesteadToEIP150At5":
c.assignNumber(FkTangerine, Five)
2020-04-12 12:02:03 +00:00
of "FrontierToHomesteadAt5":
c.assignNumber(FkHomestead, Five)
2020-04-12 12:02:03 +00:00
of "ByzantiumToConstantinopleFixAt5":
c.assignNumber(FkPetersburg, Five)
c.constantinopleBlock = Five
2020-04-12 12:02:03 +00:00
of "Istanbul":
c.assignNumber(FkIstanbul, Zero)
2021-01-11 07:54:11 +00:00
of "Berlin":
c.assignNumber(FkBerlin, Zero)
of "London":
c.assignNumber(FkLondon, Zero)
of "BerlinToLondonAt5":
c.assignNumber(FkLondon, Five)
of "Merge":
c.assignNumber(FkParis, Zero)
c.terminalTotalDifficulty = some(0.u256)
of "ArrowGlacierToMergeAtDiffC0000":
c.assignNumber(FkParis, H)
c.terminalTotalDifficulty = some(0xC0000.u256)
2019-09-04 07:32:17 +00:00
else:
2021-01-11 07:54:11 +00:00
raise newException(ValueError, "unsupported network " & network)
2019-09-04 07:32:17 +00:00
2019-09-03 16:41:01 +00:00
proc parseTester(fixture: JsonNode, testStatusIMPL: var TestStatus): Tester =
result.blocks = parseBlocks(fixture["blocks"])
2019-09-03 16:41:01 +00:00
fixture.fromJson "lastblockhash", result.lastBlockHash
result.genesisHeader = parseHeader(fixture["genesisBlockHeader"], testStatusIMPL)
2019-09-03 16:41:01 +00:00
if "genesisRLP" in fixture:
var genesisRLP: Blob
fixture.fromJson "genesisRLP", genesisRLP
let genesisBlock = EthBlock(header: result.genesisHeader)
2019-09-03 16:41:01 +00:00
check genesisRLP == rlp.encode(genesisBlock)
else:
var goodBlock = true
for h in result.blocks:
goodBlock = goodBlock and h.goodBlock
check goodBlock == false
2019-09-03 16:41:01 +00:00
if "sealEngine" in fixture:
result.sealEngine = some(parseEnum[SealEngine](fixture["sealEngine"].getStr))
if "postStateHash" in fixture:
result.postStateHash.data = hexToByteArray[32](fixture["postStateHash"].getStr)
2020-04-12 12:02:03 +00:00
result.network = fixture["network"].getStr
2019-09-03 16:41:01 +00:00
proc blockWitness(vmState: BaseVMState, chainDB: BaseChainDB) =
let rootHash = vmState.stateDB.rootHash
let witness = vmState.buildWitness()
let fork = vmState.fork
let flags = if fork >= FKSpurious: {wfEIP170} else: {}
# build tree from witness
var db = newMemoryDB()
when defined(useInputStream):
var input = memoryInput(witness)
var tb = initTreeBuilder(input, db, flags)
else:
var tb = initTreeBuilder(witness, db, flags)
let root = tb.buildTree()
# compare the result
if root != rootHash:
raise newException(ValidationError, "Invalid trie generated from block witness")
2019-09-10 12:52:36 +00:00
proc importBlock(tester: var Tester, chainDB: BaseChainDB,
preminedBlock: EthBlock, tb: TestBlock, checkSeal, validation: bool): EthBlock =
2019-09-10 12:52:36 +00:00
2019-09-05 09:07:08 +00:00
let parentHeader = chainDB.getBlockHeader(preminedBlock.header.parentHash)
var baseHeaderForImport = generateHeaderFromParentHeader(chainDB.config,
2020-04-12 10:33:17 +00:00
parentHeader,
2020-04-12 12:02:03 +00:00
preminedBlock.header.coinbase,
2019-09-25 13:05:33 +00:00
some(preminedBlock.header.timestamp),
preminedBlock.header.gasLimit,
@[],
preminedBlock.header.fee
2019-09-25 13:05:33 +00:00
)
2019-09-07 08:38:44 +00:00
2019-09-07 10:32:06 +00:00
deepCopy(result, preminedBlock)
let ttdReached = chainDB.isBlockAfterTtd(preminedBlock.header)
if ttdReached and chainDB.config.mergeForkBlock.isNone:
chainDB.config.mergeForkBlock = some(preminedBlock.header.blockNumber)
if ttdReached:
baseHeaderForImport.prevRandao = preminedBlock.header.prevRandao
Redesign of BaseVMState descriptor (#923) * Redesign of BaseVMState descriptor why: BaseVMState provides an environment for executing transactions. The current descriptor also provides data that cannot generally be known within the execution environment, e.g. the total gasUsed which is available not before after all transactions have finished. Also, the BaseVMState constructor has been replaced by a constructor that does not need pre-initialised input of the account database. also: Previous constructor and some fields are provided with a deprecated annotation (producing a lot of noise.) * Replace legacy directives in production sources * Replace legacy directives in unit test sources * fix CI (missing premix update) * Remove legacy directives * chase CI problem * rebased * Re-introduce 'AccountsCache' constructor optimisation for 'BaseVmState' re-initialisation why: Constructing a new 'AccountsCache' descriptor can be avoided sometimes when the current state root is properly positioned already. Such a feature existed already as the update function 'initStateDB()' for the 'BaseChanDB' where the accounts cache was linked into this desctiptor. The function 'initStateDB()' was removed and re-implemented into the 'BaseVmState' constructor without optimisation. The old version was of restricted use as a wrong accounts cache state would unconditionally throw an exception rather than conceptually ask for a remedy. The optimised 'BaseVmState' re-initialisation has been implemented for the 'persistBlocks()' function. also: moved some test helpers to 'test/replay' folder * Remove unused & undocumented fields from Chain descriptor why: Reduces attack surface in general & improves reading the code.
2022-01-18 16:19:32 +00:00
tester.vmState = BaseVMState.new(
parentHeader,
baseHeaderForImport,
chainDB,
(if tester.trace: {TracerFlags.EnableTracing} else: {}),
chainDB.pruneTrie)
2019-09-09 06:05:16 +00:00
let body = BlockBody(
transactions: result.txs,
uncles: result.uncles
)
if validation:
let rc = chainDB.validateHeaderAndKinship(
result.header, body, checkSeal, ttdReached, pow)
if rc.isErr:
raise newException(
ValidationError, "validateHeaderAndKinship: " & rc.error)
Fearture/poa clique tuning (#765) * Provide API details: API is bundled via clique.nim. * Set extraValidation as default for PoA chains why: This triggers consensus verification and an update of the list of authorised signers. These signers are integral part of the PoA block chain. todo: Option argument to control validation for the nimbus binary. * Fix snapshot state block number why: Using sub-sequence here, so the len() function was wrong. * Optional start where block verification begins why: Can speed up time building loading initial parts of block chain. For PoA, this allows to prove & test that authorised signers can be (correctly) calculated starting at any point on the block chain. todo: On Goerli around blocks #193537..#197568, processing time increases disproportionally -- needs to be understand * For Clique test, get old grouping back (7 transactions per log entry) why: Forgot to change back after troubleshooting * Fix field/function/module-name misunderstanding why: Make compilation work * Use eth_types.blockHash() rather than utils.hash() in Clique modules why: Prefer lib module * Dissolve snapshot_misc.nim details: .. into clique_verify.nim (the other source file clique_unused.nim is inactive) * Hide unused AsyncLock in Clique descriptor details: Unused here but was part of the Go reference implementation * Remove fakeDiff flag from Clique descriptor details: This flag was a kludge in the Go reference implementation used for the canonical tests. The tests have been adapted so there is no need for the fakeDiff flag and its implementation. * Not observing minimum distance from epoch sync point why: For compiling PoA state, the go implementation will walk back to the epoch header with at least 90000 blocks apart from the current header in the absence of other synchronisation points. Here just the nearest epoch header is used. The assumption is that all the checkpoints before have been vetted already regardless of the current branch. details: The behaviour of using the nearest vs the minimum distance epoch is controlled by a flag and can be changed at run time. * Analysing processing time (patch adds some debugging/visualisation support) why: At the first half million blocks of the Goerli replay, blocks on the interval #194854..#196224 take exceptionally long to process, but not due to PoA processing. details: It turns out that much time is spent in p2p/excecutor.processBlock() where the elapsed transaction execution time is significantly greater for many of these blocks. Between the 1371 blocks #194854..#196224 there are 223 blocks with more than 1/2 seconds execution time whereas there are only 4 such blocks before and 13 such after this range up to #504192. * fix debugging symbol in clique_desc (causes CI failing) * Fixing canonical reference tests why: Two errors were introduced earlier but ovelooked: 1. "Remove fakeDiff flag .." patch was incomplete 2. "Not observing minimum distance .." introduced problem w/tests 23/24 details: Fixing 2. needed to revert the behaviour by setting the applySnapsMinBacklog flag for the Clique descriptor. Also a new test was added to lock the new behaviour. * Remove cruft why: Clique/PoA processing was intended to take place somewhere in executor/process_block.processBlock() but was decided later to run from chain/persist_block.persistBlock() instead. * Update API comment * ditto
2021-07-30 14:06:51 +00:00
let res = tester.vmState.processBlockNotPoA(result.header, body)
if res == ValidationResult.Error:
if not (tb.hasException or (not tb.goodBlock)):
raise newException(ValidationError, "process block validation")
else:
if tester.vmState.generateWitness():
blockWitness(tester.vmState, chainDB)
2019-11-28 10:02:11 +00:00
2019-09-05 09:07:08 +00:00
discard chainDB.persistHeaderToDb(preminedBlock.header)
2019-09-04 07:32:17 +00:00
proc applyFixtureBlockToChain(tester: var Tester, tb: TestBlock,
chainDB: BaseChainDB, checkSeal, validation: bool): (EthBlock, EthBlock, Blob) =
2020-04-12 12:02:03 +00:00
# we hack the ChainConfig here and let it works with calcDifficulty
vmConfiguration(tester.network, chainDB.config)
2020-04-12 12:02:03 +00:00
2019-09-04 07:32:17 +00:00
var
preminedBlock = rlp.decode(tb.blockRLP, EthBlock)
minedBlock = tester.importBlock(chainDB, preminedBlock, tb, checkSeal, validation)
2019-09-04 07:32:17 +00:00
rlpEncodedMinedBlock = rlp.encode(minedBlock)
2019-09-04 07:32:17 +00:00
result = (preminedBlock, minedBlock, rlpEncodedMinedBlock)
2019-09-09 05:27:17 +00:00
func shouldCheckSeal(tester: Tester): bool =
2019-09-09 06:05:16 +00:00
if tester.sealEngine.isSome:
result = tester.sealEngine.get() != NoProof
2019-09-09 05:27:17 +00:00
2019-09-24 13:18:48 +00:00
proc collectDebugData(tester: var Tester) =
if tester.vmState.isNil:
return
2019-09-24 13:18:48 +00:00
let vmState = tester.vmState
let tracingResult = if tester.trace: vmState.getTracingResult() else: %[]
tester.debugData.add %{
"blockNumber": %($vmState.blockNumber),
"structLogs": tracingResult,
}
2019-09-10 12:52:36 +00:00
proc runTester(tester: var Tester, chainDB: BaseChainDB, testStatusIMPL: var TestStatus) =
discard chainDB.persistHeaderToDb(tester.genesisHeader)
check chainDB.getCanonicalHead().blockHash == tester.genesisHeader.blockHash
2019-09-09 05:27:17 +00:00
let checkSeal = tester.shouldCheckSeal
2019-09-03 16:41:01 +00:00
2019-09-24 13:18:48 +00:00
if tester.debugMode:
tester.debugData = newJArray()
for idx, testBlock in tester.blocks:
if testBlock.goodBlock:
#try:
2020-04-15 11:09:49 +00:00
let (preminedBlock, _, _) = tester.applyFixtureBlockToChain(
testBlock, chainDB, checkSeal, validation = false)
let ttdReached = chainDB.isBlockAfterTtd(preminedBlock.header)
if ttdReached and chainDB.config.mergeForkBlock.isNone:
chainDB.config.mergeForkBlock = some(preminedBlock.header.blockNumber)
# manually validating
let res = chainDB.validateHeaderAndKinship(
preminedBlock, checkSeal, ttdReached, pow)
check res.isOk
when defined(noisy):
if res.isErr:
debugEcho "blockNumber: ", preminedBlock.header.blockNumber
debugEcho "fork: ", chainDB.config.toFork(preminedBlock.header.blockNumber)
debugEcho "error message: ", res.error
debugEcho "ttdReached: ", ttdReached
#except:
#debugEcho "FATAL ERROR(WE HAVE BUG): ", getCurrentExceptionMsg()
2019-09-25 11:43:16 +00:00
2019-09-07 10:50:34 +00:00
else:
var noError = true
try:
let (_, _, _) = tester.applyFixtureBlockToChain(testBlock,
chainDB, checkSeal, validation = true)
except ValueError, ValidationError, BlockNotFound, RlpError:
2019-09-07 10:50:34 +00:00
# failure is expected on this bad block
check (testBlock.hasException or (not testBlock.goodBlock))
2019-09-07 10:50:34 +00:00
noError = false
if tester.debugMode:
tester.debugData.add %{
"exception": %($getCurrentException().name),
"msg": %getCurrentExceptionMsg()
}
2019-09-07 10:50:34 +00:00
# Block should have caused a validation error
check noError == false
2019-09-03 15:06:43 +00:00
2019-09-24 13:18:48 +00:00
if tester.debugMode:
tester.collectDebugData()
proc dumpAccount(stateDB: ReadOnlyStateDB, address: EthAddress, name: string): JsonNode =
2019-09-10 12:52:36 +00:00
result = %{
"name": %name,
"address": %($address),
"nonce": %toHex(stateDB.getNonce(address)),
"balance": %stateDB.getBalance(address).toHex(),
"codehash": %($stateDB.getCodeHash(address)),
"storageRoot": %($stateDB.getStorageRoot(address))
2019-09-10 12:52:36 +00:00
}
proc dumpDebugData(tester: Tester, vmState: BaseVMState, accountList: JsonNode): JsonNode =
2019-09-10 12:52:36 +00:00
var accounts = newJObject()
var i = 0
for ac, _ in accountList:
let account = ethAddressFromHex(ac)
accounts[$account] = dumpAccount(vmState.readOnlyStateDB, account, "acc" & $i)
inc i
%{
2019-09-24 13:18:48 +00:00
"debugData": tester.debugData,
2019-09-10 12:52:36 +00:00
"accounts": accounts
}
proc accountList(fixture: JsonNode): JsonNode =
if fixture["postState"].kind == JObject:
fixture["postState"]
else:
fixture["pre"]
proc debugDataFromAccountList(tester: Tester, fixture: JsonNode): JsonNode =
let accountList = fixture.accountList
let vmState = tester.vmState
if vmState.isNil:
%{"debugData": tester.debugData}
else:
dumpDebugData(tester, vmState, accountList)
proc debugDataFromPostStateHash(tester: Tester): JsonNode =
var
accounts = newJObject()
accountList = newSeq[EthAddress]()
vmState = tester.vmState
for address in vmState.stateDB.addresses:
accountList.add address
for i, ac in accountList:
accounts[ac.toHex] = dumpAccount(vmState.readOnlyStateDB, ac, "acc" & $i)
%{
"debugData": tester.debugData,
"postStateHash": %($vmState.readOnlyStateDB.rootHash),
"expectedStateHash": %($tester.postStateHash),
"accounts": accounts
}
proc dumpDebugData(tester: Tester, fixture: JsonNode, fixtureName: string, fixtureIndex: int, success: bool) =
let debugData = if tester.postStateHash != Hash256():
debugDataFromPostStateHash(tester)
else:
debugDataFromAccountList(tester, fixture)
2019-09-10 12:52:36 +00:00
let status = if success: "_success" else: "_failed"
writeFile("debug_" & fixtureName & "_" & $fixtureIndex & status & ".json", debugData.pretty())
2022-07-28 19:34:38 +00:00
# using only one networkParams will reduce execution
# time ~87.5% instead of create it for every test
let chainParams = networkParams(MainNet)
2019-09-10 12:52:36 +00:00
proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = false, trace = false) =
2019-09-05 09:07:08 +00:00
# 1 - mine the genesis block
# 2 - loop over blocks:
# - apply transactions
# - mine block
# 3 - diff resulting state with expected state
# 4 - check that all previous blocks were valid
2020-04-12 12:02:03 +00:00
let specifyIndex = test_config.getConfiguration().index
2019-09-10 12:52:36 +00:00
var fixtureIndex = 0
var fixtureTested = false
2019-09-05 09:07:08 +00:00
2019-09-03 15:06:43 +00:00
for fixtureName, fixture in node:
2019-09-10 12:52:36 +00:00
inc fixtureIndex
if specifyIndex > 0 and fixtureIndex != specifyIndex:
continue
2019-09-04 07:32:17 +00:00
var tester = parseTester(fixture, testStatusIMPL)
2019-09-03 15:06:43 +00:00
Redesign of BaseVMState descriptor (#923) * Redesign of BaseVMState descriptor why: BaseVMState provides an environment for executing transactions. The current descriptor also provides data that cannot generally be known within the execution environment, e.g. the total gasUsed which is available not before after all transactions have finished. Also, the BaseVMState constructor has been replaced by a constructor that does not need pre-initialised input of the account database. also: Previous constructor and some fields are provided with a deprecated annotation (producing a lot of noise.) * Replace legacy directives in production sources * Replace legacy directives in unit test sources * fix CI (missing premix update) * Remove legacy directives * chase CI problem * rebased * Re-introduce 'AccountsCache' constructor optimisation for 'BaseVmState' re-initialisation why: Constructing a new 'AccountsCache' descriptor can be avoided sometimes when the current state root is properly positioned already. Such a feature existed already as the update function 'initStateDB()' for the 'BaseChanDB' where the accounts cache was linked into this desctiptor. The function 'initStateDB()' was removed and re-implemented into the 'BaseVmState' constructor without optimisation. The old version was of restricted use as a wrong accounts cache state would unconditionally throw an exception rather than conceptually ask for a remedy. The optimised 'BaseVmState' re-initialisation has been implemented for the 'persistBlocks()' function. also: moved some test helpers to 'test/replay' folder * Remove unused & undocumented fields from Chain descriptor why: Reduces attack surface in general & improves reading the code.
2022-01-18 16:19:32 +00:00
let
pruneTrie = test_config.getConfiguration().pruning
2022-07-28 19:34:38 +00:00
chainDB = newBaseChainDB(newMemoryDb(), pruneTrie, params = chainParams)
Redesign of BaseVMState descriptor (#923) * Redesign of BaseVMState descriptor why: BaseVMState provides an environment for executing transactions. The current descriptor also provides data that cannot generally be known within the execution environment, e.g. the total gasUsed which is available not before after all transactions have finished. Also, the BaseVMState constructor has been replaced by a constructor that does not need pre-initialised input of the account database. also: Previous constructor and some fields are provided with a deprecated annotation (producing a lot of noise.) * Replace legacy directives in production sources * Replace legacy directives in unit test sources * fix CI (missing premix update) * Remove legacy directives * chase CI problem * rebased * Re-introduce 'AccountsCache' constructor optimisation for 'BaseVmState' re-initialisation why: Constructing a new 'AccountsCache' descriptor can be avoided sometimes when the current state root is properly positioned already. Such a feature existed already as the update function 'initStateDB()' for the 'BaseChanDB' where the accounts cache was linked into this desctiptor. The function 'initStateDB()' was removed and re-implemented into the 'BaseVmState' constructor without optimisation. The old version was of restricted use as a wrong accounts cache state would unconditionally throw an exception rather than conceptually ask for a remedy. The optimised 'BaseVmState' re-initialisation has been implemented for the 'persistBlocks()' function. also: moved some test helpers to 'test/replay' folder * Remove unused & undocumented fields from Chain descriptor why: Reduces attack surface in general & improves reading the code.
2022-01-18 16:19:32 +00:00
stateDB = AccountsCache.init(chainDB.db, emptyRlpHash, chainDB.pruneTrie)
setupStateDB(fixture["pre"], stateDB)
stateDB.persist()
check stateDB.rootHash == tester.genesisHeader.stateRoot
2019-09-03 15:06:43 +00:00
2019-09-07 09:47:06 +00:00
tester.debugMode = debugMode
2019-09-10 12:52:36 +00:00
tester.trace = trace
var success = true
try:
tester.runTester(chainDB, testStatusIMPL)
let latestBlockHash = chainDB.getCanonicalHead().blockHash
if latestBlockHash != tester.lastBlockHash:
if tester.postStateHash != Hash256():
let rootHash = tester.vmState.stateDB.rootHash
if tester.postStateHash != rootHash:
raise newException(ValidationError, "incorrect postStateHash, expect=" &
$rootHash & ", get=" &
$tester.postStateHash
)
else:
verifyStateDB(fixture["postState"], tester.vmState.readOnlyStateDB)
2019-09-10 12:52:36 +00:00
except ValidationError as E:
2020-07-21 06:15:06 +00:00
echo fixtureName, " ERROR: ", E.msg
2019-09-10 12:52:36 +00:00
success = false
2019-09-25 11:43:16 +00:00
if tester.debugMode:
tester.dumpDebugData(fixture, fixtureName, fixtureIndex, success)
2019-09-10 12:52:36 +00:00
fixtureTested = true
check success == true
if not fixtureTested:
2020-04-12 12:02:03 +00:00
echo test_config.getConfiguration().testSubject, " not tested at all, wrong index?"
if specifyIndex <= 0 or specifyIndex > node.len:
echo "Maximum subtest available: ", node.len
2019-09-03 15:06:43 +00:00
2019-12-06 05:26:56 +00:00
proc blockchainJsonMain*(debugMode = false) =
const
legacyFolder = "eth_tests" / "LegacyTests" / "Constantinople" / "BlockchainTests"
newFolder = "eth_tests" / "BlockchainTests"
2021-01-14 14:33:18 +00:00
let config = test_config.getConfiguration()
if config.testSubject == "" or not debugMode:
2019-09-07 09:47:06 +00:00
# run all test fixtures
2021-01-14 14:33:18 +00:00
if config.legacy:
suite "block chain json tests":
jsonTest(legacyFolder, "BlockchainTests", testFixture, skipBCTests)
else:
suite "new block chain json tests":
jsonTest(newFolder, "newBlockchainTests", testFixture, skipNewBCTests)
2019-09-07 09:47:06 +00:00
else:
# execute single test in debug mode
if config.testSubject.len == 0:
echo "missing test subject"
quit(QuitFailure)
let folder = if config.legacy: legacyFolder else: newFolder
let path = "tests" / "fixtures" / folder
2019-09-07 09:47:06 +00:00
let n = json.parseFile(path / config.testSubject)
var testStatusIMPL: TestStatus
testFixture(n, testStatusIMPL, debugMode = true, config.trace)
2019-09-07 09:47:06 +00:00
when isMainModule:
var message: string
## Processing command line arguments
2020-04-12 12:02:03 +00:00
if test_config.processArguments(message) != test_config.Success:
2019-09-07 09:47:06 +00:00
echo message
quit(QuitFailure)
else:
if len(message) > 0:
echo message
quit(QuitSuccess)
2019-12-06 05:26:56 +00:00
blockchainJsonMain(true)
2019-09-07 09:47:06 +00:00
2019-09-03 15:06:43 +00:00
# lastBlockHash -> every fixture has it, hash of a block header
# genesisRLP -> NOT every fixture has it, rlp bytes of genesis block header
# _info -> every fixture has it, can be omitted
# pre, postState -> every fixture has it, prestate and post state
# genesisHeader -> every fixture has it
2019-09-03 15:06:43 +00:00
# network -> every fixture has it
# # EIP150 247
# # ConstantinopleFix 286
# # Homestead 256
# # Frontier 396
# # Byzantium 263
# # EIP158ToByzantiumAt5 1
# # EIP158 233
# # HomesteadToDaoAt5 4
# # Constantinople 285
# # HomesteadToEIP150At5 1
# # FrontierToHomesteadAt5 7
# # ByzantiumToConstantinopleFixAt5 1
# sealEngine -> NOT every fixture has it
# # NoProof 1709
# # Ethash 112
# blocks -> every fixture has it, an array of blocks ranging from 1 block to 303 blocks
# # transactions 6230 can be empty
# # # to 6089 -> "" if contractCreation
# # # value 6089
# # # gasLimit 6089 -> "gas"
# # # s 6089
# # # r 6089
# # # gasPrice 6089
# # # v 6089
# # # data 6089 -> "input"
# # # nonce 6089
# # blockHeader 6230 can be not present, e.g. bad rlp
# # uncleHeaders 6230 can be empty
# # rlp 6810 has rlp but no blockheader, usually has exception
# # blocknumber 2733
# # chainname 1821 -> 'A' to 'H', and 'AA' to 'DD'
# # chainnetwork 21 -> all values are "Frontier"
# # expectExceptionALL 420
# # # UncleInChain 55
# # # InvalidTimestamp 42
# # # InvalidGasLimit 42
# # # InvalidNumber 42
# # # InvalidDifficulty 35
# # # InvalidBlockNonce 28
# # # InvalidUncleParentHash 26
# # # ExtraDataTooBig 21
# # # InvalidStateRoot 21
# # # ExtraDataIncorrect 19
# # # UnknownParent 16
# # # TooMuchGasUsed 14
# # # InvalidReceiptsStateRoot 9
# # # InvalidUnclesHash 7
# # # UncleIsBrother 7
# # # UncleTooOld 7
# # # InvalidTransactionsRoot 7
# # # InvalidGasUsed 7
# # # InvalidLogBloom 7
# # # TooManyUncles 7
# # # OutOfGasIntrinsic 1
# # expectExceptionEIP150 17
# # # TooMuchGasUsed 7
# # # InvalidReceiptsStateRoot 7
# # # InvalidStateRoot 3
# # expectExceptionByzantium 17
# # # InvalidStateRoot 10
# # # TooMuchGasUsed 7
# # expectExceptionHomestead 17
# # # InvalidReceiptsStateRoot 7
# # # BlockGasLimitReached 7
# # # InvalidStateRoot 3
# # expectExceptionConstantinople 14
# # # InvalidStateRoot 7
# # # TooMuchGasUsed 7
# # expectExceptionEIP158 14
# # # TooMuchGasUsed 7
# # # InvalidReceiptsStateRoot 7
# # expectExceptionFrontier 14
# # # InvalidReceiptsStateRoot 7
# # # BlockGasLimitReached 7
# # expectExceptionConstantinopleFix 14
# # # InvalidStateRoot 7
# # # TooMuchGasUsed 7