EIP-8246 (remove SELFDESTRUCT burn) - #2512
brett-monad wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Reviewed the EIP-8246 implementation covering State::selfdestruct, State::destruct_suicides, the trait flag, the state-diff tracer comment, and the coinbase-award comment in execute_transaction.cpp.
The selfdestruct reshuffle is correct: the new eip_8246_active arm collapses to a no-op when address == beneficiary (no burn) and to a transfer otherwise, and the pre-Cancun and Cancun-only arms are unchanged. The destruct_suicides refactor preserves prior semantics for < Cancun (unconditional reset), for post-Cancun with mismatched incarnation (skip), and for post-Cancun with matching incarnation under !eip_8246_active (reset). The new preservation path only fires when the account was created this tx, has non-zero balance, and mutates in place so that the incarnation drives the reincarnation flag through commit — I verified Account::operator== includes incarnation and that create_contract stamps it. The storage-zeroing iterates the original map, which is the correct superset given set_storage/get_storage both populate the original map before or in place of current. MONAD_ASSERT(orig != original_.end()) holds because the only path that inserts into current_ is current_account_state, which first goes through original_account_state. The destruct_touched_dead interaction is sound: a preserved account has balance > 0, so is_dead is false and the untemplated pass spares it. can_merge/relaxed-merge ordering (before execute_final) leaves the preservation unaffected. The tests are thorough — the preserved-storage, credit-after-destruct, touched-dead, merge-through-trie, state-root-parity, and same-block-recreation cases each pin a distinct invariant.
Verdict: CORRECT
🤖 Generated with Claude Code
4b05bfa to
f927f4e
Compare
There was a problem hiding this comment.
Pull request overview
Implements EIP-8246, removing SELFDESTRUCT balance burns and preserving funded accounts from Amsterdam onward.
Changes:
- Adds Amsterdam-gated EIP-8246 support.
- Updates transfer and finalization behavior.
- Expands state, storage, merge, and tracing tests.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Summary |
|---|---|
category/vm/evm/traits.hpp |
Adds the EIP-8246 activation predicate. |
category/execution/ethereum/trace/state_tracer.cpp |
Documents preserved-account tracing behavior. |
category/execution/ethereum/test/test_call_trace.cpp |
Verifies SELFDESTRUCT trace values. |
category/execution/ethereum/state3/state.cpp |
Implements non-burning and account-preservation logic. |
category/execution/ethereum/state2/test/test_state.cpp |
Tests preservation, deletion, storage clearing, and commits. |
category/execution/ethereum/execute_transaction.cpp |
Documents finalization ordering. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
f927f4e to
b89479a
Compare
ff1dfbe to
e1b9d1a
Compare
584bd9e to
ca8f8dc
Compare
d002b44 to
e2cf84d
Compare
e2cf84d to
37cb8c0
Compare
37cb8c0 to
2132967
Compare
2132967 to
c66d816
Compare
c66d816 to
fec7654
Compare
adefbab to
c699e12
Compare
c699e12 to
3dfc152
Compare
3dfc152 to
1648fa4
Compare
42ba207 to
af1e775
Compare
af1e775 to
33603d6
Compare
…n Amsterdam EIP-8246 removes the two paths by which SELFDESTRUCT still destroys ETH. It is a prerequisite of EIP-7708, whose current text requires it and no longer specifies a burn, so 8246 has to be live before 7708 or that EIP would claim complete native-value logging while two burn paths went unlogged. Rule 1: a same-transaction account destructing to itself keeps its balance. State::selfdestruct moves the balance whenever the beneficiary differs; for the destruct-to-self case one predicate, selfdestruct_to_self_burns, holds the revision rule: always before Cancun, only for a same-transaction account under EIP-6780, never under EIP-8246. Rule 2: at finalization a destructed account is preserved rather than deleted when it holds a balance -- nonce reset, code and storage cleared, balance untouched. destruct_suicides clears nonce and code first and then applies EIP-161 to the result, so an empty account is still deleted. Rule 2 also covers the EIP's second burn, value sent to an account already marked for selfdestruction, because it keys on the balance rather than on who the beneficiary was. Gated on a new eip_8246_active() predicate: pre-Amsterdam revisions must keep burning. Implementation notes. The preserved account is mutated in place so its incarnation survives: it is the storage-generation key the commit builders compare against the pre-block account to decide whether the old storage subtree is rebuilt, and a fresh Account would default it to a value that is itself a legal incarnation. zero_storage writes an explicit zero for every key in either storage map rather than erasing them: a missing key falls back to the original map or the block delta and could still read a pre-destruct value. The two key sets differ only on the RPC full-state-override path, where set_to_state_incarnation stamps the current incarnation onto a pre-existing contract. A preserved account survives destruct_touched_dead because holding a balance makes it not empty under EIP-161. BlockState::can_merge runs before execute_final, which is what stops relaxed merge from zeroing the balance and committing an empty account. The reserve-balance check runs at depth zero before finalization, so rule 2 is invisible to it and only rule 1 is, which removes a debit. Testing. The Amsterdam spec suite stays switched off here, as on the commits below it: the fixtures are generated with the whole fork active, so the suite opens only once every Amsterdam EIP is in the tree. The pinned bundle is generated with EIP-8246 in effect, so the selfdestruct fixtures outside amsterdam/ (cancun/eip6780_selfdestruct, tangerine_whistle eip150_selfdestruct, frontier/create, paris/security, monad_nine/mip4_checkreservebalance) expect the balance-only account that 8246 preserves, and pass once the suite is enabled. Until then the unit tests are the gate. Every assertion was checked by deleting the code it covers and confirming the test fails. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
33603d6 to
8f90b15
Compare
Removes the two paths by which
SELFDESTRUCTstill destroys ETH. This is a prerequisite of EIP-7708 rather than an adjacent EIP — 7708's current text listsrequires: 8246and no longer specifies a burn — which is what lets the 7708 implementation land with noBurnlog at all. That is a sequencing constraint rather than a code one: 8246 must be live before 7708 is, or 7708 would claim complete native-value logging while two burn paths went unlogged.Rule 1: a same-transaction account destructing to itself keeps its balance.
State::selfdestructmoves the balance whenever the beneficiary differs; for the destruct-to-self case one predicate,selfdestruct_to_self_burns, holds the revision rule: always before Cancun, only for a same-transaction account under EIP-6780, never under EIP-8246.Rule 2: at finalization a destructed account holding a balance is preserved — nonce reset, code and storage cleared, balance untouched — rather than deleted. Nonce and code are cleared first and EIP-161 is applied to the result, so an empty account is still deleted. Rule 2 also covers the EIP's second burn, value sent to an account already marked for selfdestruction, because it keys on the balance rather than on who the beneficiary was.
Gated on a new
eip_8246_active(), which is>= MONAD_ETH_AMSTERDAMin both trait families; pre-Amsterdam revisions must keep burning.Reserve balance is deliberately unchanged
A preserved account is a funded, nonce-0, codeless address, and no predicate distinguishes it from an ordinary pre-funded address — three
Accountfields match by construction, and the fourth,incarnation, is drawn from the same source for both and is absent from the merkle account. So it takes the existing MIP-4 floor exactly as a pre-funded address does today. Exempting it would mean making the storage-generation key carry account provenance, and that equivalence is the reason not to.Spec tests
No change to the Amsterdam exclusion list. The Amsterdam suite is disabled at
this commit and opens once every Amsterdam EIP is in the tree. Nothing needs
excluding: the pinned bundle is generated with 8246 in effect, and its nine
selfdestruct fixtures all pass against this tree. The unit tests are the gate
on this PR.
Testing
Full
MONAD_COMPILER_TESTING=ONbuild clean.ctestclean apart from the knownBacktraceTest.works. All spec-test jobs pass.clang-formatandlicense.shclean.Known limitation: the exec-event schema cannot express a code-cleared surviving account.
🤖 Generated with Claude Code
https://claude.ai/code/session_01T2YcdwU1mj3Lqunp9Y7pqa