Context
While reviewing Nethermind PR NethermindEth/nethermind#11547 we identified an EIP-7928 BAL coverage gap in EEST that is worth considering for bal-devnet-7 alignment. Filing in the spirit of the bal-devnet-7 tracker (#2804).
The gap
When an execution client wraps its code-info repository with a precompile-caching decorator (e.g. Nethermind's PrecompileCachedCodeInfoRepository, used when block prewarming is enabled) the decorator's IsPrecompile fast-path returns the cached CodeInfo without going through the underlying world state. Without an explicit AddAccountRead on that fast-path, code lookups on precompile addresses can be missing from the generated BAL.
Existing test_pointer_to_precompile catches this for the EIP-7702 EOA-delegated-to-precompile via CALL scenario, but the gap is broader:
- Direct DELEGATECALL or CALLCODE to a precompile address: the call sets
target == ExecutingAccount (NOT codeSource), so the indirect AccountExists(target) records the caller, not the precompile. The codeSource (precompile) is therefore missing from BAL.
For plain CALL / STATICCALL the gap is masked because target == codeSource and AccountExists(target) records the precompile incidentally. For DELEGATECALL / CALLCODE there is no fallback path.
Why pyspec doesn't currently catch it
tests/prague/eip7702_set_code_tx/test_set_code_txs_2.py::test_pointer_to_precompile only exercises 7702-delegated CALL paths into a precompile. There is no pyspec test (that we can find) that:
- has a contract execute
DELEGATECALL or CALLCODE to a precompile address (e.g. 0x02 Sha256), and
- asserts that
<precompile_addr> appears in the generated BlockAccessList.
A node running with the precompile-caching decorator active would compute a BAL hash that omits the precompile address, diverging from clients that record it. This becomes a consensus risk if/when EIP-7928 is consensus-active.
Suggested coverage
State / blockchain test (one variant per opcode):
- Pre-state: contract whose runtime code does
DELEGATECALL <precompile_addr> <gas>; sender EOA with funds.
- Tx: sender invokes the contract.
- Post-state assertion: BAL contains an entry for
<precompile_addr> (read-only - no balance / nonce / storage changes), in addition to the contract address.
A second variant for CALLCODE (or just CALL with target != codeSource constructed differently) would close the related opcode.
References
- Nethermind regression test that catches this in unit form:
Nethermind.Evm.Test/Eip7928Tests.cs::DelegateCall_to_precompile_records_codeSource_in_BAL_under_PrecompileCachedCodeInfoRepository - in NethermindEth/nethermind#11547.
- Nethermind decorator fix:
f3c0b2706e in the same PR.
A second test in that PR (Direct_transaction_to_precompile_records_recipient_in_BAL_*) verified that tx.to == precompile is already incidentally recorded by an existing TransactionProcessor.Execute path - so that scenario does NOT need additional EEST coverage for the same defect class.
Context
While reviewing Nethermind PR NethermindEth/nethermind#11547 we identified an EIP-7928 BAL coverage gap in EEST that is worth considering for
bal-devnet-7alignment. Filing in the spirit of thebal-devnet-7tracker (#2804).The gap
When an execution client wraps its code-info repository with a precompile-caching decorator (e.g. Nethermind's
PrecompileCachedCodeInfoRepository, used when block prewarming is enabled) the decorator'sIsPrecompilefast-path returns the cachedCodeInfowithout going through the underlying world state. Without an explicitAddAccountReadon that fast-path, code lookups on precompile addresses can be missing from the generated BAL.Existing
test_pointer_to_precompilecatches this for the EIP-7702 EOA-delegated-to-precompile via CALL scenario, but the gap is broader:target == ExecutingAccount(NOTcodeSource), so the indirectAccountExists(target)records the caller, not the precompile. ThecodeSource(precompile) is therefore missing from BAL.For plain CALL / STATICCALL the gap is masked because
target == codeSourceandAccountExists(target)records the precompile incidentally. For DELEGATECALL / CALLCODE there is no fallback path.Why pyspec doesn't currently catch it
tests/prague/eip7702_set_code_tx/test_set_code_txs_2.py::test_pointer_to_precompileonly exercises 7702-delegated CALL paths into a precompile. There is no pyspec test (that we can find) that:DELEGATECALLorCALLCODEto a precompile address (e.g.0x02Sha256), and<precompile_addr>appears in the generatedBlockAccessList.A node running with the precompile-caching decorator active would compute a BAL hash that omits the precompile address, diverging from clients that record it. This becomes a consensus risk if/when EIP-7928 is consensus-active.
Suggested coverage
State / blockchain test (one variant per opcode):
DELEGATECALL <precompile_addr> <gas>; sender EOA with funds.<precompile_addr>(read-only - no balance / nonce / storage changes), in addition to the contract address.A second variant for
CALLCODE(or justCALLwithtarget != codeSourceconstructed differently) would close the related opcode.References
Nethermind.Evm.Test/Eip7928Tests.cs::DelegateCall_to_precompile_records_codeSource_in_BAL_under_PrecompileCachedCodeInfoRepository- in NethermindEth/nethermind#11547.f3c0b2706ein the same PR.A second test in that PR (
Direct_transaction_to_precompile_records_recipient_in_BAL_*) verified thattx.to == precompileis already incidentally recorded by an existingTransactionProcessor.Executepath - so that scenario does NOT need additional EEST coverage for the same defect class.