diff --git a/core/parallel_state_processor.go b/core/parallel_state_processor.go index ad0fb4a04e..08856d50dd 100644 --- a/core/parallel_state_processor.go +++ b/core/parallel_state_processor.go @@ -182,7 +182,7 @@ func (task *ExecutionTask) Settle() { coinbaseBalance := task.finalStateDB.GetBalance(task.coinbase) - task.finalStateDB.ApplyMVWriteSet(task.statedb.MVFullWriteList()) + task.finalStateDB.ApplyMVWriteSet(task.statedb.MVWriteList()) for _, l := range task.statedb.GetLogs(task.tx.Hash(), task.blockNumber.Uint64(), task.blockHash, task.blockTime) { task.finalStateDB.AddLog(l) diff --git a/core/state/journal.go b/core/state/journal.go index a1cc291493..55dd50b18c 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -23,6 +23,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/blockstm" "github.com/ethereum/go-ethereum/crypto" "github.com/holiman/uint256" ) @@ -323,6 +324,7 @@ func (ch selfDestructChange) revert(s *StateDB) { obj := s.getStateObject(ch.account) if obj != nil { obj.selfDestructed = false + RevertWrite(s, blockstm.NewSubpathKey(ch.account, SuicidePath)) } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 5f5c3c5c5f..607a479199 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -421,7 +421,7 @@ func (s *StateDB) ApplyMVWriteSet(writes []blockstm.WriteDescriptor) { s.SetCode(addr, sr.GetCode(addr)) case SuicidePath: stateObject := s.getStateObject(addr) - if stateObject != nil { + if stateObject != nil && sr.HasSelfDestructed(addr) { s.SelfDestruct(addr) } default: diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 614dafcef8..4d05c4b27f 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -2025,3 +2025,40 @@ func TestShouldDeleteSmartContractIfItExistsInState(t *testing.T) { codeAfterDeletion := s.GetCode(addr) assert.Equal(t, []byte(nil), codeAfterDeletion, "smart contract should be deleted") } + +// containsKey returns true if the provided write descriptor list contains the given key. +func containsKey(writes []blockstm.WriteDescriptor, key blockstm.Key) bool { + for _, w := range writes { + if w.Path == key { + return true + } + } + return false +} + +// Test that selfdestruct writes (suicide and balance) are excluded after revert. +func TestRevertWriteSelfDestruct(t *testing.T) { + t.Parallel() + + db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil) + mvhm := blockstm.MakeMVHashMap() + s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm) + + addr := common.HexToAddress("0x06") + s.CreateAccount(addr) + s.SetBalance(addr, uint256.NewInt(100), tracing.BalanceChangeTransfer) + // Clear writes so only selfdestruct writes are tracked + s.ClearWriteMap() + + snap := s.Snapshot() + s.SelfDestruct(addr) + + keySuicide := blockstm.NewSubpathKey(addr, SuicidePath) + assert.True(t, containsKey(s.MVFullWriteList(), keySuicide)) + assert.True(t, containsKey(s.MVWriteList(), keySuicide)) + + s.RevertToSnapshot(snap) + // Full list still contains both, filtered excludes both + assert.True(t, containsKey(s.MVFullWriteList(), keySuicide)) + assert.False(t, containsKey(s.MVWriteList(), keySuicide)) +}