mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
core: fix storage deletion on account self-destruct
This commit is contained in:
parent
8329e836ae
commit
394eaf75c1
6 changed files with 72 additions and 24 deletions
|
|
@ -91,3 +91,9 @@ func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash com
|
|||
log.Crit("Failed to delete storage snapshot", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// IterateStorageSnapshots returns an iterator for walking the entire storage
|
||||
// space of a specific account.
|
||||
func IterateStorageSnapshots(db ethdb.Iteratee, accountHash common.Hash) ethdb.Iterator {
|
||||
return db.NewIteratorWithPrefix(storageSnapshotsKey(accountHash))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,6 +159,11 @@ func storageSnapshotKey(accountHash, storageHash common.Hash) []byte {
|
|||
return append(append(StateSnapshotPrefix, accountHash.Bytes()...), storageHash.Bytes()...)
|
||||
}
|
||||
|
||||
// storageSnapshotsKey = StateSnapshotPrefix + account hash + storage hash
|
||||
func storageSnapshotsKey(accountHash common.Hash) []byte {
|
||||
return append(StateSnapshotPrefix, accountHash.Bytes()...)
|
||||
}
|
||||
|
||||
// bloomBitsKey = bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash
|
||||
func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte {
|
||||
key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...)
|
||||
|
|
|
|||
|
|
@ -247,12 +247,17 @@ func (dl *diffLayer) Cap(layers int, memory uint64) (uint64, uint64) {
|
|||
base = parent.parent.(*diskLayer)
|
||||
batch = base.db.NewBatch()
|
||||
)
|
||||
parent.lock.RLock()
|
||||
defer parent.lock.RUnlock()
|
||||
|
||||
// Start by temporarilly deleting the current snapshot block marker. This
|
||||
// ensures that in the case of a crash, the entire snapshot is invalidated.
|
||||
rawdb.DeleteSnapshotBlock(batch)
|
||||
|
||||
// Push all the accounts into the database
|
||||
for hash, data := range parent.accountData {
|
||||
if len(data) > 0 {
|
||||
// Account was updated, push to disk
|
||||
rawdb.WriteAccountSnapshot(batch, hash, data)
|
||||
base.cache.Set(string(hash[:]), data)
|
||||
|
||||
|
|
@ -262,12 +267,31 @@ func (dl *diffLayer) Cap(layers int, memory uint64) (uint64, uint64) {
|
|||
}
|
||||
batch.Reset()
|
||||
}
|
||||
} else {
|
||||
// Account was deleted, remove all storage slots too
|
||||
rawdb.DeleteAccountSnapshot(batch, hash)
|
||||
base.cache.Set(string(hash[:]), nil)
|
||||
|
||||
it := rawdb.IterateStorageSnapshots(base.db, hash)
|
||||
for it.Next() {
|
||||
if key := it.Key(); len(key) == 65 { // TODO(karalabe): Yuck, we should move this into the iterator
|
||||
batch.Delete(key)
|
||||
base.cache.Delete(string(key[1:]))
|
||||
}
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
}
|
||||
// Push all the storage slots into the database
|
||||
for accountHash, storage := range parent.storageData {
|
||||
for storageHash, data := range storage {
|
||||
if len(data) > 0 {
|
||||
rawdb.WriteStorageSnapshot(batch, accountHash, storageHash, data)
|
||||
base.cache.Set(string(append(accountHash[:], storageHash[:]...)), data)
|
||||
} else {
|
||||
rawdb.DeleteStorageSnapshot(batch, accountHash, storageHash)
|
||||
base.cache.Set(string(append(accountHash[:], storageHash[:]...)), nil)
|
||||
}
|
||||
}
|
||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||
if err := batch.Write(); err != nil {
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ func generateSnapshot(db ethdb.KeyValueStore, journal string, headNumber uint64,
|
|||
storageNodes int
|
||||
accountSize common.StorageSize
|
||||
storageSize common.StorageSize
|
||||
logged time.Time
|
||||
)
|
||||
batch := db.NewBatch()
|
||||
triedb := trie.NewDatabase(db)
|
||||
|
|
@ -176,8 +177,13 @@ func generateSnapshot(db ethdb.KeyValueStore, journal string, headNumber uint64,
|
|||
storageSize += curStorageSize
|
||||
storageNodes += curStorageNodes
|
||||
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
fmt.Printf("%#x: %9s + %9s (%6d slots, %6d nodes), total %9s (%d accs, %d nodes) + %9s (%d slots, %d nodes)\n", accIt.Key, curAccountSize.TerminalString(), curStorageSize.TerminalString(), curStorageCount, curStorageNodes, accountSize.TerminalString(), accountCount, accIt.Nodes, storageSize.TerminalString(), storageCount, storageNodes)
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
fmt.Printf("Totals: %9s (%d accs, %d nodes) + %9s (%d slots, %d nodes)\n", accountSize.TerminalString(), accountCount, accIt.Nodes, storageSize.TerminalString(), storageCount, storageNodes)
|
||||
|
||||
// Update the snapshot block marker and write any remainder data
|
||||
rawdb.WriteSnapshotBlock(batch, headNumber, headRoot)
|
||||
batch.Write()
|
||||
|
|
|
|||
|
|
@ -287,6 +287,23 @@ func (s *stateObject) updateTrie(db Database) Trie {
|
|||
// Make sure all dirty slots are finalized into the pending storage area
|
||||
s.finalise()
|
||||
|
||||
// Retrieve the snapshot storage map for the object
|
||||
var storage map[common.Hash][]byte
|
||||
if s.db.snap != nil {
|
||||
// Retrieve the old storage map, if available
|
||||
s.db.snapLock.RLock()
|
||||
storage = s.db.snapStorage[s.addrHash]
|
||||
s.db.snapLock.RUnlock()
|
||||
|
||||
// If no old storage map was available, create a new one
|
||||
if storage == nil {
|
||||
storage = make(map[common.Hash][]byte)
|
||||
|
||||
s.db.snapLock.Lock()
|
||||
s.db.snapStorage[s.addrHash] = storage
|
||||
s.db.snapLock.Unlock()
|
||||
}
|
||||
}
|
||||
// Insert all the pending updates into the trie
|
||||
tr := s.getTrie(db)
|
||||
for key, value := range s.pendingStorage {
|
||||
|
|
@ -305,20 +322,7 @@ func (s *stateObject) updateTrie(db Database) Trie {
|
|||
s.setError(tr.TryUpdate(key[:], v))
|
||||
}
|
||||
// If state snapshotting is active, cache the data til commit
|
||||
if s.db.snap != nil {
|
||||
// Retrieve an old storage map, if available
|
||||
s.db.snapLock.RLock()
|
||||
storage := s.db.snapStorage[s.addrHash]
|
||||
s.db.snapLock.RUnlock()
|
||||
|
||||
if storage == nil {
|
||||
// No old storage available, create a new one
|
||||
storage = make(map[common.Hash][]byte)
|
||||
|
||||
s.db.snapLock.Lock()
|
||||
s.db.snapStorage[s.addrHash] = storage
|
||||
s.db.snapLock.Unlock()
|
||||
}
|
||||
if storage != nil {
|
||||
storage[crypto.Keccak256Hash(key[:])] = v // v will be nil if value is 0x00
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -484,7 +484,10 @@ func (s *StateDB) deleteStateObject(obj *stateObject) {
|
|||
|
||||
// If state snapshotting is active, cache the data til commit
|
||||
if s.snap != nil {
|
||||
s.snapAccounts[obj.addrHash] = nil // Yes, nil means deleted
|
||||
s.snapLock.Lock()
|
||||
s.snapAccounts[obj.addrHash] = nil // We need to maintain account deletions explicitly
|
||||
s.snapStorage[obj.addrHash] = nil // We need to maintain storage deletions explicitly
|
||||
s.snapLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue