Differential Testing Engine
To guarantee convergence between Ο_geth(E_Geth) and Ο_reth(E_Reth), this differential testing engine generates deterministic execution paths that maximize EVM edge-case entropy, runs them through both client boundary implementations, and checks bitwise identity over the resulting SSZ-serialized Ξ_canonical byte streams.
1. Test Harness Format
Test vectors are defined as JSON (KaysentinelTestCase): a pre-state configuration, a transaction to execute, and the expected SSR root. See tests/transient_storage_case.json for a worked example.
2. Edge-Case Matrix
Test Vector 1 β Transient Storage TraceMid Clean-Up (EIP-1153)
Objective: verify the extractor pulls transient storage values before VM teardown purges them, while ignoring keys written inside sub-calls that reverted.
Execution path:
Tx.tocalls Contract A.- A executes
TSTORE(slot=0x01, value=0xAA). - A
DELEGATECALLs into Contract B. - B executes
TSTORE(slot=0x01, value=0xBB)andTSTORE(slot=0x02, value=0xCC). - B hits
REVERT, rolling back its stack frame.
Assertion: the final TransientMutation list for Contract A in Ξ_canonical must contain exactly one entry β [slot=0x01, value=0xAA]. B's writes must be fully discarded, proving extraction happens at the TraceMid β Post boundary and doesn't pick up stale tree artifacts.
Test Vector 2 β Post-Cancun SELFDESTRUCT Lifecycles (EIP-6780)
Objective: validate lifecycle statuses assigned by Geth's core/state/journal.go and Reth's BundleState align with Cancun semantics.
Execution path:
Tx.todeploys Contract C viaCREATE2.- C calls a function executing
SELFDESTRUCTtargeting an external escrow vault, same transaction. - Contract D (100+ blocks old) is also forced to
SELFDESTRUCTin the same block.
Assertion: C's account entry must show lifecycle_status = 2 (SELFDESTRUCT_PENDING) with balance wiped. D's account entry must stay lifecycle_status = 0 (LIVE), code intact, only the balance mutation recorded.
Test Vector 3 β High-Entropy Multi-Account Modification (Sorting Validation)
Objective: stress-test map iteration ordering differences between Go's randomized map traversal and Rust's BTreeMap.
Execution path:
- A proxy batching contract runs 20 sub-calls touching randomly generated addresses (A1...A20) via
BALANCEmodifications and storage initialization. - Addresses are touched out of sequential order at runtime (e.g. A15 β A3 β A19).
Assertion: Geth and Reth extractors must produce byte-identical SSZ output, proving account_mutations is sorted ascending lexicographically by address and storage_diffs by slot before serialization.
3. Verification Script
See scripts/verify_ssr.py. It takes the SSZ byte outputs from both runtimes, checks length equality, computes SHA-256 digests, and β on mismatch β walks the byte streams to report the first differing offset.