Skip to content

Commit f60b50e

Browse files
rjl493456442devopsbo3
authored andcommitted
core/state: clear out cached state data when reset occurs (ethereum#27376)
* core/state: remove cached snap data if reset occurs * core/state: address comment from peter * core/state: skip revert in case data is nil
1 parent 8d91a3e commit f60b50e

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

core/state/journal.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ type (
9393
account *common.Address
9494
prev *stateObject
9595
prevdestruct bool
96+
prevAccount []byte
97+
prevStorage map[common.Hash][]byte
9698
}
9799
suicideChange struct {
98100
account *common.Address
@@ -160,6 +162,12 @@ func (ch resetObjectChange) revert(s *StateDB) {
160162
if !ch.prevdestruct {
161163
delete(s.stateObjectsDestruct, ch.prev.address)
162164
}
165+
if ch.prevAccount != nil {
166+
s.snapAccounts[ch.prev.addrHash] = ch.prevAccount
167+
}
168+
if ch.prevStorage != nil {
169+
s.snapStorage[ch.prev.addrHash] = ch.prevStorage
170+
}
163171
}
164172

165173
func (ch resetObjectChange) dirtied() *common.Address {

core/state/statedb.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,11 +627,34 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
627627
if prev == nil {
628628
s.journal.append(createObjectChange{account: &addr})
629629
} else {
630+
// The original account should be marked as destructed and all cached
631+
// account and storage data should be cleared as well. Note, it must
632+
// be done here, otherwise the destruction event of original one will
633+
// be lost.
630634
_, prevdestruct := s.stateObjectsDestruct[prev.address]
631635
if !prevdestruct {
632636
s.stateObjectsDestruct[prev.address] = struct{}{}
633637
}
634-
s.journal.append(resetObjectChange{account: &addr, prev: prev, prevdestruct: prevdestruct})
638+
var (
639+
account []byte
640+
storage map[common.Hash][]byte
641+
)
642+
// There may be some cached account/storage data already since IntermediateRoot
643+
// will be called for each transaction before byzantium fork which will always
644+
// cache the latest account/storage data.
645+
if s.snap != nil {
646+
account = s.snapAccounts[prev.addrHash]
647+
storage = s.snapStorage[prev.addrHash]
648+
delete(s.snapAccounts, prev.addrHash)
649+
delete(s.snapStorage, prev.addrHash)
650+
}
651+
s.journal.append(resetObjectChange{
652+
account: &addr,
653+
prev: prev,
654+
prevdestruct: prevdestruct,
655+
prevAccount: account,
656+
prevStorage: storage,
657+
})
635658
}
636659
s.setStateObject(newobj)
637660
if prev != nil && !prev.deleted {

core/state/statedb_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ import (
3131

3232
"github.com/ethereum/go-ethereum/common"
3333
"github.com/ethereum/go-ethereum/core/rawdb"
34+
"github.com/ethereum/go-ethereum/core/state/snapshot"
3435
"github.com/ethereum/go-ethereum/core/types"
36+
"github.com/ethereum/go-ethereum/crypto"
37+
"github.com/ethereum/go-ethereum/trie"
3538
)
3639

3740
// Tests that updating a state trie does not leak any database writes prior to
@@ -998,3 +1001,37 @@ func TestStateDBTransientStorage(t *testing.T) {
9981001
t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
9991002
}
10001003
}
1004+
1005+
func TestResetObject(t *testing.T) {
1006+
var (
1007+
disk = rawdb.NewMemoryDatabase()
1008+
tdb = trie.NewDatabase(disk)
1009+
db = NewDatabaseWithNodeDB(disk, tdb)
1010+
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
1011+
state, _ = New(types.EmptyRootHash, db, snaps)
1012+
addr = common.HexToAddress("0x1")
1013+
slotA = common.HexToHash("0x1")
1014+
slotB = common.HexToHash("0x2")
1015+
)
1016+
// Initialize account with balance and storage in first transaction.
1017+
state.SetBalance(addr, big.NewInt(1))
1018+
state.SetState(addr, slotA, common.BytesToHash([]byte{0x1}))
1019+
state.IntermediateRoot(true)
1020+
1021+
// Reset account and mutate balance and storages
1022+
state.CreateAccount(addr)
1023+
state.SetBalance(addr, big.NewInt(2))
1024+
state.SetState(addr, slotB, common.BytesToHash([]byte{0x2}))
1025+
root, _ := state.Commit(true)
1026+
1027+
// Ensure the original account is wiped properly
1028+
snap := snaps.Snapshot(root)
1029+
slot, _ := snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotA.Bytes()))
1030+
if len(slot) != 0 {
1031+
t.Fatalf("Unexpected storage slot")
1032+
}
1033+
slot, _ = snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotB.Bytes()))
1034+
if !bytes.Equal(slot, []byte{0x2}) {
1035+
t.Fatalf("Unexpected storage slot value %v", slot)
1036+
}
1037+
}

0 commit comments

Comments
 (0)