Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 45 additions & 39 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,44 +764,6 @@ 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.
Expand All @@ -821,7 +783,40 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccess
// finalise or delete, so ignore it here.
continue
}
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
// EIP-8246: clear code/storage/nonce, preserve balance.
if obj.selfDestructed && s.stateAccessList != nil {
clearSelfdestructAccount(obj)

if deleteEmptyObjects && obj.empty() {
// Cleanup left account empty; delete per EIP-161.
delete(s.stateObjects, obj.address)
s.markDelete(addr)
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
s.stateObjectsDestruct[obj.address] = obj
}
balance := uint256.NewInt(0)
if state.balanceSet && balance.Cmp(state.balance) != 0 {
s.stateAccessList.BalanceChange(s.blockAccessIndex, addr, balance)
}
} else {
// Keep as balance-only account.
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)
}
} else if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
delete(s.stateObjects, obj.address)
s.markDelete(addr)

Expand Down Expand Up @@ -887,6 +882,17 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccess
return s.stateAccessList
}

// clearSelfdestructAccount clears code, storage, and nonce for an EIP-8246
// selfdestructed account while preserving the balance.
func clearSelfdestructAccount(obj *stateObject) {
obj.data.CodeHash = types.EmptyCodeHash.Bytes()
obj.dirtyCode = true
obj.dirtyStorage = make(Storage)
obj.pendingStorage = make(Storage)
obj.originStorage = make(Storage)
obj.data.Nonce = 0
}

// IntermediateRoot computes the current root hash of the state trie.
// It is called in between transactions to get the root hash that
// goes into transaction receipts.
Expand Down
7 changes: 2 additions & 5 deletions core/state/statedb_hooked.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -261,7 +257,8 @@ func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlock
// 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 {
// EIP-8246: balance is preserved, skip the burn trace.
if s.hooks.OnBalanceChange != nil && s.inner.stateAccessList == nil {
if bal := obj.Balance(); bal.Sign() != 0 {
s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
}
Expand Down
6 changes: 0 additions & 6 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,12 +744,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,
Expand Down
8 changes: 5 additions & 3 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,11 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
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)
// EIP-8246: if the beneficiary is the executing account itself and the fork
// is active, the balance remains unchanged instead of being burned.
if !evm.chainRules.IsAmsterdam || this != beneficiary {
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
}
evm.StateDB.SelfDestruct(this)
}

Expand All @@ -928,8 +932,6 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
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))
}
}

Expand Down
1 change: 0 additions & 1 deletion core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ type StateDB interface {
Snapshot() int

AddLog(*types.Log)
LogsForBurnAccounts() []*types.Log
AddPreimage(common.Hash, []byte)

Witness() *stateless.Witness
Expand Down
2 changes: 1 addition & 1 deletion p2p/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func TestProtocolHandshakeErrors(t *testing.T) {
p1, p2 := MsgPipe()
go Send(p1, test.code, test.msg)
_, err := readProtocolHandshake(p2)
if !reflect.DeepEqual(err, test.err) {
if err.Error() != test.err.Error() {
t.Errorf("test %d: error mismatch: got %q, want %q", i, err, test.err)
}
}
Expand Down
Loading