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)
|
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()...)
|
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()...)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue