Skip to content

EIP-7928 BAL: missing test coverage for direct DELEGATECALL/CALLCODE to a precompile (bal-devnet-7) #2830

@benaadams

Description

@benaadams

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):

  1. Pre-state: contract whose runtime code does DELEGATECALL <precompile_addr> <gas>; sender EOA with funds.
  2. Tx: sender invokes the contract.
  3. 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions