diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index c128d2488c..0ba04d5613 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -89,6 +89,16 @@ func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash) return h.hash(data) == hash } +// ExistsAccountTrieNode checks the presence of the account trie node with the +// specified node path, regardless of the node hash. +func ExistsAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool { + has, err := db.Has(accountTrieNodeKey(path)) + if err != nil { + return false + } + return has +} + // WriteAccountTrieNode writes the provided account trie node into database. func WriteAccountTrieNode(db ethdb.KeyValueWriter, path []byte, node []byte) { if err := db.Put(accountTrieNodeKey(path), node); err != nil { @@ -127,6 +137,16 @@ func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path [ return h.hash(data) == hash } +// ExistsStorageTrieNode checks the presence of the storage trie node with the +// specified account hash and node path, regardless of the node hash. +func ExistsStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool { + has, err := db.Has(storageTrieNodeKey(accountHash, path)) + if err != nil { + return false + } + return has +} + // WriteStorageTrieNode writes the provided storage trie node into database. func WriteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte, node []byte) { if err := db.Put(storageTrieNodeKey(accountHash, path), node); err != nil { @@ -141,24 +161,6 @@ func DeleteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, pat } } -// HasTrieNodeInPath checks for the presence of the trie node with the specified -// account hash and node path, regardless of the node hash. -func HasTrieNodeInPath(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool { - var ( - err error - result bool - ) - if accountHash == (common.Hash{}) { - result, err = db.Has(accountTrieNodeKey(path)) - } else { - result, err = db.Has(storageTrieNodeKey(accountHash, path)) - } - if err != nil { - return false - } - return result -} - // ReadLegacyTrieNode retrieves the legacy trie node with the given // associated node hash. func ReadLegacyTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte { @@ -282,6 +284,29 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has } } +// ExistsTrieNode checks for the presence of the trie node with the specified +// account hash and node path, regardless of the node hash. +// +// hashScheme-based lookup requires the following: +// - hash +// +// pathScheme-based lookup requires the following: +// - owner +// - path +func ExistsTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) bool { + switch scheme { + case HashScheme: + return HasLegacyTrieNode(db, hash) + case PathScheme: + if owner == (common.Hash{}) { + return ExistsAccountTrieNode(db, path) + } + return ExistsStorageTrieNode(db, owner, path) + default: + panic(fmt.Sprintf("Unknown scheme %v", scheme)) + } +} + // ReadStateScheme reads the state scheme of persistent state, or none // if the state is not present in database. func ReadStateScheme(db ethdb.Reader) string { diff --git a/trie/sync.go b/trie/sync.go index 8d3a54ec47..dd2804b687 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -361,20 +361,23 @@ func (s *Sync) ProcessNode(result NodeSyncResult) error { // Commit flushes the data stored in the internal membatch out to persistent // storage, returning any occurred error. func (s *Sync) Commit(dbw ethdb.Batch) error { - // Dump the membatch into a database dbw + // Flush the pending node writes into database batch. for path, value := range s.membatch.nodes { owner, inner := ResolvePath([]byte(path)) rawdb.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value, s.scheme) } + // Flush the pending node deletes into the database batch. + // Please note that each written and deleted node has a + // unique path, ensuring no duplication occurs. for path := range s.membatch.deletes { owner, inner := ResolvePath([]byte(path)) rawdb.DeleteTrieNode(dbw, owner, inner, common.Hash{} /* unused */, s.scheme) } + // Flush the pending code writes into database batch. for hash, value := range s.membatch.codes { rawdb.WriteCode(dbw, hash, value) } - // Drop the membatch data and return - s.membatch = newSyncMemBatch() + s.membatch = newSyncMemBatch() // reset the batch return nil } @@ -464,7 +467,13 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) { // Pebble doesn't use a bloom filter to enhance read performance // for non-existent items, this check would significantly slow down // overall performance. FIX IT(rjl493456442) - if rawdb.HasTrieNodeInPath(s.database, owner, append(inner, key[:i]...)) { + var exists bool + if owner == (common.Hash{}) { + exists = rawdb.ExistsAccountTrieNode(s.database, append(inner, key[:i]...)) + } else { + exists = rawdb.ExistsStorageTrieNode(s.database, owner, append(inner, key[:i]...)) + } + if exists { req.deletes = append(req.deletes, key[:i]) deletionGauge.Inc(1) log.Info("Detected dangling node", "owner", owner, "path", append(inner, key[:i]...))