This document explains how to run Ethereum reference tests in Besu and how to enable JSON tracing during test and block execution. This is useful for debugging EVM behavior, inspecting opcode execution, and verifying correctness against official test vectors.
To run the Ethereum reference tests included in the Besu codebase, use the following Gradle task:
./gradlew referenceTestsThis will execute the available test suites (such as GeneralStateTests and execution-spec-tests) and validate Besu's EVM behavior.
Note:
- Out-of-memory (OOM) errors are common due to the size and number of tests. You may need to increase the heap size using
-Xmx(e.g.,./gradlew referenceTests -Dorg.gradle.jvmargs="-Xmx8g")
Execution-spec-tests are generated with class names that reflect their hardfork and EIP directory structure. This allows targeted test execution using standard Gradle --tests filters.
# Run all Prague execution spec tests (blockchain + state)
./gradlew referenceTests --tests "*ExecutionSpec*_prague_*"
# Run only Amsterdam state tests
./gradlew referenceTests --tests "*ExecutionSpecStateTest_amsterdam_*"
# Run all Cancun blockchain tests
./gradlew referenceTests --tests "*ExecutionSpecBlockchainTest_cancun_*"# Run only EIP-7702 tests
./gradlew referenceTests --tests "*eip7702*"
# Run only EIP-4844 blob tests
./gradlew referenceTests --tests "*eip4844*"# Run Prague EIP-2537 BLS precompile tests specifically
./gradlew referenceTests --tests "*_prague_eip2537_*"# Run all static legacy tests
./gradlew referenceTests --tests "*ExecutionSpec*_static_*"
# Run a specific static test category
./gradlew referenceTests --tests "*_static_stCreate2_*"Test classes follow the pattern:
ExecutionSpec{Blockchain,State}Test_{hardfork}_{eip_or_topic}_{batch_index}
For example:
ExecutionSpecBlockchainTest_prague_eip7702_set_code_tx_0ExecutionSpecStateTest_cancun_eip4844_blobs_2ExecutionSpecBlockchainTest_static_stCreate2_1ExecutionSpecBlockchainTest_frontier_opcodes_0
Note: These hardfork/EIP filters apply only to execution-spec-tests. The legacy
GeneralStateReferenceTestandBlockchainReferenceTestclasses still use sequential numbering. For those, use the runtime system propertiestest.ethereum.state.eipsandtest.ethereum.includeinstead.
In addition to the stable execution-spec-tests fixtures, Besu supports a second set of pre-release (devnet) fixtures from upstream. These contain tests for upcoming hardforks (e.g., Amsterdam).
# Run all devnet/pre-release reference tests
./gradlew referenceTestsDevnet
# Run only Amsterdam devnet tests
./gradlew referenceTestsDevnet --tests "*_amsterdam_*"
# Run both stable + devnet
./gradlew referenceTests referenceTestsDevnetThe default referenceTests task excludes devnet tests, so CI is unaffected.
Devnet test classes follow the same pattern as stable ones, but with an ExecutionSpecDevnet prefix:
ExecutionSpecDevnet{Blockchain,State}Test_{hardfork}_{eip_or_topic}_{batch_index}
- Update the
versionin thedevnetTarConfigdependency inethereum/referencetests/build.gradle - Make any required infrastructure changes (new header fields, etc.)
- Run
./gradlew --write-verification-metadata sha256to update checksums - Commit all changes together
The devnet fixtures are resolved from the same GitHub Ivy repository as stable fixtures. The dependency is declared separately via the devnetTarConfig configuration in ethereum/referencetests/build.gradle.
Besu supports detailed opcode-level JSON tracing. You can enable it using either a JVM system property or an environment variable.
-Dbesu.debug.traceBlocks=trueexport BESU_TRACE_BLOCKS=trueThis enables a fallback implementation of BlockAwareOperationTracer if no plugin is configured. The default tracer used is BlockAwareJsonTracer.
JSON trace output does not appear in the console. To view it, open the associated Gradle test report (usually located in build/reports/tests/test/index.html) and find the specific test case output.
When enabled, tracing includes:
- Opcode execution and names
- Stack state
- Gas remaining and gas cost
- Memory size
- Precompile execution
- Contract creation and call frames
- Transaction lifecycle events (start, prepare, end)
- Exceptional halts
Each traced operation emits structured JSON data representing the EVM state at that point.
The tracer prints a complete JSON trace of each block’s execution to standard output at the end of the block:
==== JSON Trace for Block <BLOCK_NUMBER> (<BLOCK_HASH>) ====
<trace entries>
Example:
{
"pc": 0,
"op": "0x60",
"opName": "PUSH1",
"gas": 999999,
"gasCost": 3,
"stack": [],
"memSize": 0,
"depth": 1,
"refund": 0
}The tracer is implemented in:
org.hyperledger.besu.ethereum.mainnet.BlockAwareJsonTracer
It uses a StringWriter and a StandardJsonTracer to collect and format execution traces. Output is flushed during the traceEndBlock(...) callback.
The BlockAwareJsonTracer is enabled automatically when no plugin provides a custom tracer and one of the tracing flags is set:
if (Boolean.getBoolean("besu.debug.traceBlocks")
|| "true".equalsIgnoreCase(System.getenv("BESU_TRACE_BLOCKS"))) {
return new BlockAwareJsonTracer();
}- Tracing is for debugging purposes only and should not be enabled in production environments.
- Trace output can become large, especially for blocks with many transactions.
- Tracing does not affect EVM execution semantics.