Skip to content
Open
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
2 changes: 1 addition & 1 deletion core/parallel_state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -293,6 +294,7 @@ type (

func (ch createObjectChange) revert(s *StateDB) {
delete(s.stateObjects, ch.account)
RevertWrite(s, blockstm.NewAddressKey(ch.account))
}

func (ch createObjectChange) dirtied() *common.Address {
Expand Down Expand Up @@ -323,6 +325,8 @@ func (ch selfDestructChange) revert(s *StateDB) {
obj := s.getStateObject(ch.account)
if obj != nil {
obj.selfDestructed = false
RevertWrite(s, blockstm.NewSubpathKey(ch.account, SuicidePath))
RevertWrite(s, blockstm.NewSubpathKey(ch.account, BalancePath))
}
}

Expand Down Expand Up @@ -353,6 +357,7 @@ func (ch touchChange) copy() journalEntry {

func (ch balanceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setBalance(ch.prev)
RevertWrite(s, blockstm.NewSubpathKey(ch.account, BalancePath))
}

func (ch balanceChange) dirtied() *common.Address {
Expand All @@ -368,6 +373,7 @@ func (ch balanceChange) copy() journalEntry {

func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setNonce(ch.prev)
RevertWrite(s, blockstm.NewSubpathKey(ch.account, NoncePath))
}

func (ch nonceChange) dirtied() *common.Address {
Expand All @@ -383,6 +389,7 @@ func (ch nonceChange) copy() journalEntry {

func (ch codeChange) revert(s *StateDB) {
s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode)
RevertWrite(s, blockstm.NewSubpathKey(ch.account, CodePath))
}

func (ch codeChange) dirtied() *common.Address {
Expand All @@ -398,6 +405,7 @@ func (ch codeChange) copy() journalEntry {

func (ch storageChange) revert(s *StateDB) {
s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
RevertWrite(s, blockstm.NewStateKey(ch.account, ch.key))
}

func (ch storageChange) dirtied() *common.Address {
Expand Down
165 changes: 165 additions & 0 deletions core/state/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,171 @@ func TestCopyCommitCopy(t *testing.T) {
}
}

// 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 create-object write is excluded from MVWriteList after revert via RevertWrite.
func TestRevertWrite_CreateObject(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("0x01")

snap := s.Snapshot()
s.CreateAccount(addr)

key := blockstm.NewAddressKey(addr)
// Sanity: before revert, both Full and Filtered lists should contain the key
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.True(t, containsKey(s.MVWriteList(), key))

// Revert and expect the filtered list to exclude it
s.RevertToSnapshot(snap)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.False(t, containsKey(s.MVWriteList(), key))
}

// Test that balance write is excluded from MVWriteList after revert.
func TestRevertWrite_BalanceChange(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("0x02")
s.CreateAccount(addr)
// Clear initial address write to focus test on balance key behavior
s.ClearWriteMap()

snap := s.Snapshot()
s.SetBalance(addr, uint256.NewInt(123), tracing.BalanceChangeTransfer)

key := blockstm.NewSubpathKey(addr, BalancePath)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.True(t, containsKey(s.MVWriteList(), key))

s.RevertToSnapshot(snap)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.False(t, containsKey(s.MVWriteList(), key))
}

// Test that nonce write is excluded from MVWriteList after revert.
func TestRevertWrite_NonceChange(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("0x03")
s.CreateAccount(addr)
s.ClearWriteMap()

snap := s.Snapshot()
s.SetNonce(addr, 7, tracing.NonceChangeUnspecified)

key := blockstm.NewSubpathKey(addr, NoncePath)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.True(t, containsKey(s.MVWriteList(), key))

s.RevertToSnapshot(snap)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.False(t, containsKey(s.MVWriteList(), key))
}

// Test that code write is excluded from MVWriteList after revert.
func TestRevertWrite_CodeChange(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("0x04")
s.CreateAccount(addr)
s.ClearWriteMap()

snap := s.Snapshot()
s.SetCode(addr, []byte{1, 2, 3})

key := blockstm.NewSubpathKey(addr, CodePath)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.True(t, containsKey(s.MVWriteList(), key))

s.RevertToSnapshot(snap)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.False(t, containsKey(s.MVWriteList(), key))
}

// Test that storage write is excluded from MVWriteList after revert.
func TestRevertWrite_StorageChange(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("0x05")
s.CreateAccount(addr)
s.ClearWriteMap()

snap := s.Snapshot()
k := common.HexToHash("0x99")
v := common.HexToHash("0xaa")
s.SetState(addr, k, v)

key := blockstm.NewStateKey(addr, k)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.True(t, containsKey(s.MVWriteList(), key))

s.RevertToSnapshot(snap)
assert.True(t, containsKey(s.MVFullWriteList(), key))
assert.False(t, containsKey(s.MVWriteList(), key))
}

// Test that selfdestruct writes (suicide and balance) are excluded after revert.
func TestRevertWrite_SelfDestruct(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)
keyBalance := blockstm.NewSubpathKey(addr, BalancePath)
assert.True(t, containsKey(s.MVFullWriteList(), keySuicide))
assert.True(t, containsKey(s.MVFullWriteList(), keyBalance))
assert.True(t, containsKey(s.MVWriteList(), keySuicide))
assert.True(t, containsKey(s.MVWriteList(), keyBalance))

s.RevertToSnapshot(snap)
// Full list still contains both, filtered excludes both
assert.True(t, containsKey(s.MVFullWriteList(), keySuicide))
assert.True(t, containsKey(s.MVFullWriteList(), keyBalance))
assert.False(t, containsKey(s.MVWriteList(), keySuicide))
assert.False(t, containsKey(s.MVWriteList(), keyBalance))
}

// Tests a regression where committing a copy lost some internal meta information,
// leading to corrupted subsequent copies.
//
Expand Down
Loading