2018-09-07 19:44:17 +00:00
|
|
|
# Nimbus
|
2024-02-21 09:14:20 +00:00
|
|
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
2018-09-07 19:44:17 +00:00
|
|
|
# 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
|
2024-06-14 07:31:08 +00:00
|
|
|
std/[strutils, tables, json, os, sets],
|
2019-11-20 15:25:09 +00:00
|
|
|
./test_helpers, ./test_allowed_to_fail,
|
2022-12-02 04:39:12 +00:00
|
|
|
../nimbus/core/executor, test_config,
|
2019-11-13 14:49:39 +00:00
|
|
|
../nimbus/transaction,
|
2024-06-17 07:56:39 +00:00
|
|
|
../nimbus/[evm/state, evm/types],
|
2023-12-12 19:12:56 +00:00
|
|
|
../nimbus/db/ledger,
|
2022-12-02 04:39:12 +00:00
|
|
|
../nimbus/common/common,
|
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/core/eip4844,
|
2023-01-10 17:02:21 +00:00
|
|
|
../tools/common/helpers as chp,
|
|
|
|
../tools/evmstate/helpers,
|
2023-01-09 14:39:36 +00:00
|
|
|
../tools/common/state_clearing,
|
2024-10-04 14:34:31 +00:00
|
|
|
eth/common/transaction_utils,
|
2022-01-10 09:04:06 +00:00
|
|
|
unittest2,
|
2024-05-30 12:54:03 +00:00
|
|
|
stew/byteutils,
|
|
|
|
results
|
2018-09-07 19:44:17 +00:00
|
|
|
|
2019-03-18 01:55:02 +00:00
|
|
|
type
|
2023-09-24 23:53:20 +00:00
|
|
|
TestCtx = object
|
2019-03-18 01:55:02 +00:00
|
|
|
name: string
|
2024-10-16 01:34:12 +00:00
|
|
|
parent: Header
|
|
|
|
header: Header
|
2019-03-18 01:55:02 +00:00
|
|
|
pre: JsonNode
|
|
|
|
tx: Transaction
|
2024-10-16 01:34:12 +00:00
|
|
|
expectedHash: Hash32
|
|
|
|
expectedLogs: Hash32
|
2023-01-10 17:02:21 +00:00
|
|
|
chainConfig: ChainConfig
|
2019-03-18 01:55:02 +00:00
|
|
|
debugMode: bool
|
2019-08-19 14:12:32 +00:00
|
|
|
trace: bool
|
2019-03-21 09:01:26 +00:00
|
|
|
index: int
|
2023-03-17 13:20:52 +00:00
|
|
|
fork: string
|
2019-02-28 08:22:07 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
var
|
|
|
|
trustedSetupLoaded = false
|
|
|
|
|
2019-03-18 14:18:04 +00:00
|
|
|
proc toBytes(x: string): seq[byte] =
|
|
|
|
result = newSeq[byte](x.len)
|
|
|
|
for i in 0..<x.len: result[i] = x[i].byte
|
|
|
|
|
2024-10-16 01:34:12 +00:00
|
|
|
method getAncestorHash*(vmState: BaseVMState; blockNumber: BlockNumber): Hash32 =
|
2019-03-18 14:18:04 +00:00
|
|
|
if blockNumber >= vmState.blockNumber:
|
2024-10-16 01:34:12 +00:00
|
|
|
return default(Hash32)
|
2019-03-18 14:18:04 +00:00
|
|
|
elif blockNumber < 0:
|
2024-10-16 01:34:12 +00:00
|
|
|
return default(Hash32)
|
2019-03-18 14:18:04 +00:00
|
|
|
elif blockNumber < vmState.blockNumber - 256:
|
2024-10-16 01:34:12 +00:00
|
|
|
return default(Hash32)
|
2019-03-18 14:18:04 +00:00
|
|
|
else:
|
2024-10-16 01:34:12 +00:00
|
|
|
return keccak256(toBytes($blockNumber))
|
2019-03-18 14:18:04 +00:00
|
|
|
|
2024-02-21 09:14:20 +00:00
|
|
|
func normalizeFileName(x: string): string =
|
|
|
|
const invalidChars = ['/', '\\', '?', '%', '*', ':', '|', '"', '<', '>', ',', ';', '=']
|
|
|
|
result = newStringOfCap(x.len)
|
|
|
|
for c in x:
|
|
|
|
if c in invalidChars: result.add '_'
|
|
|
|
else: result.add c
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc dumpDebugData(ctx: TestCtx, vmState: BaseVMState, gasUsed: GasInt, success: bool) =
|
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: %[]
|
2019-03-18 01:55:02 +00:00
|
|
|
let debugData = %{
|
|
|
|
"gasUsed": %gasUsed,
|
2019-08-19 14:12:32 +00:00
|
|
|
"structLogs": tracingResult,
|
2023-03-17 13:20:52 +00:00
|
|
|
"accounts": vmState.dumpAccounts()
|
2019-03-18 01:55:02 +00:00
|
|
|
}
|
2019-03-21 09:01:26 +00:00
|
|
|
let status = if success: "_success" else: "_failed"
|
2024-02-21 09:14:20 +00:00
|
|
|
let fileName = normalizeFileName(ctx.name)
|
|
|
|
writeFile(fileName & "_" & ctx.fork & "_" & $ctx.index & status & ".json", debugData.pretty())
|
2019-03-18 01:55:02 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
proc testFixtureIndexes(ctx: var TestCtx, testStatusIMPL: var TestStatus) =
|
2022-12-02 04:39:12 +00:00
|
|
|
let
|
2024-12-13 04:53:41 +00:00
|
|
|
com = CommonRef.new(newCoreDbRef DefaultDbMemory, nil, ctx.chainConfig)
|
2024-11-25 15:37:57 +00:00
|
|
|
parent = Header(stateRoot: emptyRoot)
|
2023-09-24 23:53:20 +00:00
|
|
|
tracer = if ctx.trace:
|
2023-08-02 10:17:40 +00:00
|
|
|
newLegacyTracer({})
|
|
|
|
else:
|
|
|
|
LegacyTracer(nil)
|
2022-09-26 16:14:12 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
if com.isCancunOrLater(ctx.header.timestamp):
|
|
|
|
if not trustedSetupLoaded:
|
|
|
|
let res = loadKzgTrustedSetup()
|
|
|
|
if res.isErr:
|
|
|
|
echo "FATAL: ", res.error
|
|
|
|
quit(QuitFailure)
|
|
|
|
trustedSetupLoaded = true
|
|
|
|
|
2022-09-26 16:14:12 +00:00
|
|
|
let vmState = BaseVMState.new(
|
2023-08-02 10:17:40 +00:00
|
|
|
parent = parent,
|
2023-09-24 23:53:20 +00:00
|
|
|
header = ctx.header,
|
2023-08-02 10:17:40 +00:00
|
|
|
com = com,
|
|
|
|
tracer = tracer,
|
2024-08-20 13:23:24 +00:00
|
|
|
storeSlotHash = ctx.trace,
|
2022-12-02 04:39:12 +00:00
|
|
|
)
|
2019-11-28 10:02:11 +00:00
|
|
|
|
2019-03-21 09:01:26 +00:00
|
|
|
var gasUsed: GasInt
|
2024-10-04 14:34:31 +00:00
|
|
|
let sender = ctx.tx.recoverSender().expect("valid signature")
|
2019-03-21 09:01:26 +00:00
|
|
|
|
2018-09-19 16:46:14 +00:00
|
|
|
vmState.mutateStateDB:
|
2023-09-24 23:53:20 +00:00
|
|
|
setupStateDB(ctx.pre, db)
|
2018-09-19 16:46:14 +00:00
|
|
|
|
2023-12-12 19:12:56 +00:00
|
|
|
# this is an important step when using `db/ledger`
|
2020-06-01 04:49:56 +00:00
|
|
|
# it will affect the account storage's location
|
|
|
|
# during the next call to `getComittedStorage`
|
|
|
|
db.persist()
|
2019-11-19 06:12:13 +00:00
|
|
|
|
2024-05-29 09:26:27 +00:00
|
|
|
let rc = vmState.processTransaction(
|
2024-07-04 13:48:36 +00:00
|
|
|
ctx.tx, sender, ctx.header)
|
2024-05-29 09:26:27 +00:00
|
|
|
if rc.isOk:
|
|
|
|
gasUsed = rc.value
|
|
|
|
|
|
|
|
let miner = ctx.header.coinbase
|
2024-07-04 13:48:36 +00:00
|
|
|
coinbaseStateClearing(vmState, miner)
|
2024-05-29 09:26:27 +00:00
|
|
|
|
|
|
|
block post:
|
2024-10-27 18:56:28 +00:00
|
|
|
let obtainedHash = vmState.readOnlyStateDB.getStateRoot()
|
2023-09-24 23:53:20 +00:00
|
|
|
check obtainedHash == ctx.expectedHash
|
2019-02-28 08:22:07 +00:00
|
|
|
let logEntries = vmState.getAndClearLogEntries()
|
2023-03-20 11:51:09 +00:00
|
|
|
let actualLogsHash = rlpHash(logEntries)
|
2023-09-24 23:53:20 +00:00
|
|
|
check(ctx.expectedLogs == actualLogsHash)
|
|
|
|
if ctx.debugMode:
|
|
|
|
let success = ctx.expectedLogs == actualLogsHash and obtainedHash == ctx.expectedHash
|
|
|
|
ctx.dumpDebugData(vmState, gasUsed, success)
|
2018-09-19 16:46:14 +00:00
|
|
|
|
2019-03-18 03:05:24 +00:00
|
|
|
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus,
|
2023-01-10 17:02:21 +00:00
|
|
|
trace = false, debugMode = false) =
|
2023-09-24 23:53:20 +00:00
|
|
|
var ctx: TestCtx
|
2018-09-19 16:46:14 +00:00
|
|
|
var fixture: JsonNode
|
|
|
|
for label, child in fixtures:
|
|
|
|
fixture = child
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.name = label
|
2018-09-19 16:46:14 +00:00
|
|
|
break
|
2018-09-07 19:44:17 +00:00
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.pre = fixture["pre"]
|
|
|
|
ctx.parent = parseParentHeader(fixture["env"])
|
|
|
|
ctx.header = parseHeader(fixture["env"])
|
|
|
|
ctx.trace = trace
|
|
|
|
ctx.debugMode = debugMode
|
2023-01-10 17:02:21 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
post = fixture["post"]
|
|
|
|
txData = fixture["transaction"]
|
|
|
|
conf = getConfiguration()
|
|
|
|
|
|
|
|
template prepareFork(forkName: string) =
|
|
|
|
try:
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.chainConfig = getChainConfig(forkName)
|
2023-01-10 17:02:21 +00:00
|
|
|
except ValueError as ex:
|
|
|
|
debugEcho ex.msg
|
2024-11-01 19:06:26 +00:00
|
|
|
testStatusIMPL = TestStatus.FAILED
|
2023-01-10 17:02:21 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
template runSubTest(subTest: JsonNode) =
|
2024-10-16 01:34:12 +00:00
|
|
|
ctx.expectedHash = Hash32.fromJson(subTest["hash"])
|
|
|
|
ctx.expectedLogs = Hash32.fromJson(subTest["logs"])
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.tx = parseTx(txData, subTest["indexes"])
|
|
|
|
ctx.testFixtureIndexes(testStatusIMPL)
|
2023-01-10 17:02:21 +00:00
|
|
|
|
|
|
|
if conf.fork.len > 0:
|
|
|
|
if not post.hasKey(conf.fork):
|
|
|
|
debugEcho "selected fork not available: " & conf.fork
|
|
|
|
return
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.fork = conf.fork
|
2023-01-10 17:02:21 +00:00
|
|
|
let forkData = post[conf.fork]
|
|
|
|
prepareFork(conf.fork)
|
|
|
|
if conf.index.isNone:
|
|
|
|
for subTest in forkData:
|
|
|
|
runSubTest(subTest)
|
2023-09-24 23:53:20 +00:00
|
|
|
inc ctx.index
|
2020-02-19 14:26:16 +00:00
|
|
|
else:
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.index = conf.index.get()
|
|
|
|
if ctx.index > forkData.len or ctx.index < 0:
|
2023-01-10 17:02:21 +00:00
|
|
|
debugEcho "selected index out of range(0-$1), requested $2" %
|
2023-09-24 23:53:20 +00:00
|
|
|
[$forkData.len, $ctx.index]
|
2023-01-10 17:02:21 +00:00
|
|
|
return
|
|
|
|
|
2023-09-24 23:53:20 +00:00
|
|
|
let subTest = forkData[ctx.index]
|
2023-01-10 17:02:21 +00:00
|
|
|
runSubTest(subTest)
|
|
|
|
else:
|
|
|
|
for forkName, forkData in post:
|
|
|
|
prepareFork(forkName)
|
2023-09-24 23:53:20 +00:00
|
|
|
ctx.fork = forkName
|
|
|
|
ctx.index = 0
|
2023-01-10 17:02:21 +00:00
|
|
|
for subTest in forkData:
|
|
|
|
runSubTest(subTest)
|
2023-09-24 23:53:20 +00:00
|
|
|
inc ctx.index
|
2019-03-18 03:05:24 +00:00
|
|
|
|
2019-09-21 05:45:23 +00:00
|
|
|
proc generalStateJsonMain*(debugMode = false) =
|
2021-01-06 10:02:19 +00:00
|
|
|
const
|
2023-09-24 23:53:20 +00:00
|
|
|
legacyFolder = "eth_tests/LegacyTests/Constantinople/GeneralStateTests"
|
|
|
|
newFolder = "eth_tests/GeneralStateTests"
|
|
|
|
#newFolder = "eth_tests/EIPTests/StateTests"
|
2021-01-06 10:02:19 +00:00
|
|
|
|
2021-01-14 14:33:18 +00:00
|
|
|
let config = getConfiguration()
|
|
|
|
if config.testSubject == "" or not debugMode:
|
2019-03-18 01:55:02 +00:00
|
|
|
# run all test fixtures
|
2021-01-14 14:33:18 +00:00
|
|
|
if config.legacy:
|
|
|
|
suite "generalstate json tests":
|
2023-03-17 13:20:52 +00:00
|
|
|
jsonTest(legacyFolder, "GeneralStateTests", testFixture, skipGSTTests)
|
2021-01-14 14:33:18 +00:00
|
|
|
else:
|
|
|
|
suite "new generalstate json tests":
|
|
|
|
jsonTest(newFolder, "newGeneralStateTests", testFixture, skipNewGSTTests)
|
2019-03-18 01:55:02 +00:00
|
|
|
else:
|
|
|
|
# execute single test in debug mode
|
2019-03-18 03:05:24 +00:00
|
|
|
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
|
2020-02-19 14:26:16 +00:00
|
|
|
let path = "tests" / "fixtures" / folder
|
2019-03-18 03:05:24 +00:00
|
|
|
let n = json.parseFile(path / config.testSubject)
|
2019-03-18 01:55:02 +00:00
|
|
|
var testStatusIMPL: TestStatus
|
2023-01-10 17:02:21 +00:00
|
|
|
testFixture(n, testStatusIMPL, config.trace, true)
|
2019-03-18 03:05:24 +00:00
|
|
|
|
|
|
|
when isMainModule:
|
2023-06-24 13:56:44 +00:00
|
|
|
import std/times
|
2019-03-18 03:05:24 +00:00
|
|
|
var message: string
|
|
|
|
|
2023-05-10 15:40:48 +00:00
|
|
|
let start = getTime()
|
|
|
|
|
2019-03-18 03:05:24 +00:00
|
|
|
## Processing command line arguments
|
|
|
|
if processArguments(message) != Success:
|
|
|
|
echo message
|
|
|
|
quit(QuitFailure)
|
|
|
|
else:
|
|
|
|
if len(message) > 0:
|
|
|
|
echo message
|
|
|
|
quit(QuitSuccess)
|
2021-04-24 04:12:10 +00:00
|
|
|
|
2019-09-21 05:45:23 +00:00
|
|
|
generalStateJsonMain(true)
|
2023-05-10 15:40:48 +00:00
|
|
|
let elpd = getTime() - start
|
|
|
|
echo "TIME: ", elpd
|