feat(tests): EIP-7928 self destruct to system address with 0 value#2893
Merged
fselmo merged 3 commits intoMay 22, 2026
Merged
Conversation
7 tasks
pull Bot
pushed a commit
to Dustin4444/erigon
that referenced
this pull request
May 22, 2026
…alue transfer (erigontech#21333) added a test for this to EEST ethereum/execution-specs#2893 --- ## Summary `makeSelfdestructGasFn` in `execution/vm/operations_acl.go` previously gated `MarkAddressAccess(beneficiary, false)` on the destructing contract having a non-zero balance, and on the zero-balance path additionally marked the gas-calc `Empty()` reads internal so the beneficiary would not appear in the BAL purely from that read. Per EIP-7928, "Beneficiary addresses for `SELFDESTRUCT`" are in scope for inclusion unconditionally — the SELFDESTRUCT is itself a state access on the beneficiary, independent of any value transfer. The pre-fix gate caused a BAL hash mismatch in the specific case where a SELFDESTRUCT targets `SYSTEM_ADDRESS` (`0xff…fffe`) with the destructing contract at zero balance: `versionedio.go::AsBlockAccessList`'s "drop SystemAddress unless it experienced state access itself" filter then dropped the entry because no non-revertable access mark was ever recorded, while other clients (which mark the beneficiary unconditionally) kept it. Result: BAL hash mismatch, block rejected, sync stall. ## Fix `execution/vm/operations_acl.go::makeSelfdestructGasFn`: record the beneficiary access unconditionally: ```go // Per EIP-7928, SELFDESTRUCT is a state access on the beneficiary // independently of any value transfer, so record it unconditionally. evm.IntraBlockState().MarkAddressAccess(address, false) ``` The pre-existing `SnapshotVersionedReadKeys` / `MarkNewReadsInternal` helpers in `execution/state/intra_block_state.go` were only used by the now-removed zero-balance branch and are dropped as dead code (−35 lines). `execution/state/versionedio.go::AsBlockAccessList`'s SystemAddress filter is left unchanged — its `&& !account.nonRevertableUserAccess` clause is now correctly satisfied by the unconditional mark above. ## Test coverage New file `execution/execmodule/bal_selfdestruct_systemaddress_test.go` adds three regression tests via `blockgen.GenerateChain` against `chain.AllProtocolChanges` (Amsterdam active at genesis): - `TestBALIncludesSystemAddressOnSelfdestructToItWithZeroBalance` — the bug scenario: CREATE tx with init code `PUSH20 SYSTEM_ADDRESS; SELFDESTRUCT` and zero contract balance. Asserts the resulting BAL contains a SystemAddress entry with every change-set empty. - `TestBALIncludesSystemAddressOnSelfdestructToItWithNonZeroBalance` — guard for the non-zero balance variant. - `TestBALIncludesOrdinaryBeneficiaryOnSelfdestructWithZeroBalance` — guard that ordinary EOA beneficiaries (where the SystemAddress filter doesn't apply) are unaffected. ## Test plan - [x] `go test -count=1 -run='TestBALIncludes' ./execution/execmodule/` — 3/3 pass. - [x] TDD verified: with the pre-fix `if !balance.IsZero()` guard restored, `TestBALIncludesSystemAddressOnSelfdestructToItWithZeroBalance` fails with "BAL must include a SystemAddress entry"; the other two still pass (confirming the bug is specific to SystemAddress + zero balance). - [x] `make lint` — 0 issues. - [x] `make eest-spec-statetests-devnet` — 65,202 / 65,202 tests pass. - [x] `make eest-spec-blocktests-devnet` — 82,941 / 82,941 tests pass (parallel exec). - [x] Fresh resync from block 0 against the BAL devnet through previously-divergent blocks: 0 BAL mismatches. - [x] EEST upstream coverage: matching test `test_bal_selfdestruct_to_system_address_zero_balance` opened as a PR against `ethereum/execution-specs devnets/bal/7` (link to follow). --------- Co-authored-by: Alex Sharov <AskAlexSharov@gmail.com>
4ec7f8a to
4c63727
Compare
Contributor
|
Thanks @taratorio. I pointed this to |
4c63727 to
59436f8
Compare
fselmo
approved these changes
May 22, 2026
fselmo
left a comment
Contributor
There was a problem hiding this comment.
lgtm, will wait for CI 👍🏼
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## forks/amsterdam #2893 +/- ##
================================================
Coverage 90.44% 90.44%
================================================
Files 535 535
Lines 32439 32439
Branches 3012 3012
================================================
Hits 29338 29338
Misses 2573 2573
Partials 528 528
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
test_bal_selfdestruct_to_system_address_zero_balancetotests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_eip4788.py.The test covers the intersection of two BAL rules that previously had no joint coverage:
SELFDESTRUCT" MUST appear in the BAL — regardless of value transfer.SYSTEM_ADDRESS(0xff...fffe) "MUST NOT be included unless it experiences state access itself" — and a SELFDESTRUCT to it is such an access, so the exception fires.Scenario
A single CREATE transaction whose init code is
Op.SELFDESTRUCT(SYSTEM_ADDRESS), with the new contract's balance still zero (the CREATE tx carries no value). Per EIP-6780 the contract is actually deleted because creation and destruction happen in the same transaction. The resulting BAL must contain theSYSTEM_ADDRESSentry with every change-set empty — the SELFDESTRUCT is the only access onSYSTEM_ADDRESSin this block.Motivation
This case was identified via a real-world client divergence on a Block Access List devnet — one client dropped the
SYSTEM_ADDRESSentry under this combination of conditions, producing a BAL hash mismatch with other clients. Existing BAL SELFDESTRUCT tests in this directory all use either a beacon-root / history-storage / withdrawal-queue / consolidation-queue beneficiary or an ordinary EOA — none of them targetSYSTEM_ADDRESSitself, so the carve-out's "unless it experiences state access" clause was never exercised end-to-end.Test plan
uv run fill tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_eip4788.py::test_bal_selfdestruct_to_system_address_zero_balance --fork Amsterdam— fills against EELS t8n.SYSTEM_ADDRESSentry).