nethtest: add --engineTest, --stateTest, --jsonout, --workers flags#11035
nethtest: add --engineTest, --stateTest, --jsonout, --workers flags#11035spencer-tb wants to merge 10 commits into
Conversation
4e22df4 to
a5d2ab4
Compare
…ests Parse all fixture files upfront and flatten to individual test cases before distributing to workers, instead of parallelizing by file. Nethermind full suite: 2m48s with 8 workers (was 2m58s file-level).
- Pre-warm thread pool capped at CPU count (fixes starvation at w>=4) - Parallel file parsing via Parallel.For - UTF-8 byte span deserialization (File.ReadAllBytes + ReadOnlySpan<byte>) - Pre-allocated results array instead of ConcurrentBag + sort - Lightweight BlockchainTestsRunner constructor for parallel path - Compiled Regex for filter matching - Pre-initialize KZG once upfront Prague engine tests: 24.1s → 18.5s at w=8 (23% faster). Full suite: 2m48s → 1m45s at w=4 (37% faster).
…r field, hide timeInMs
…irect - Add ForkName to BlockchainTest, use raw network string (e.g. CancunToPragueAtTime15k) - Add lastBlockHash, lastPayloadStatus to JSON output for block/engine tests - Remove stateRoot from block/engine results (only state tests) - Report validation error in `error` even on pass for negative tests: engine tests via PayloadStatus.ValidationError, block tests via BlockRemoved event and ValidateSuggestedBlock, state tests via TransactionResult.ErrorDescription - Suggest invalid blocks (blockHeader=null) to actually test rejection - Enables EELS consume direct exception mapping for all test types
… hardcoded string
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
New `validate` CLI command for running EEST fixtures directly against client EVM binaries, replacing Hive for execution correctness testing. Usage: validate health # health check all clients validate engine --client geth # engine tests validate state --client besu # state tests validate block --client nethermind # block tests Features: - 7 clients: geth, besu, nethermind, erigon, reth, ethrex, nimbus - Per-type Pydantic result models: StateTestResult, BlockTestResult, EngineTestResult with type-specific fields - Exception matching: maps client error strings to EEST exception types via ExceptionMapper, verifies correct exception for every invalid test (--no-exception-check to disable) - Cross-validation: lastBlockHash against fixture, lastPayloadStatus (VALID/INVALID) for engine tests - validate.toml config for client binary paths with per-type overrides (state-bin, block-bin, engine-bin) - Auto bin-workers and xdist tuning per client - Bundled Frontier sanity fixtures for health checks - Shared validate_helpers.py for validation logic Client binary PRs: - geth: ethereum/go-ethereum#34650 - erigon: erigontech/erigon#20315 - besu: besu-eth/besu#10184 - nethermind: NethermindEth/nethermind#11035 - reth: paradigmxyz/reth#23361 - ethrex: lambdaclass/ethrex#6445 - nimbus: status-im/nimbus-eth1#4101 - revm: bluealloy/revm#3544 Tracking issue: ethereum#2319
|
@claude review |
|
Claude finished @kamilchodola's task in 6m 43s —— View job Code Review
SummaryThis PR adds Counts: 0 Critical / 3 High / 5 Medium / 4 Low HighH1 — Thread-safety race on
|
| try | ||
| { | ||
| var source = new TestsSourceLoader(new LoadBlockchainTestFileStrategy(), item.file); | ||
| var runner = new BlockchainTestsRunner(source, filter, item.file.Contains("chain") ? chainId : chainId, trace: false, traceMemory, traceStack, jsonOutput: true, suppressOutput: true); |
There was a problem hiding this comment.
[Medium] Dead ternary — both branches are identical
item.file.Contains("chain") ? chainId : chainId always evaluates to chainId. This looks like an unfinished conditional (perhaps meant to use a Gnosis chain ID for gnosis fixtures). Either simplify to just chainId, or complete the intended logic.
| var runner = new BlockchainTestsRunner(source, filter, item.file.Contains("chain") ? chainId : chainId, trace: false, traceMemory, traceStack, jsonOutput: true, suppressOutput: true); | |
| var runner = new BlockchainTestsRunner(source, filter, chainId, trace: false, traceMemory, traceStack, jsonOutput: true, suppressOutput: true); |
| if (testTypeCount != 1) | ||
| { | ||
| Console.WriteLine("Please specify one of: --stateTest, --blockTest, or --engineTest"); | ||
| return 0; |
There was a problem hiding this comment.
[Medium] Returns exit code 0 on usage error
Callers (CI scripts, wrappers) cannot distinguish a misconfiguration from a successful empty run. This should return a non-zero exit code to signal the error.
| return 0; | |
| return 1; |
| var stackJson = string.Join(",", entry.Stack.Select(s => $"\"{s}\"")); | ||
| Console.Error.Write($"{{\"pc\":{entry.Pc},\"op\":{entry.Operation},\"gas\":\"0x{entry.Gas:x}\",\"gasCost\":\"0x{entry.GasCost:x}\",\"stack\":[{stackJson}],\"depth\":{entry.Depth},\"memSize\":{entry.MemSize}"); | ||
| if (!string.IsNullOrEmpty(entry.Error)) | ||
| Console.Error.Write($",\"error\":\"{entry.Error}\""); |
There was a problem hiding this comment.
[Low] Error string embedded in JSON without encoding
If entry.Error contains ", \, or control characters, the output is malformed JSON. Use JsonSerializer.Serialize to safely encode the value:
| Console.Error.Write($",\"error\":\"{entry.Error}\""); | |
| Console.Error.Write($",\"error\":{System.Text.Json.JsonSerializer.Serialize(entry.Error)}"); |
| if (validationError is null) | ||
| throw new Exception( | ||
| $"engine_newPayloadV{newPayloadVersion} unexpected RPC error: {errorResponse.Error?.Code} {errorResponse.Error?.Message}"); | ||
| continue; |
There was a problem hiding this comment.
[High] Silent continue masks real RPC errors when invalid payload is expected
When validationError != null (test expects rejection), any RPC-level error is silently skipped. An internal server error, deserialization failure, or version mismatch is treated identically to an expected INVALID response, causing a false-positive pass.
Consider at minimum throwing on unexpected error codes (e.g. -32601 method not found, -32603 internal error) even when a validation error is expected:
if (npResponse is JsonRpcErrorResponse errorResponse)
{
if (validationError is null)
throw new Exception(
$"engine_newPayloadV{newPayloadVersion} unexpected RPC error: {errorResponse.Error?.Code} {errorResponse.Error?.Message}");
// RPC-level errors like method-not-found or internal-error are not valid rejections.
// Only -32602 (InvalidParams) or engine-specific codes map to expected invalid-payload behaviour.
if (errorResponse.Error?.Code is not (ErrorCodes.InvalidParams or MergeErrorCodes.UnsupportedFork))
throw new Exception(
$"engine_newPayloadV{newPayloadVersion} unexpected RPC error code {errorResponse.Error?.Code}: {errorResponse.Error?.Message}");
continue;
}| return bag.OrderBy(x => x.index).SelectMany(x => x.results).ToList(); | ||
| } | ||
|
|
||
| private static List<BlockchainTest> ParseBlockchainTestFile(string file, Regex? filterRegex) |
There was a problem hiding this comment.
[Medium] Dead code — ParseBlockchainTestFile is never called
This method is defined but has no callers. It also adds null entries to the returned list (lines 238, 251), which would cause NullReferenceException if the code were ever wired up. Remove it to keep the file clean.
Summary
Add
--engineTestflag tonethtestfor direct engine fixture execution, along with--stateTest,--jsonout,--workers, and--runflags. This enables runningblockchain_test_enginefixtures without requiring Hive or full client startup.nethtest --engineTestEngine tests go through the real
IEngineRpcModuleviaIJsonRpcService.SendRequestAsync("engine_newPayloadVX"), exercising the full Engine API code path including version validation, payload processing, and forkchoice state management. This is the same path that Hiveconsume engineexercises, but without Docker/client startup overhead.--workersflagAdds parallel file processing via
Parallel.ForEachAsyncto all three runners. This doesn't do much right now unless we make it more lightweight. WIP.Other changes
--stateTestflag — explicitly run as state test (previously the implicit default; now required)--jsonoutflag — JSON array output for blocktest/enginetest (statetest already outputs JSON)--runalias for--filter— matching geth/erigon naming conventionAssert.ThatinRunNewPayloadswith explicit exceptions for structured error reportingBenchmarks
Tested against EEST v5.3.0 stable fixtures.
For reference, Hive runs of the same test suite on the same Nethermind version:
consume engine: 16h 39mconsume rlp: >24hnethtest --engineTest— exercises the same engine code paths asconsume engine(40,519 tests):nethtest --blockTest— exercises the same execution paths asconsume rlp(43,912 tests):nethtest --stateTest(40,553 tests):Hive parity
All 7 Hive
consume enginefailures for Nethermind on v5.3.0 are exception mapper issues in EELS (correct INVALID status returned, but error message string not mapped inNethermindExceptionMapper). The direct runner correctly reports these as passing since the execution result is correct. To fully test the mapper changes this will need to be integrated into consume direct via EELS.Usage
Related: ethereum/go-ethereum#34650, erigontech/erigon#20315, ethereum/execution-specs#2650