mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-30 10:17:39 +00:00
core: implement EIP-8246 (#35219)
EIP: https://eips.ethereum.org/EIPS/eip-8246 Supersedes #35218
This commit is contained in:
parent
9700b5b10e
commit
68671a4530
8 changed files with 278 additions and 109 deletions
|
|
@ -946,6 +946,62 @@ func TestBALSelfDestructPreExistingContract(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestBALSelfDestructToSelfKeepsBalance: under EIP-8246 a freshly created
|
||||
// contract that self-destructs to itself keeps its balance (it is not burnt and
|
||||
// the account is not removed). The surviving balance-only account must therefore
|
||||
// be recorded in the BAL with its preserved balance.
|
||||
func TestBALSelfDestructToSelfKeepsBalance(t *testing.T) {
|
||||
env := newBALTestEnv(nil)
|
||||
// Init code: ADDRESS SELFDESTRUCT — the contract self-destructs to itself
|
||||
// during its own creation transaction (satisfying EIP-6780's same-tx rule).
|
||||
// ADDRESS (0x30) ; SELFDESTRUCT (0xff)
|
||||
init := []byte{0x30, 0xff}
|
||||
|
||||
b, receipts := env.run(t, func(g *BlockGen) {
|
||||
g.AddTx(env.tx(0, nil, big.NewInt(100), 1_000_000, 0, init))
|
||||
})
|
||||
|
||||
created := receipts[0].ContractAddress
|
||||
cc := assertPresent(t, b, created)
|
||||
// EIP-8246: balance preserved (not burnt), account survives -> the BAL must
|
||||
// record the created address with its retained balance.
|
||||
if len(cc.BalanceChanges) != 1 || cc.BalanceChanges[0].PostBalance.Uint64() != 100 {
|
||||
t.Fatalf("self-destruct-to-self must preserve balance 100 in the BAL: %+v", cc.BalanceChanges)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBALSelfDestructToSelfPrefundedUnchanged: a pre-funded address onto which a
|
||||
// contract is deployed and which self-destructs to itself in the same
|
||||
// transaction. Under EIP-8246 the account survives with its balance unchanged,
|
||||
// so the BAL must list it only as an access (no balance/nonce/code change).
|
||||
func TestBALSelfDestructToSelfPrefundedUnchanged(t *testing.T) {
|
||||
// The contract address created by the sender's nonce-0 transaction; it is
|
||||
// pre-funded in genesis (balance only: nonce 0, no code, no storage), which
|
||||
// EIP-7610 permits as a deployment target.
|
||||
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
created := crypto.CreateAddress(crypto.PubkeyToAddress(key.PublicKey), 0)
|
||||
|
||||
env := newBALTestEnv(types.GenesisAlloc{
|
||||
created: {Balance: big.NewInt(77)},
|
||||
})
|
||||
// Init code: ADDRESS SELFDESTRUCT, deployed with zero value so the balance is
|
||||
// untouched (stays at the pre-funded 77).
|
||||
init := []byte{0x30, 0xff}
|
||||
|
||||
b, receipts := env.run(t, func(g *BlockGen) {
|
||||
g.AddTx(env.tx(0, nil, big.NewInt(0), 1_000_000, 0, init))
|
||||
})
|
||||
|
||||
if receipts[0].ContractAddress != created {
|
||||
t.Fatalf("unexpected created address: have %x want %x", receipts[0].ContractAddress, created)
|
||||
}
|
||||
aa := assertPresent(t, b, created)
|
||||
// EIP-8246: balance preserved and equal to the pre-transaction value, so no
|
||||
// balance change; nonce and code end where they started (0 / empty). The
|
||||
// account is only read, with an empty change set.
|
||||
assertEmpty(t, aa)
|
||||
}
|
||||
|
||||
// ============================== Mid-tx balance round-trip ==============================
|
||||
|
||||
// TestBALMidTxBalanceRoundTrip: when an address's balance changes during a
|
||||
|
|
|
|||
105
core/eip8246_test.go
Normal file
105
core/eip8246_test.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// TestEIP8246SelfdestructNoBurn verifies that, once EIP-8246 is active
|
||||
// (Amsterdam), a contract that is created and self-destructs to itself within
|
||||
// the same transaction keeps its balance instead of burning it: the account
|
||||
// survives as a balance-only account (no code, zero nonce, balance preserved).
|
||||
//
|
||||
// https://eips.ethereum.org/EIPS/eip-8246
|
||||
func TestEIP8246SelfdestructNoBurn(t *testing.T) {
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
config = *params.MergedTestChainConfig
|
||||
signer = types.LatestSigner(&config)
|
||||
engine = beacon.New(ethash.NewFaker())
|
||||
value = big.NewInt(1_000_000)
|
||||
// Init code: ADDRESS (0x30) ; SELFDESTRUCT (0xff). The created contract
|
||||
// self-destructs to itself during its own creation transaction.
|
||||
initcode = common.FromHex("30ff")
|
||||
)
|
||||
// TODO: drop this hacky Amsterdam config initialization once the final
|
||||
// Amsterdam config is available (mirrors TestEthTransferLogs).
|
||||
config.AmsterdamTime = new(uint64)
|
||||
blobConfig := *config.BlobScheduleConfig
|
||||
blobConfig.Amsterdam = blobConfig.Osaka
|
||||
config.BlobScheduleConfig = &blobConfig
|
||||
|
||||
gspec := &Genesis{
|
||||
Config: &config,
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {Balance: newGwei(1_000_000_000)},
|
||||
},
|
||||
}
|
||||
// The contract created by addr1's first (nonce 0) transaction.
|
||||
created := crypto.CreateAddress(addr1, 0)
|
||||
|
||||
db, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||
tx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{
|
||||
ChainID: gspec.Config.ChainID,
|
||||
Nonce: 0,
|
||||
To: nil, // contract creation
|
||||
Gas: 1_000_000,
|
||||
GasFeeCap: newGwei(5),
|
||||
GasTipCap: newGwei(5),
|
||||
Value: value,
|
||||
Data: initcode,
|
||||
})
|
||||
b.AddTx(tx)
|
||||
})
|
||||
|
||||
chain, err := NewBlockChain(db, gspec, engine, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create chain: %v", err)
|
||||
}
|
||||
defer chain.Stop()
|
||||
// Read the post-state of the generated block directly. InsertChain is avoided
|
||||
// on purpose: it would additionally verify the EIP-7928 block access list,
|
||||
// which the chain-generation harness on this branch does not yet populate
|
||||
// consistently — an orthogonal concern to the EIP-8246 state semantics under
|
||||
// test here.
|
||||
state, err := chain.StateAt(blocks[0].Header())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to obtain block state: %v", err)
|
||||
}
|
||||
// EIP-8246: the self-destructed, freshly-created contract keeps its balance
|
||||
// rather than burning it, so the account survives.
|
||||
if got := state.GetBalance(created); got.ToBig().Cmp(value) != 0 {
|
||||
t.Errorf("created account balance = %v, want %v (EIP-8246: balance must be preserved, not burned)", got, value)
|
||||
}
|
||||
// It survives as a balance-only account: nonce reset to 0 and no code.
|
||||
if got := state.GetNonce(created); got != 0 {
|
||||
t.Errorf("created account nonce = %d, want 0", got)
|
||||
}
|
||||
if got := state.GetCodeSize(created); got != 0 {
|
||||
t.Errorf("created account code size = %d, want 0 (code must be cleared)", got)
|
||||
}
|
||||
}
|
||||
|
|
@ -764,50 +764,15 @@ func (s *StateDB) GetRefund() uint64 {
|
|||
return s.refund
|
||||
}
|
||||
|
||||
type removedAccountWithBalance struct {
|
||||
address common.Address
|
||||
balance *uint256.Int
|
||||
}
|
||||
|
||||
// LogsForBurnAccounts returns the eth burn logs for accounts scheduled for
|
||||
// removal which still have positive balance. The purpose of this function is
|
||||
// to handle a corner case of EIP-7708 where a self-destructed account might
|
||||
// still receive funds between sending/burning its previous balance and actual
|
||||
// removal. In this case the burning of these remaining balances still need to
|
||||
// be logged.
|
||||
// Specification EIP-7708: https://eips.ethereum.org/EIPS/eip-7708
|
||||
//
|
||||
// This function should only be invoked at the transaction boundary, specifically
|
||||
// before the Finalise.
|
||||
func (s *StateDB) LogsForBurnAccounts() []*types.Log {
|
||||
var list []removedAccountWithBalance
|
||||
for addr := range s.journal.mutations {
|
||||
if obj, exist := s.stateObjects[addr]; exist && obj.selfDestructed && !obj.Balance().IsZero() {
|
||||
list = append(list, removedAccountWithBalance{
|
||||
address: obj.address,
|
||||
balance: obj.Balance(),
|
||||
})
|
||||
}
|
||||
}
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].address.Cmp(list[j].address) < 0
|
||||
})
|
||||
logs := make([]*types.Log, len(list))
|
||||
for i, acct := range list {
|
||||
logs[i] = types.EthBurnLog(acct.address, acct.balance)
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
// Finalise finalises the state by removing the destructed objects and clears
|
||||
// the journal as well as the refunds. Finalise, however, will not push any updates
|
||||
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
||||
func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccessList {
|
||||
if s.stateAccessList != nil {
|
||||
return s.finaliseAmsterdam(deleteEmptyObjects)
|
||||
}
|
||||
addressesToPrefetch := make([]common.Address, 0, len(s.journal.mutations))
|
||||
for addr, state := range s.journal.mutations {
|
||||
for addr := range s.journal.mutations {
|
||||
obj, exist := s.stateObjects[addr]
|
||||
if !exist {
|
||||
// RIPEMD160 (0x03) gets an extra dirty marker for a historical
|
||||
|
|
@ -831,46 +796,103 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccess
|
|||
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
||||
s.stateObjectsDestruct[obj.address] = obj
|
||||
}
|
||||
// Aggregate the account mutation into the block-level accessList
|
||||
// if Amsterdam has been activated.
|
||||
if s.stateAccessList != nil {
|
||||
// Notably, if the account is deleted during the transaction,
|
||||
// its pre-transaction nonce, code, and storage must be empty.
|
||||
//
|
||||
// EIP-6780 restricts self-destruct to contracts deployed within
|
||||
// the same transaction, while EIP-7610 rejects deployments to
|
||||
// destinations with non-empty storage, non-zero nonce and non-empty
|
||||
// code.
|
||||
//
|
||||
// Therefore, when an account is deleted, its pre-transaction nonce
|
||||
// code and storage is guaranteed to be empty, leaving nothing to
|
||||
// clean up here.
|
||||
balance := uint256.NewInt(0)
|
||||
if state.balanceSet && balance.Cmp(state.balance) != 0 {
|
||||
s.stateAccessList.BalanceChange(s.blockAccessIndex, addr, balance)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Aggregate the account mutation into the block-level accessList
|
||||
// if Amsterdam has been activated.
|
||||
if s.stateAccessList != nil {
|
||||
balance := obj.Balance()
|
||||
if state.balanceSet && balance.Cmp(state.balance) != 0 {
|
||||
s.stateAccessList.BalanceChange(s.blockAccessIndex, addr, balance)
|
||||
}
|
||||
nonce := obj.Nonce()
|
||||
if state.nonceSet && nonce != state.nonce {
|
||||
s.stateAccessList.NonceChange(addr, s.blockAccessIndex, nonce)
|
||||
}
|
||||
if state.codeSet {
|
||||
if code := obj.Code(); !bytes.Equal(code, state.code) {
|
||||
s.stateAccessList.CodeChange(addr, s.blockAccessIndex, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.finalise()
|
||||
s.markUpdate(addr)
|
||||
}
|
||||
addressesToPrefetch = append(addressesToPrefetch, addr) // Copy needed for closure
|
||||
}
|
||||
if s.prefetcher != nil && len(addressesToPrefetch) > 0 {
|
||||
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch, nil, false); err != nil {
|
||||
log.Error("Failed to prefetch addresses", "addresses", len(addressesToPrefetch), "err", err)
|
||||
}
|
||||
}
|
||||
// Invalidate journal because reverting across transactions is not allowed.
|
||||
s.clearJournalAndRefund()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StateDB) recordAccessListChanges(addr common.Address, state *journalMutationState) {
|
||||
var (
|
||||
balance = uint256.NewInt(0)
|
||||
nonce uint64
|
||||
)
|
||||
obj := s.stateObjects[addr] // nil when the account was removed
|
||||
if obj != nil {
|
||||
balance, nonce = obj.Balance(), obj.Nonce()
|
||||
}
|
||||
if state.balanceSet && balance.Cmp(state.balance) != 0 {
|
||||
s.stateAccessList.BalanceChange(s.blockAccessIndex, addr, balance)
|
||||
}
|
||||
if state.nonceSet && nonce != state.nonce {
|
||||
s.stateAccessList.NonceChange(addr, s.blockAccessIndex, nonce)
|
||||
}
|
||||
if state.codeSet {
|
||||
var code []byte
|
||||
if obj != nil {
|
||||
code = obj.Code()
|
||||
}
|
||||
if !bytes.Equal(code, state.code) {
|
||||
s.stateAccessList.CodeChange(addr, s.blockAccessIndex, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finaliseAmsterdam is the Amsterdam-and-later variant of Finalise.
|
||||
func (s *StateDB) finaliseAmsterdam(deleteEmptyObjects bool) *bal.ConstructionBlockAccessList {
|
||||
addressesToPrefetch := make([]common.Address, 0, len(s.journal.mutations))
|
||||
for addr, state := range s.journal.mutations {
|
||||
obj, exist := s.stateObjects[addr]
|
||||
if !exist {
|
||||
// RIPEMD160 (0x03) gets an extra dirty marker for a historical
|
||||
// mainnet consensus exception (at block 1714175, in tx
|
||||
// 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2)
|
||||
// around empty-account touch/revert handling.
|
||||
//
|
||||
// That marker survives journal revert, so the account may remain in
|
||||
// s.journal.mutations even though its state object was rolled
|
||||
// back and no longer exists. In that case there is nothing to
|
||||
// finalise or delete, so ignore it here.
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case obj.selfDestructed:
|
||||
// EIP-8264: accounts marked for self-destruction, instead of
|
||||
// being deleted, are modified as follows:
|
||||
// - nonce is reset to 0,
|
||||
// - balance is unchanged,
|
||||
// - code is cleared,
|
||||
// - all storage is cleared
|
||||
if !obj.Balance().IsZero() {
|
||||
o := newObject(s, obj.address, obj.origin)
|
||||
o.setBalance(new(uint256.Int).Set(obj.Balance()))
|
||||
s.setStateObject(o)
|
||||
s.markUpdate(addr)
|
||||
} else {
|
||||
delete(s.stateObjects, obj.address)
|
||||
s.markDelete(addr)
|
||||
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
||||
s.stateObjectsDestruct[obj.address] = obj
|
||||
}
|
||||
}
|
||||
|
||||
case deleteEmptyObjects && obj.empty():
|
||||
// EIP-161: a touched, empty account is removed.
|
||||
delete(s.stateObjects, obj.address)
|
||||
s.markDelete(addr)
|
||||
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
||||
s.stateObjectsDestruct[obj.address] = obj
|
||||
}
|
||||
|
||||
default:
|
||||
obj.finalise()
|
||||
s.markUpdate(addr)
|
||||
}
|
||||
// Aggregate the resulting account metadata change
|
||||
// into the block-level access list.
|
||||
s.recordAccessListChanges(addr, state)
|
||||
|
||||
// At this point, also ship the address off to the precacher. The precacher
|
||||
// will start loading tries, and when the change is eventually committed,
|
||||
// the commit-phase will be a lot faster
|
||||
|
|
|
|||
|
|
@ -230,10 +230,6 @@ func (s *hookedStateDB) AddLog(log *types.Log) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) LogsForBurnAccounts() []*types.Log {
|
||||
return s.inner.LogsForBurnAccounts()
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccessList {
|
||||
if s.hooks.OnBalanceChange == nil && s.hooks.OnNonceChangeV2 == nil && s.hooks.OnNonceChange == nil && s.hooks.OnCodeChangeV2 == nil && s.hooks.OnCodeChange == nil {
|
||||
// Short circuit if no relevant hooks are set.
|
||||
|
|
@ -256,18 +252,24 @@ func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlock
|
|||
return bytes.Compare(selfDestructedAddrs[i][:], selfDestructedAddrs[j][:]) < 0
|
||||
})
|
||||
|
||||
// EIP-8246 (Amsterdam) removes the SELFDESTRUCT burn: a self-destructed
|
||||
// account that retains a non-zero balance is preserved as a balance-only
|
||||
// account rather than removed, so its balance is no longer burnt.
|
||||
burnsBalance := s.inner.stateAccessList == nil
|
||||
|
||||
for _, addr := range selfDestructedAddrs {
|
||||
obj := s.inner.stateObjects[addr]
|
||||
// Bingo: state object was self-destructed, call relevant hooks.
|
||||
|
||||
// If ether was sent to account post-selfdestruct, record as burnt.
|
||||
if s.hooks.OnBalanceChange != nil {
|
||||
if burnsBalance && s.hooks.OnBalanceChange != nil {
|
||||
if bal := obj.Balance(); bal.Sign() != 0 {
|
||||
s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
|
||||
}
|
||||
}
|
||||
|
||||
// Nonce is set to reset on self-destruct.
|
||||
//
|
||||
// TODO(rjl) shall we emit the nonce change if the pre-tx nonce was zero?
|
||||
if s.hooks.OnNonceChangeV2 != nil {
|
||||
s.hooks.OnNonceChangeV2(addr, obj.Nonce(), 0, tracing.NonceChangeSelfdestruct)
|
||||
} else if s.hooks.OnNonceChange != nil {
|
||||
|
|
|
|||
|
|
@ -790,12 +790,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// EIP-7708: Emit the ETH-burn logs
|
||||
if rules.IsAmsterdam {
|
||||
for _, log := range st.evm.StateDB.LogsForBurnAccounts() {
|
||||
st.evm.StateDB.AddLog(log)
|
||||
}
|
||||
}
|
||||
return &ExecutionResult{
|
||||
UsedGas: gasUsed,
|
||||
MaxUsedGas: peakUsed,
|
||||
|
|
|
|||
|
|
@ -79,17 +79,3 @@ func EthTransferLog(from, to common.Address, amount *uint256.Int) *Log {
|
|||
Data: amount32[:],
|
||||
}
|
||||
}
|
||||
|
||||
// EthBurnLog creates an ETH burn log according to EIP-7708.
|
||||
// Specification: https://eips.ethereum.org/EIPS/eip-7708
|
||||
func EthBurnLog(from common.Address, amount *uint256.Int) *Log {
|
||||
amount32 := amount.Bytes32()
|
||||
return &Log{
|
||||
Address: params.SystemAddress,
|
||||
Topics: []common.Hash{
|
||||
params.EthBurnLogEvent,
|
||||
common.BytesToHash(from.Bytes()),
|
||||
},
|
||||
Data: amount32[:],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -928,8 +928,15 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
|
|||
if newContract {
|
||||
if this != beneficiary { // Skip no-op transfer when self-destructing to self.
|
||||
evm.StateDB.AddBalance(beneficiary, balance, tracing.BalanceIncreaseSelfdestruct)
|
||||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
} else if !evm.chainRules.IsAmsterdam {
|
||||
// Self-destructing to self burns the balance prior to EIP-8246.
|
||||
// EIP-8246 (Amsterdam) removes this burn: the balance is left
|
||||
// untouched and the account is preserved as a balance-only account
|
||||
// at transaction finalization (unless its balance is zero, in which
|
||||
// case EIP-161 deletes it).
|
||||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
}
|
||||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
evm.StateDB.SelfDestruct(this)
|
||||
}
|
||||
|
||||
|
|
@ -938,12 +945,10 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
|
|||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
evm.StateDB.AddBalance(beneficiary, balance, tracing.BalanceIncreaseSelfdestruct)
|
||||
}
|
||||
if evm.chainRules.IsAmsterdam && !balance.IsZero() {
|
||||
if this != beneficiary {
|
||||
evm.StateDB.AddLog(types.EthTransferLog(this, beneficiary, balance))
|
||||
} else if newContract {
|
||||
evm.StateDB.AddLog(types.EthBurnLog(this, balance))
|
||||
}
|
||||
// EIP-7708: emit a transfer log for the moved balance. EIP-8246 removes the
|
||||
// SELFDESTRUCT burn entirely, so there is no longer a burn to log.
|
||||
if evm.chainRules.IsAmsterdam && !balance.IsZero() && this != beneficiary {
|
||||
evm.StateDB.AddLog(types.EthTransferLog(this, beneficiary, balance))
|
||||
}
|
||||
|
||||
if tracer := evm.Config.Tracer; tracer != nil {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ type StateDB interface {
|
|||
Snapshot() int
|
||||
|
||||
AddLog(*types.Log)
|
||||
LogsForBurnAccounts() []*types.Log
|
||||
AddPreimage(common.Hash, []byte)
|
||||
|
||||
Witness() *stateless.Witness
|
||||
|
|
|
|||
Loading…
Reference in a new issue