Skip to content

Commit

Permalink
core/state: clear out cached state data when reset occurs (ethereum#2…
Browse files Browse the repository at this point in the history
…7376)

* core/state: remove cached snap data if reset occurs

* core/state: address comment from peter

* core/state: skip revert in case data is nil
  • Loading branch information
rjl493456442 authored and MoonShiesty committed Aug 30, 2023
1 parent 3cc7b1e commit 5c85493
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 1 deletion.
8 changes: 8 additions & 0 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ type (
account *common.Address
prev *stateObject
prevdestruct bool
prevAccount []byte
prevStorage map[common.Hash][]byte
}
suicideChange struct {
account *common.Address
Expand Down Expand Up @@ -160,6 +162,12 @@ func (ch resetObjectChange) revert(s *StateDB) {
if !ch.prevdestruct {
delete(s.stateObjectsDestruct, ch.prev.address)
}
if ch.prevAccount != nil {
s.snapAccounts[ch.prev.addrHash] = ch.prevAccount
}
if ch.prevStorage != nil {
s.snapStorage[ch.prev.addrHash] = ch.prevStorage
}
}

func (ch resetObjectChange) dirtied() *common.Address {
Expand Down
25 changes: 24 additions & 1 deletion core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,11 +627,34 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
if prev == nil {
s.journal.append(createObjectChange{account: &addr})
} else {
// The original account should be marked as destructed and all cached
// account and storage data should be cleared as well. Note, it must
// be done here, otherwise the destruction event of original one will
// be lost.
_, prevdestruct := s.stateObjectsDestruct[prev.address]
if !prevdestruct {
s.stateObjectsDestruct[prev.address] = struct{}{}
}
s.journal.append(resetObjectChange{account: &addr, prev: prev, prevdestruct: prevdestruct})
var (
account []byte
storage map[common.Hash][]byte
)
// There may be some cached account/storage data already since IntermediateRoot
// will be called for each transaction before byzantium fork which will always
// cache the latest account/storage data.
if s.snap != nil {
account = s.snapAccounts[prev.addrHash]
storage = s.snapStorage[prev.addrHash]
delete(s.snapAccounts, prev.addrHash)
delete(s.snapStorage, prev.addrHash)
}
s.journal.append(resetObjectChange{
account: &addr,
prev: prev,
prevdestruct: prevdestruct,
prevAccount: account,
prevStorage: storage,
})
}
s.setStateObject(newobj)
if prev != nil && !prev.deleted {
Expand Down
37 changes: 37 additions & 0 deletions core/state/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/trie"
)

// Tests that updating a state trie does not leak any database writes prior to
Expand Down Expand Up @@ -998,3 +1001,37 @@ func TestStateDBTransientStorage(t *testing.T) {
t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
}
}

func TestResetObject(t *testing.T) {
var (
disk = rawdb.NewMemoryDatabase()
tdb = trie.NewDatabase(disk)
db = NewDatabaseWithNodeDB(disk, tdb)
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
state, _ = New(types.EmptyRootHash, db, snaps)
addr = common.HexToAddress("0x1")
slotA = common.HexToHash("0x1")
slotB = common.HexToHash("0x2")
)
// Initialize account with balance and storage in first transaction.
state.SetBalance(addr, big.NewInt(1))
state.SetState(addr, slotA, common.BytesToHash([]byte{0x1}))
state.IntermediateRoot(true)

// Reset account and mutate balance and storages
state.CreateAccount(addr)
state.SetBalance(addr, big.NewInt(2))
state.SetState(addr, slotB, common.BytesToHash([]byte{0x2}))
root, _ := state.Commit(true)

// Ensure the original account is wiped properly
snap := snaps.Snapshot(root)
slot, _ := snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotA.Bytes()))
if len(slot) != 0 {
t.Fatalf("Unexpected storage slot")
}
slot, _ = snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotB.Bytes()))
if !bytes.Equal(slot, []byte{0x2}) {
t.Fatalf("Unexpected storage slot value %v", slot)
}
}

0 comments on commit 5c85493

Please sign in to comment.