2019-09-03 15:06:43 +00:00
|
|
|
# Nimbus
|
2024-01-09 15:15:19 +00:00
|
|
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
2019-09-03 15:06:43 +00:00
|
|
|
# Licensed under either of
|
2022-11-25 05:26:29 +00:00
|
|
|
# * 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.
|
2019-09-03 15:06:43 +00:00
|
|
|
|
|
|
|
import
|
2023-09-24 23:53:20 +00:00
|
|
|
std/[json, os, tables, strutils, options, streams],
|
2022-12-02 04:39:12 +00:00
|
|
|
unittest2,
|
2023-03-17 18:16:24 +00:00
|
|
|
eth/rlp, eth/trie/trie_defs, eth/common/eth_types_rlp,
|
2023-01-31 12:38:08 +00:00
|
|
|
stew/byteutils,
|
2019-12-07 15:37:31 +00:00
|
|
|
./test_helpers, ./test_allowed_to_fail,
|
2019-11-20 15:25:09 +00:00
|
|
|
../premix/parser, test_config,
|
2022-12-02 04:39:12 +00:00
|
|
|
../nimbus/[vm_state, vm_types, errors, constants],
|
2024-01-22 09:11:37 +00:00
|
|
|
../nimbus/db/[ledger, state_db],
|
2023-03-17 13:20:52 +00:00
|
|
|
../nimbus/utils/[utils, debug],
|
2023-08-02 10:17:40 +00:00
|
|
|
../nimbus/evm/tracer/legacy_tracer,
|
2023-09-24 23:53:20 +00:00
|
|
|
../nimbus/evm/tracer/json_tracer,
|
2023-10-05 03:04:12 +00:00
|
|
|
../nimbus/core/[validate, chain, pow/header],
|
2023-01-10 17:02:21 +00:00
|
|
|
../tools/common/helpers as chp,
|
|
|
|
../tools/evmstate/helpers,
|
2023-09-24 23:53:20 +00:00
|
|
|
../nimbus/common/common,
|
2024-01-22 09:11:37 +00:00
|
|
|
../nimbus/core/eip4844,
|
|
|
|
../nimbus/rpc/experimental
|
2019-09-03 15:06:43 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
SealEngine = enum
|
|
|
|
NoProof
|
|
|
|
Ethash
|
|
|
|
|
2021-05-14 03:47:58 +00:00
|
|
|
TestBlock = object
|
|
|
|
goodBlock: bool
|
2021-05-14 05:19:49 +00:00
|
|
|
blockRLP : Blob
|
2022-12-09 08:17:33 +00:00
|
|
|
header : BlockHeader
|
|
|
|
body : BlockBody
|
2021-05-14 08:27:25 +00:00
|
|
|
hasException: bool
|
2023-03-09 23:40:55 +00:00
|
|
|
withdrawals: Option[seq[Withdrawal]]
|
2019-09-03 15:06:43 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
TestCtx = object
|
2019-09-03 15:06:43 +00:00
|
|
|
lastBlockHash: Hash256
|
2021-05-14 03:47:58 +00:00
|
|
|
genesisHeader: BlockHeader
|
2021-05-14 05:19:49 +00:00
|
|
|
blocks : seq[TestBlock]
|
|
|
|
sealEngine : Option[SealEngine]
|
|
|
|
debugMode : bool
|
|
|
|
trace : bool
|
|
|
|
vmState : BaseVMState
|
|
|
|
debugData : JsonNode
|
|
|
|
network : string
|
2022-09-26 16:14:12 +00:00
|
|
|
postStateHash: Hash256
|
2023-09-24 23:53:20 +00:00
|
|
|
json : bool
|
|
|
|
|
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
|
2019-11-13 11:39:35 +00:00
|
|
|
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':
|
2023-03-16 20:34:47 +00:00
|
|
|
var i = 2
|
2019-09-03 15:06:43 +00:00
|
|
|
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",
|
2021-06-30 13:41:29 +00:00
|
|
|
"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
|
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
func normalizeWithdrawal(node: JsonNode): JsonNode =
|
|
|
|
for k, v in node:
|
|
|
|
case k
|
2023-07-05 09:30:01 +00:00
|
|
|
of "amount", "index", "validatorIndex":
|
2023-03-09 23:40:55 +00:00
|
|
|
node[k] = normalizeNumber(v)
|
|
|
|
else: discard
|
|
|
|
result = node
|
|
|
|
|
2019-09-03 15:06:43 +00:00
|
|
|
proc parseHeader(blockHeader: JsonNode, testStatusIMPL: var TestStatus): BlockHeader =
|
|
|
|
result = normalizeBlockHeader(blockHeader).parseBlockHeader
|
|
|
|
var blockHash: Hash256
|
|
|
|
blockHeader.fromJson "hash", blockHash
|
|
|
|
check blockHash == hash(result)
|
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
proc parseWithdrawals(withdrawals: JsonNode): Option[seq[Withdrawal]] =
|
|
|
|
case withdrawals.kind
|
|
|
|
of JArray:
|
|
|
|
var ws: seq[Withdrawal]
|
|
|
|
for v in withdrawals:
|
|
|
|
ws.add(parseWithdrawal(normalizeWithdrawal(v)))
|
|
|
|
some(ws)
|
|
|
|
else:
|
|
|
|
none[seq[Withdrawal]]()
|
|
|
|
|
2021-05-14 03:47:58 +00:00
|
|
|
proc parseBlocks(blocks: JsonNode): seq[TestBlock] =
|
2019-09-03 15:06:43 +00:00
|
|
|
for fixture in blocks:
|
2021-05-14 03:47:58 +00:00
|
|
|
var t: TestBlock
|
2023-03-17 18:16:24 +00:00
|
|
|
t.withdrawals = none[seq[Withdrawal]]()
|
2019-09-03 15:06:43 +00:00
|
|
|
for key, value in fixture:
|
|
|
|
case key
|
|
|
|
of "blockHeader":
|
2021-05-14 03:47:58 +00:00
|
|
|
# header is absent in bad block
|
|
|
|
t.goodBlock = true
|
2019-09-03 15:06:43 +00:00
|
|
|
of "rlp":
|
2021-05-14 03:47:58 +00:00
|
|
|
fixture.fromJson "rlp", t.blockRLP
|
2023-07-05 09:30:01 +00:00
|
|
|
of "transactions", "uncleHeaders", "hasBigInt",
|
2021-05-14 08:27:25 +00:00
|
|
|
"blocknumber", "chainname", "chainnetwork":
|
2021-05-14 03:47:58 +00:00
|
|
|
discard
|
2021-06-30 13:41:29 +00:00
|
|
|
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")
|
2023-03-09 23:40:55 +00:00
|
|
|
of "withdrawals":
|
|
|
|
t.withdrawals = parseWithdrawals(value)
|
2023-10-18 23:55:50 +00:00
|
|
|
of "rlp_decoded":
|
|
|
|
# this field is intended for client who
|
|
|
|
# doesn't support rlp encoding(e.g. evmone)
|
|
|
|
discard
|
2021-05-14 08:27:25 +00:00
|
|
|
else:
|
2021-06-30 13:41:29 +00:00
|
|
|
doAssert("expectException" in key, key)
|
2021-05-14 08:27:25 +00:00
|
|
|
t.hasException = true
|
2019-09-03 15:06:43 +00:00
|
|
|
|
|
|
|
result.add t
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc parseTestCtx(fixture: JsonNode, testStatusIMPL: var TestStatus): TestCtx =
|
2021-05-14 03:47:58 +00:00
|
|
|
result.blocks = parseBlocks(fixture["blocks"])
|
|
|
|
|
2019-09-03 16:41:01 +00:00
|
|
|
fixture.fromJson "lastblockhash", result.lastBlockHash
|
|
|
|
|
|
|
|
if "genesisRLP" in fixture:
|
|
|
|
var genesisRLP: Blob
|
|
|
|
fixture.fromJson "genesisRLP", genesisRLP
|
2022-12-09 08:17:33 +00:00
|
|
|
result.genesisHeader = rlp.decode(genesisRLP, EthBlock).header
|
2021-05-14 03:47:58 +00:00
|
|
|
else:
|
2022-12-09 08:17:33 +00:00
|
|
|
result.genesisHeader = parseHeader(fixture["genesisBlockHeader"], testStatusIMPL)
|
2021-05-14 03:47:58 +00:00
|
|
|
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))
|
2022-09-26 16:14:12 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2024-06-08 08:05:00 +00:00
|
|
|
proc testGetMultiKeys(chain: ChainRef, parentHeader, currentHeader: BlockHeader) =
|
2024-01-22 09:11:37 +00:00
|
|
|
# check that current state matches current header
|
2024-02-21 09:14:20 +00:00
|
|
|
let currentStateRoot = chain.vmState.stateDB.rootHash
|
2024-01-22 09:11:37 +00:00
|
|
|
if currentStateRoot != currentHeader.stateRoot:
|
|
|
|
raise newException(ValidationError, "Expected currentStateRoot == currentHeader.stateRoot")
|
|
|
|
|
2024-06-08 08:05:00 +00:00
|
|
|
let mkeys = getMultiKeys(chain.com, currentHeader, false)
|
2024-01-22 09:11:37 +00:00
|
|
|
|
2024-06-08 08:05:00 +00:00
|
|
|
# check that the vmstate hasn't changed after call to getMultiKeys
|
2024-02-21 09:14:20 +00:00
|
|
|
if chain.vmState.stateDB.rootHash != currentHeader.stateRoot:
|
2024-01-22 09:11:37 +00:00
|
|
|
raise newException(ValidationError, "Expected chain.vmstate.stateDB.rootHash == currentHeader.stateRoot")
|
|
|
|
|
2024-02-21 16:04:59 +00:00
|
|
|
# use the MultiKeysRef to build the block proofs
|
2024-01-22 09:11:37 +00:00
|
|
|
let
|
2024-05-20 10:17:51 +00:00
|
|
|
ac = newAccountStateDB(chain.com.db, currentHeader.stateRoot)
|
2024-02-09 04:09:02 +00:00
|
|
|
blockProofs = getBlockProofs(state_db.ReadOnlyStateDB(ac), mkeys)
|
2024-06-08 08:05:00 +00:00
|
|
|
if blockProofs.len() != 0:
|
2024-01-22 09:11:37 +00:00
|
|
|
raise newException(ValidationError, "Expected blockProofs.len() == 0")
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc setupTracer(ctx: TestCtx): TracerRef =
|
|
|
|
if ctx.trace:
|
|
|
|
if ctx.json:
|
|
|
|
var tracerFlags = {
|
|
|
|
TracerFlags.DisableMemory,
|
|
|
|
TracerFlags.DisableStorage,
|
|
|
|
TracerFlags.DisableState,
|
|
|
|
TracerFlags.DisableStateDiff,
|
|
|
|
TracerFlags.DisableReturnData
|
|
|
|
}
|
|
|
|
let stream = newFileStream(stdout)
|
|
|
|
newJsonTracer(stream, tracerFlags, false)
|
|
|
|
else:
|
|
|
|
newLegacyTracer({})
|
|
|
|
else:
|
|
|
|
TracerRef()
|
|
|
|
|
|
|
|
proc importBlock(ctx: var TestCtx, com: CommonRef,
|
2023-10-05 03:04:12 +00:00
|
|
|
tb: TestBlock, checkSeal: bool) =
|
2023-09-24 23:53:20 +00:00
|
|
|
if ctx.vmState.isNil or ctx.vmState.stateDB.isTopLevelClean.not:
|
2023-10-05 03:04:12 +00:00
|
|
|
let
|
|
|
|
parentHeader = com.db.getBlockHeader(tb.header.parentHash)
|
|
|
|
tracerInst = ctx.setupTracer()
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.vmState = BaseVMState.new(
|
|
|
|
parentHeader,
|
|
|
|
tb.header,
|
|
|
|
com,
|
|
|
|
tracerInst,
|
|
|
|
)
|
2024-06-08 08:05:00 +00:00
|
|
|
ctx.vmState.collectWitnessData = true # Enable saving witness data
|
2019-09-09 06:05:16 +00:00
|
|
|
|
2023-10-05 03:04:12 +00:00
|
|
|
let
|
|
|
|
chain = newChain(com, extraValidation = true, ctx.vmState)
|
|
|
|
res = chain.persistBlocks([tb.header], [tb.body])
|
2022-09-26 16:14:12 +00:00
|
|
|
|
2024-06-07 02:01:45 +00:00
|
|
|
if res.isErr():
|
2024-05-31 07:13:56 +00:00
|
|
|
raise newException(ValidationError, res.error())
|
2021-05-14 08:27:25 +00:00
|
|
|
else:
|
2024-06-08 08:05:00 +00:00
|
|
|
testGetMultiKeys(chain, chain.vmState.parent, tb.header)
|
2022-09-26 16:14:12 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc applyFixtureBlockToChain(ctx: var TestCtx, tb: var TestBlock,
|
2023-10-05 03:04:12 +00:00
|
|
|
com: CommonRef, checkSeal: bool) =
|
2023-06-12 04:29:03 +00:00
|
|
|
decompose(tb.blockRLP, tb.header, tb.body)
|
2023-10-05 03:04:12 +00:00
|
|
|
ctx.importBlock(com, tb, checkSeal)
|
2019-09-04 07:32:17 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
func shouldCheckSeal(ctx: TestCtx): bool =
|
|
|
|
if ctx.sealEngine.isSome:
|
|
|
|
result = ctx.sealEngine.get() != NoProof
|
2019-09-09 05:27:17 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc collectDebugData(ctx: var TestCtx) =
|
|
|
|
if ctx.vmState.isNil:
|
2021-05-16 12:46:17 +00:00
|
|
|
return
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
let vmState = ctx.vmState
|
2023-08-02 10:17:40 +00:00
|
|
|
let tracerInst = LegacyTracer(vmState.tracer)
|
2023-09-24 23:53:20 +00:00
|
|
|
let tracingResult = if ctx.trace: tracerInst.getTracingResult() else: %[]
|
|
|
|
ctx.debugData.add %{
|
2019-09-24 13:18:48 +00:00
|
|
|
"blockNumber": %($vmState.blockNumber),
|
|
|
|
"structLogs": tracingResult,
|
|
|
|
}
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc runTestCtx(ctx: var TestCtx, com: CommonRef, testStatusIMPL: var TestStatus) =
|
Consolidate block type for block processing (#2325)
This PR consolidates the split header-body sequences into a single EthBlock
sequence and cleans up the fallout from that which significantly reduces
block processing overhead during import thanks to less garbage collection
and fewer copies of things all around.
Notably, since the number of headers must always match the number of bodies,
we also get rid of a pointless degree of freedom that in the future could
introduce unnecessary bugs.
* only read header and body from era file
* avoid several unnecessary copies along the block processing way
* simplify signatures, cleaning up unused arguemnts and returns
* use `stew/assign2` in a few strategic places where the generated
nim assignent is slow and add a few `move` to work around poor
analysis in nim 1.6 (will need to be revisited for 2.0)
```
stats-20240607_2223-a814aa0b.csv vs stats-20240608_0714-21c1d0a9.csv
bps_x bps_y tps_x tps_y bpsd tpsd timed
block_number
(498305, 713245] 1,540.52 1,809.73 2,361.58 2775.340189 17.63% 17.63% -14.92%
(713245, 928185] 730.36 865.26 1,715.90 2028.973852 18.01% 18.01% -15.21%
(928185, 1143126] 663.03 789.10 2,529.26 3032.490771 19.79% 19.79% -16.28%
(1143126, 1358066] 393.46 508.05 2,152.50 2777.578119 29.13% 29.13% -22.50%
(1358066, 1573007] 370.88 440.72 2,351.31 2791.896052 18.81% 18.81% -15.80%
(1573007, 1787947] 283.65 335.11 2,068.93 2441.373402 17.60% 17.60% -14.91%
(1787947, 2002888] 287.29 342.11 2,078.39 2474.179448 18.99% 18.99% -15.91%
(2002888, 2217828] 293.38 343.16 2,208.83 2584.77457 17.16% 17.16% -14.61%
(2217828, 2432769] 140.09 167.86 1,081.87 1296.336926 18.82% 18.82% -15.80%
blocks: 1934464, baseline: 3h13m1s, contender: 2h43m47s
bpsd (mean): 19.55%
tpsd (mean): 19.55%
Time (total): -29m13s, -15.14%
```
2024-06-09 14:32:20 +00:00
|
|
|
com.db.persistHeaderToDb(ctx.genesisHeader,
|
2022-12-10 01:32:55 +00:00
|
|
|
com.consensus == ConsensusType.POS)
|
2023-09-24 23:53:20 +00:00
|
|
|
check com.db.getCanonicalHead().blockHash == ctx.genesisHeader.blockHash
|
|
|
|
let checkSeal = ctx.shouldCheckSeal
|
2019-09-03 16:41:01 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
if ctx.debugMode:
|
|
|
|
ctx.debugData = newJArray()
|
2019-09-24 13:18:48 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
for idx, tb in ctx.blocks:
|
2022-12-09 08:17:33 +00:00
|
|
|
if tb.goodBlock:
|
2022-10-17 10:22:01 +00:00
|
|
|
try:
|
2023-10-05 03:04:12 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.applyFixtureBlockToChain(
|
2023-10-05 03:04:12 +00:00
|
|
|
ctx.blocks[idx], com, checkSeal)
|
2021-06-24 15:29:21 +00:00
|
|
|
|
2023-06-12 04:29:03 +00:00
|
|
|
except CatchableError as ex:
|
|
|
|
debugEcho "FATAL ERROR(WE HAVE BUG): ", ex.msg
|
2019-09-25 11:43:16 +00:00
|
|
|
|
2019-09-07 10:50:34 +00:00
|
|
|
else:
|
|
|
|
var noError = true
|
|
|
|
try:
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.applyFixtureBlockToChain(ctx.blocks[idx],
|
2023-10-05 03:04:12 +00:00
|
|
|
com, checkSeal)
|
2021-05-15 06:37:40 +00:00
|
|
|
except ValueError, ValidationError, BlockNotFound, RlpError:
|
2019-09-07 10:50:34 +00:00
|
|
|
# failure is expected on this bad block
|
2022-12-09 08:17:33 +00:00
|
|
|
check (tb.hasException or (not tb.goodBlock))
|
2019-09-07 10:50:34 +00:00
|
|
|
noError = false
|
2023-09-24 23:53:20 +00:00
|
|
|
if ctx.debugMode:
|
|
|
|
ctx.debugData.add %{
|
2021-05-16 12:46:17 +00:00
|
|
|
"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
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
if ctx.debugMode and not ctx.json:
|
|
|
|
ctx.collectDebugData()
|
2019-09-24 13:18:48 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc debugDataFromAccountList(ctx: TestCtx): JsonNode =
|
|
|
|
let vmState = ctx.vmState
|
|
|
|
result = %{"debugData": ctx.debugData}
|
2023-03-17 13:20:52 +00:00
|
|
|
if not vmState.isNil:
|
|
|
|
result["accounts"] = vmState.dumpAccounts()
|
2022-09-26 16:14:12 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc debugDataFromPostStateHash(ctx: TestCtx): JsonNode =
|
|
|
|
let vmState = ctx.vmState
|
2022-09-26 16:14:12 +00:00
|
|
|
%{
|
2023-09-24 23:53:20 +00:00
|
|
|
"debugData": ctx.debugData,
|
2022-09-26 16:14:12 +00:00
|
|
|
"postStateHash": %($vmState.readOnlyStateDB.rootHash),
|
2023-09-24 23:53:20 +00:00
|
|
|
"expectedStateHash": %($ctx.postStateHash),
|
2023-03-17 13:20:52 +00:00
|
|
|
"accounts": vmState.dumpAccounts()
|
2022-09-26 16:14:12 +00:00
|
|
|
}
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc dumpDebugData(ctx: TestCtx, fixtureName: string, fixtureIndex: int, success: bool) =
|
|
|
|
let debugData = if ctx.postStateHash != Hash256():
|
|
|
|
debugDataFromPostStateHash(ctx)
|
2021-05-16 12:46:17 +00:00
|
|
|
else:
|
2023-09-24 23:53:20 +00:00
|
|
|
debugDataFromAccountList(ctx)
|
2022-09-26 16:14:12 +00:00
|
|
|
|
2019-09-10 12:52:36 +00:00
|
|
|
let status = if success: "_success" else: "_failed"
|
2023-09-24 23:53:20 +00:00
|
|
|
let name = fixtureName.replace('/', '-').replace(':', '-')
|
2023-07-05 09:30:01 +00:00
|
|
|
writeFile("debug_" & name & "_" & $fixtureIndex & status & ".json", debugData.pretty())
|
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
|
2023-01-10 17:02:21 +00:00
|
|
|
let specifyIndex = test_config.getConfiguration().index.get(0)
|
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
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
var ctx = parseTestCtx(fixture, testStatusIMPL)
|
2019-09-03 15:06:43 +00:00
|
|
|
|
2022-01-18 16:19:32 +00:00
|
|
|
let
|
2024-05-20 10:17:51 +00:00
|
|
|
memDB = newCoreDbRef DefaultDbMemory
|
2024-05-29 11:06:49 +00:00
|
|
|
stateDB = LedgerRef.init(memDB, emptyRlpHash)
|
2023-09-24 23:53:20 +00:00
|
|
|
config = getChainConfig(ctx.network)
|
2024-05-20 10:17:51 +00:00
|
|
|
com = CommonRef.new(memDB, config)
|
2022-01-18 16:19:32 +00:00
|
|
|
|
|
|
|
setupStateDB(fixture["pre"], stateDB)
|
|
|
|
stateDB.persist()
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
check stateDB.rootHash == ctx.genesisHeader.stateRoot
|
2019-09-03 15:06:43 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.debugMode = debugMode
|
|
|
|
ctx.trace = trace
|
|
|
|
ctx.json = test_config.getConfiguration().json
|
2019-09-10 12:52:36 +00:00
|
|
|
|
|
|
|
var success = true
|
|
|
|
try:
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.runTestCtx(com, testStatusIMPL)
|
2022-12-10 01:32:55 +00:00
|
|
|
let header = com.db.getCanonicalHead()
|
|
|
|
let lastBlockHash = header.blockHash
|
2023-09-24 23:53:20 +00:00
|
|
|
check lastBlockHash == ctx.lastBlockHash
|
|
|
|
success = lastBlockHash == ctx.lastBlockHash
|
|
|
|
if ctx.postStateHash != Hash256():
|
|
|
|
let rootHash = ctx.vmState.stateDB.rootHash
|
|
|
|
if ctx.postStateHash != rootHash:
|
2022-12-10 01:32:55 +00:00
|
|
|
raise newException(ValidationError, "incorrect postStateHash, expect=" &
|
|
|
|
$rootHash & ", get=" &
|
2023-09-24 23:53:20 +00:00
|
|
|
$ctx.postStateHash
|
2022-12-10 01:32:55 +00:00
|
|
|
)
|
2023-09-24 23:53:20 +00:00
|
|
|
elif lastBlockHash == ctx.lastBlockHash:
|
2022-12-10 01:32:55 +00:00
|
|
|
# multiple chain, we are using the last valid canonical
|
|
|
|
# state root to test against 'postState'
|
2024-05-29 11:06:49 +00:00
|
|
|
let stateDB = LedgerRef.init(memDB, header.stateRoot)
|
2024-01-22 09:11:37 +00:00
|
|
|
verifyStateDB(fixture["postState"], ledger.ReadOnlyStateDB(stateDB))
|
2023-01-09 14:39:36 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
success = lastBlockHash == ctx.lastBlockHash
|
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
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
if ctx.debugMode:
|
|
|
|
ctx.dumpDebugData(fixtureName, fixtureIndex, success)
|
2019-09-25 11:43:16 +00:00
|
|
|
|
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?"
|
2020-02-19 14:26:16 +00:00
|
|
|
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) =
|
2021-01-06 10:02:19 +00:00
|
|
|
const
|
2023-09-24 23:53:20 +00:00
|
|
|
legacyFolder = "eth_tests/LegacyTests/Constantinople/BlockchainTests"
|
2023-10-18 23:55:50 +00:00
|
|
|
newFolder = "eth_tests/BlockchainTests"
|
2023-09-24 23:53:20 +00:00
|
|
|
#newFolder = "eth_tests/EIPTests/BlockchainTests"
|
2023-10-18 23:55:50 +00:00
|
|
|
#newFolder = "eth_tests/EIPTests/Pyspecs/cancun"
|
2021-01-06 10:02:19 +00:00
|
|
|
|
2023-10-05 03:04:12 +00:00
|
|
|
let res = loadKzgTrustedSetup()
|
|
|
|
if res.isErr:
|
|
|
|
echo "FATAL: ", res.error
|
|
|
|
quit(QuitFailure)
|
|
|
|
|
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)
|
|
|
|
|
2021-01-06 10:02:19 +00:00
|
|
|
let folder = if config.legacy: legacyFolder else: newFolder
|
2023-09-24 23:53:20 +00:00
|
|
|
let path = "tests/fixtures/" & folder
|
2019-09-07 09:47:06 +00:00
|
|
|
let n = json.parseFile(path / config.testSubject)
|
|
|
|
var testStatusIMPL: TestStatus
|
2021-05-14 08:27:25 +00:00
|
|
|
testFixture(n, testStatusIMPL, debugMode = true, config.trace)
|
2019-09-07 09:47:06 +00:00
|
|
|
|
|
|
|
when isMainModule:
|
2023-06-24 13:56:44 +00:00
|
|
|
import std/times
|
2019-09-07 09:47:06 +00:00
|
|
|
var message: string
|
|
|
|
|
2023-05-10 15:40:48 +00:00
|
|
|
let start = getTime()
|
|
|
|
|
2019-09-07 09:47:06 +00:00
|
|
|
## 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)
|
2023-05-10 15:40:48 +00:00
|
|
|
let elpd = getTime() - start
|
|
|
|
echo "TIME: ", elpd
|
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
|
2021-05-14 03:47:58 +00:00
|
|
|
# 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
|