Repository navigation
fix(eth/tracers): skip the EIP-8246 selfdestruct burn #35219, close #2832 - #2833
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
c110369 to
cd6b8a0
Compare
|
Review note after the latest push ( One clarification on scope: the most recent external round inspected Net change in this push:
Upstream still has nothing to port: go-ethereum's Checks on Merge order is unchanged: this still needs #2834 first or in the same batch. |
194468c to
e0f3542
Compare
568b653 to
f439b84
Compare
f439b84 to
43e7fc7
Compare
The supply live tracer accounted a selfdestruct-to-self as burned whenever the SELFDESTRUCT hook fired with from == to and a non-zero balance. That rule predates EIP-8246: from Amsterdam onwards the balance is preserved, so the tracer over-reports burn.misc on Amsterdam chains. The EIP-8246 state change does not reach this path: hookedStateDB.Finalise no longer emits BalanceDecreaseSelfdestructBurn, but this inference runs off the OnEnter event instead of a balance hook. Restore the chain-config plumbing upstream go-ethereum already has for this tracer - a chainConfig field filled by the OnBlockchainInit hook, which core.NewBlockChain invokes for every live tracer - and cache IsAmsterdam(block.Number) in OnBlockStart, so OnEnter only records the burn while the fork still burns the balance. A missing chain config keeps the previous behaviour rather than silently dropping the accounting, and is logged once so the fallback cannot go unnoticed. The gate is only correct together with the consensus side of EIP-8246: on a chain that schedules Amsterdam without it, the balance is still destroyed while the tracer stops reporting it. Ship both changes together. go-ethereum has the same latent bug: its EIP-8246 commit does not touch eth/tracers, and its selfdestruct supply test runs with params.TestChainConfig where Cancun and Amsterdam are unset, so there is no upstream fix to port. Tests: eth/tracers/live covers the pre-Amsterdam burn, Amsterdam active, the fork boundary in both directions, a reverted selfdestruct and the missing chain config; removing the gate makes the Amsterdam cases fail. The warning for a missing chain config is diagnostic only and deliberately not asserted, so the test stays independent of the log handler. core/blockchain_livetracer_test.go pins that core.NewBlockChain delivers the chain config through OnBlockchainInit; dropping that call makes it fail. Verified: make all and gofmt/goimports are clean, golangci-lint reports no new issues, and go test ./eth/tracers/... and ./core/... pass. Ref: ethereum#35219 Closes XinFinOrg#2832
43e7fc7 to
1021cfc
Compare
The supply tracer adds a BalanceDecreaseSelfdestructBurn reported by the state to burn.misc; that half of the accounting had no test in this tree, since the upstream supply tests (go-ethereum ethereum#29347) were never ported. Cover it at the hook boundary: the burn is accumulated, the other selfdestruct reasons are ignored, and the accounting does not depend on the fork because the state layer decides whether a balance is burnt. Ref: ethereum#35219
There was a problem hiding this comment.
🔵 Needs a closer look
Safe merging requires confirming the consensus prerequisite #2834 and coordinating with the overlapping alternative fix in #2870.
No open issues 🎉
What changed in this PR
Updates the supply live tracer to stop reporting selfdestruct-to-self balances as burnt after Amsterdam, aligning its accounting with EIP-8246 without changing consensus behavior.
Changes:
- Restores chain-configuration initialization and adds a per-block Amsterdam gate.
- Preserves pre-fork accounting when configuration is missing, with a one-time warning.
- Adds tests for fork boundaries, reverts, balance-hook accounting, and delivery of chain configuration.
| File | Description |
|---|---|
| eth/tracers/live/supply.go | Gates inferred selfdestruct burns on Amsterdam activation. |
| eth/tracers/live/supply_test.go | Tests fork-aware burn accounting and fallback behavior. |
| core/blockchain_livetracer_test.go | Verifies chain configuration reaches live tracers during initialization. |
Review effort: Balanced
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the enter event alone reports no burn, the balance hook reports the whole burn. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it; its enter-only assertion moves into the balance-change test.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. The same test asserts the final balances of the contract and of the other account, so the burn accounting cannot drift into moving the ether elsewhere. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. The same test asserts the final balances of the contract and of the other account, so the burn accounting cannot drift into moving the ether elsewhere. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. The same test asserts the final balances of the contract and of the other account, so the burn accounting cannot drift into moving the ether elsewhere. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. The same test asserts the final balances of the contract and of the other account, so the burn accounting cannot drift into moving the ether elsewhere. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. The same test asserts the final balances of the contract and of the other account, so the burn accounting cannot drift into moving the ether elsewhere. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from XinFinOrg#2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference.
…ate layer (#2870) * fix(core/state,eth/tracers): report the selfdestruct burn from the state layer The supply live tracer inferred a selfdestruct burn from the SELFDESTRUCT enter event: a self-destruct to self was counted as burnt whenever that event carried a non-zero balance. The inference is wrong from EIP-6780 (Cancun) onwards, where a pre-existing contract is not destructed at all and keeps its balance, and from EIP-8246 (Amsterdam) onwards, where the balance is preserved even for a contract created in the same transaction. The tracer therefore over-reported burn.misc on every chain that reached Cancun. The state knows how much a destruction really burns, so let it report that: the balance a destruct clears is remembered on the account and reported at finalisation, together with the ether that arrives in the destructed account afterwards. A reverted destruct drops that memory again, so nothing is reported for it. The supply tracer drops its own inference and accounts the burn from the BalanceDecreaseSelfdestructBurn event alone. For that accounting to hold, the balance a destruct clears has to be the balance it destroys. The EIP-6780 SELFDESTRUCT already moves the balance with an explicit decrease/increase pair before the account is destroyed, so it has nothing left to clear when the balance went to another account, and clears exactly the balance when the account appointed itself. The pre-Cancun implementation credited the beneficiary first and cleared the account afterwards instead, which left the state unable to tell a transfer from a burn; it now moves the balance the same way. The final balances are unchanged, only the tracing picture becomes truthful. That event now carries the whole burnt balance instead of only the ether that arrived in an already destructed account, so its documented meaning is extended accordingly. The only production consumer of the event is the supply tracer. Tests: core/selfdestruct_burn_test.go drives the cases through block processing for every fork: up to Cancun a self-destruct to itself burns the balance and one to another account burns nothing, a pre-existing contract burns nothing from Cancun onwards and EIP-8246 keeps the balance. The same test asserts the final balances of the contract and of the other account, so the burn accounting cannot drift into moving the ether elsewhere. core/state covers the reverted destruct, the preserved balance, the copied state and successive destructs of one account. eth/tracers/live covers the tracer side: the balance hook alone reports the burn, every other balance reason is ignored. dev-upgrade already carried the tracer-side fork gate from #2833, which kept that inference on IsAmsterdam(block) and pulled in the chain config to do so. With the burn reported by the state layer the gate has nothing left to decide, so it goes together with the inference and the chain-config plumbing behind it, along with the callstack wiring that only fed the removed inference. * chore(eth/tracers): add the missing license header to the supply tracer * docs(core): correct the pre-Byzantium finalisation comment
Summary
The
supplylive tracer (--vmtrace supply) counted a selfdestruct-to-self asburn.miscwhenever theSELFDESTRUCThook fired withfrom == toand a non-zero balance. EIP-8246 (Amsterdam) preserves that balance instead of burning it, so the tracer over-reported the burn on Amsterdam chains.The EIP-8246 state change does not cover this path:
hookedStateDB.Finaliseno longer emitsBalanceDecreaseSelfdestructBurn, but the tracer infers the burn from theOnEnterevent instead of a balance hook.Fix
chainConfigfield and theOnBlockchainInithook that upstream go-ethereum already has on this tracer.core/blockchain.goinvokesOnBlockchainInitfor every live tracer.IsAmsterdam(block.Number)inOnBlockStartand only recordcall.burnwhile the fork still burns the balance. XDC forks are block-based, so the block number alone decides.Tests
eth/tracers/live/supply_test.godrives the hooks through a transaction whose top-level call executesSELFDESTRUCTwith itself as beneficiary and checks the producedburn.misc:Removing the
s.burnsBalancegate makes the Amsterdam cases fail (reported burn = 100, want none).core/blockchain_livetracer_test.gopins thatcore.NewBlockChaindelivers the chain configuration throughOnBlockchainInit; dropping that call makes the test fail.Upstream
go-ethereum has the same latent bug: its EIP-8246 commit (ethereum#35219) does not touch
eth/tracers, and its selfdestruct supply test runs withparams.TestChainConfig, where Cancun and Amsterdam are unset. There is no upstream fix to port; this is a local fix that could be proposed upstream.Verification
make allpasses;gofmt/goimportsclean.golangci-lintreports no new issues in./eth/tracers/live/...and./core/....go test ./eth/tracers/... ./core/... -count=1passes.Merge order
The gate must not reach a chain that schedules Amsterdam before the consensus-side change does. Merge #2834 (EIP-8246 selfdestruct burn removal) first, or in the same batch. If this PR lands first, an Amsterdam-enabled dev/test chain running
--vmtrace supplyunder-reportsburn.miscfor selfdestruct-to-self until #2834 is in.Related
PR #2870 fixes the same over-reporting from the other end: it drops the
OnEnterinference entirely and lets the state layer report the destroyed balance. The two
changes overlap on
eth/tracers/live/supply.goand both addeth/tracers/live/supply_test.go, so merge only one of them:chainConfig/burnsBalancegate introduced here has noconsumer left and this PR should be closed instead of rebased;
supply.goandsupply_test.goand delete the gate as part of its own change.Impact
Observability only, no consensus change. Production XDC networks do not schedule Amsterdam; the affected set is an Amsterdam-enabled dev/test chain with
--vmtrace supplyenabled.Closes #2832