core: fix storage deletion on account self-destruct

This commit is contained in:
Péter Szilágyi 2019-08-27 10:21:20 +03:00
parent 8329e836ae
commit 394eaf75c1
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
6 changed files with 72 additions and 24 deletions

View file

@ -91,3 +91,9 @@ func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash com
log.Crit("Failed to delete storage snapshot", "err", err) 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))
}

View file

@ -159,6 +159,11 @@ func storageSnapshotKey(accountHash, storageHash common.Hash) []byte {
return append(append(StateSnapshotPrefix, accountHash.Bytes()...), storageHash.Bytes()...) 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 // bloomBitsKey = bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash
func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte { func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte {
key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...) key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...)

View file

@ -247,12 +247,17 @@ func (dl *diffLayer) Cap(layers int, memory uint64) (uint64, uint64) {
base = parent.parent.(*diskLayer) base = parent.parent.(*diskLayer)
batch = base.db.NewBatch() batch = base.db.NewBatch()
) )
parent.lock.RLock()
defer parent.lock.RUnlock()
// Start by temporarilly deleting the current snapshot block marker. This // Start by temporarilly deleting the current snapshot block marker. This
// ensures that in the case of a crash, the entire snapshot is invalidated. // ensures that in the case of a crash, the entire snapshot is invalidated.
rawdb.DeleteSnapshotBlock(batch) rawdb.DeleteSnapshotBlock(batch)
// Push all the accounts into the database // Push all the accounts into the database
for hash, data := range parent.accountData { for hash, data := range parent.accountData {
if len(data) > 0 {
// Account was updated, push to disk
rawdb.WriteAccountSnapshot(batch, hash, data) rawdb.WriteAccountSnapshot(batch, hash, data)
base.cache.Set(string(hash[:]), data) base.cache.Set(string(hash[:]), data)
@ -262,12 +267,31 @@ func (dl *diffLayer) Cap(layers int, memory uint64) (uint64, uint64) {
} }
batch.Reset() 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 // Push all the storage slots into the database
for accountHash, storage := range parent.storageData { for accountHash, storage := range parent.storageData {
for storageHash, data := range storage { for storageHash, data := range storage {
if len(data) > 0 {
rawdb.WriteStorageSnapshot(batch, accountHash, storageHash, data) rawdb.WriteStorageSnapshot(batch, accountHash, storageHash, data)
base.cache.Set(string(append(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 batch.ValueSize() > ethdb.IdealBatchSize {
if err := batch.Write(); err != nil { if err := batch.Write(); err != nil {

View file

@ -119,6 +119,7 @@ func generateSnapshot(db ethdb.KeyValueStore, journal string, headNumber uint64,
storageNodes int storageNodes int
accountSize common.StorageSize accountSize common.StorageSize
storageSize common.StorageSize storageSize common.StorageSize
logged time.Time
) )
batch := db.NewBatch() batch := db.NewBatch()
triedb := trie.NewDatabase(db) triedb := trie.NewDatabase(db)
@ -176,8 +177,13 @@ func generateSnapshot(db ethdb.KeyValueStore, journal string, headNumber uint64,
storageSize += curStorageSize storageSize += curStorageSize
storageNodes += curStorageNodes 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) 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 // Update the snapshot block marker and write any remainder data
rawdb.WriteSnapshotBlock(batch, headNumber, headRoot) rawdb.WriteSnapshotBlock(batch, headNumber, headRoot)
batch.Write() batch.Write()

View file

@ -287,6 +287,23 @@ func (s *stateObject) updateTrie(db Database) Trie {
// Make sure all dirty slots are finalized into the pending storage area // Make sure all dirty slots are finalized into the pending storage area
s.finalise() 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 // Insert all the pending updates into the trie
tr := s.getTrie(db) tr := s.getTrie(db)
for key, value := range s.pendingStorage { for key, value := range s.pendingStorage {
@ -305,20 +322,7 @@ func (s *stateObject) updateTrie(db Database) Trie {
s.setError(tr.TryUpdate(key[:], v)) s.setError(tr.TryUpdate(key[:], v))
} }
// If state snapshotting is active, cache the data til commit // If state snapshotting is active, cache the data til commit
if s.db.snap != nil { if storage != 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()
}
storage[crypto.Keccak256Hash(key[:])] = v // v will be nil if value is 0x00 storage[crypto.Keccak256Hash(key[:])] = v // v will be nil if value is 0x00
} }
} }

View file

@ -484,7 +484,10 @@ func (s *StateDB) deleteStateObject(obj *stateObject) {
// If state snapshotting is active, cache the data til commit // If state snapshotting is active, cache the data til commit
if s.snap != nil { 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()
} }
} }