dbcmd inspect trie depth

This commit is contained in:
weiihann 2025-12-27 14:09:27 +08:00
parent c974722dc0
commit 9276cfdb9f
2 changed files with 157 additions and 0 deletions

View file

@ -70,6 +70,7 @@ Remove blockchain and state databases`,
ArgsUsage: "",
Subcommands: []*cli.Command{
dbInspectCmd,
dbInspectTrieDepthCmd,
dbStatCmd,
dbCompactCmd,
dbGetCmd,
@ -92,6 +93,14 @@ Remove blockchain and state databases`,
Usage: "Inspect the storage size for each type of data in the database",
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
}
dbInspectTrieDepthCmd = &cli.Command{
Action: inspectTrieDepth,
Name: "inspect-trie-depth",
Usage: "Inspect the depth of the trie",
ArgsUsage: "<start> <end>",
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
Description: "This command inspects the depth of the trie",
}
dbCheckStateContentCmd = &cli.Command{
Action: checkStateContent,
Name: "check-state-content",
@ -331,6 +340,16 @@ func inspect(ctx *cli.Context) error {
return rawdb.InspectDatabase(db, prefix, start)
}
func inspectTrieDepth(ctx *cli.Context) error {
stack, _ := makeConfigNode(ctx)
defer stack.Close()
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
return rawdb.InspectTrieDepth(db)
}
func checkStateContent(ctx *cli.Context) error {
var (
prefix []byte

View file

@ -798,3 +798,141 @@ func SafeDeleteRange(db ethdb.KeyValueStore, start, end []byte, hashScheme bool,
}
return batch.Write()
}
// InspectTrieDepth traverses all path-based trie nodes in the database
// and calculates the depth distribution of nodes in the account trie
// and storage tries. Uses a streaming algorithm that processes nodes
// in lexicographic order without loading all paths into memory.
func InspectTrieDepth(db ethdb.Database) error {
var (
start = time.Now()
accountDepths [65]int64
storageDepths [65]int64
accountCount int64
storageCount int64
storageTriesCnt int64
nodeCount int64
)
log.Info("Calculating trie depth distribution (streaming)...")
// Process account trie nodes (prefix "A")
// Keys are already in lexicographic order from the database iterator
accountStack := make([]string, 0, 65)
it := db.NewIterator(TrieNodeAccountPrefix, nil)
for it.Next() {
ok, path := ResolveAccountTrieNodeKey(it.Key())
if !ok {
continue
}
pathStr := string(path)
// Pop until stack top is a strict prefix of current path
for len(accountStack) > 0 {
top := accountStack[len(accountStack)-1]
if len(top) < len(pathStr) && pathStr[:len(top)] == top {
break
}
accountStack = accountStack[:len(accountStack)-1]
}
depth := len(accountStack)
accountDepths[depth]++
accountCount++
nodeCount++
accountStack = append(accountStack, pathStr)
if nodeCount%10000000 == 0 {
log.Info("Processing nodes", "count", nodeCount, "elapsed", common.PrettyDuration(time.Since(start)))
}
}
it.Release()
if err := it.Error(); err != nil {
return err
}
log.Info("Finished account trie", "nodes", accountCount, "elapsed", common.PrettyDuration(time.Since(start)))
// Process storage trie nodes (prefix "O")
// Keys are ordered by (accountHash, path), so when accountHash changes,
// we're starting a new trie and need to reset the stack
var (
storageStack = make([]string, 0, 65)
currentAccountHash common.Hash
firstStorageTrie = true
)
it = db.NewIterator(TrieNodeStoragePrefix, nil)
for it.Next() {
ok, accountHash, path := ResolveStorageTrieNode(it.Key())
if !ok {
continue
}
// Check if we're starting a new storage trie
if accountHash != currentAccountHash {
if !firstStorageTrie {
storageTriesCnt++
}
firstStorageTrie = false
currentAccountHash = accountHash
storageStack = storageStack[:0] // Reset stack for new trie
}
pathStr := string(path)
// Pop until stack top is a strict prefix of current path
for len(storageStack) > 0 {
top := storageStack[len(storageStack)-1]
if len(top) < len(pathStr) && pathStr[:len(top)] == top {
break
}
storageStack = storageStack[:len(storageStack)-1]
}
depth := len(storageStack)
storageDepths[depth]++
storageCount++
nodeCount++
storageStack = append(storageStack, pathStr)
if nodeCount%10000000 == 0 {
log.Info("Processing nodes", "count", nodeCount, "elapsed", common.PrettyDuration(time.Since(start)))
}
}
it.Release()
if err := it.Error(); err != nil {
return err
}
// Count the last storage trie if we processed any
if !firstStorageTrie {
storageTriesCnt++
}
log.Info("Depth calculation complete",
"accountNodes", accountCount,
"storageNodes", storageCount,
"storageTries", storageTriesCnt,
"elapsed", common.PrettyDuration(time.Since(start)))
// Print results
fmt.Println("\n=== Trie Depth Distribution ===")
fmt.Printf("\nAccount Trie: %d nodes\n", accountCount)
fmt.Printf("Storage Tries: %d nodes across %d tries\n\n", storageCount, storageTriesCnt)
fmt.Println("Depth | Account Nodes | Storage Nodes")
fmt.Println("------|---------------|---------------")
for i := 0; i < 65; i++ {
if accountDepths[i] > 0 || storageDepths[i] > 0 {
fmt.Printf("%5d | %13d | %13d\n", i, accountDepths[i], storageDepths[i])
}
}
fmt.Println()
return nil
}