cmd/geth, core/rawdb: add inspect-contract

This commit is contained in:
weiihann 2026-01-29 23:11:58 +08:00
parent 4d057b2c49
commit b4083957d7
2 changed files with 114 additions and 0 deletions

View file

@ -71,6 +71,7 @@ Remove blockchain and state databases`,
Subcommands: []*cli.Command{
dbInspectCmd,
dbInspectTrieCmd,
dbInspectContractCmd,
dbStatCmd,
dbCompactCmd,
dbGetCmd,
@ -101,6 +102,14 @@ Remove blockchain and state databases`,
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
Description: "This command inspects the depth of the trie",
}
dbInspectContractCmd = &cli.Command{
Action: inspectContract,
Name: "inspect-contract",
Usage: "Inspect the on-disk footprint of a contract",
ArgsUsage: "<address>",
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
Description: "This command inspects the snapshot account, snapshot storage slots, and storage trie nodes for a given contract address.",
}
dbCheckStateContentCmd = &cli.Command{
Action: checkStateContent,
Name: "check-state-content",
@ -350,6 +359,21 @@ func inspectTrieDepth(ctx *cli.Context) error {
return rawdb.InspectTrieDepth(db)
}
func inspectContract(ctx *cli.Context) error {
if ctx.NArg() != 1 {
return fmt.Errorf("required argument: <address>")
}
address := common.HexToAddress(ctx.Args().Get(0))
stack, _ := makeConfigNode(ctx)
defer stack.Close()
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
return rawdb.InspectContract(db, address)
}
func checkStateContent(ctx *cli.Context) error {
var (
prefix []byte

View file

@ -943,3 +943,93 @@ func InspectTrieDepth(db ethdb.Database) error {
return nil
}
// InspectContract inspects the on-disk footprint of a single contract:
// snapshot account, snapshot storage slots, and path-based storage trie
// nodes including depth distribution.
func InspectContract(db ethdb.Database, address common.Address) error {
start := time.Now()
accountHash := crypto.Keccak256Hash(address.Bytes())
log.Info("Inspecting contract", "address", address, "hash", accountHash)
// 1. Account snapshot
accountData := ReadAccountSnapshot(db, accountHash)
accountSnapshotSize := len(accountSnapshotKey(accountHash)) + len(accountData)
// 2. Storage snapshot: iterate all storage slots for this account
var storageStat stat
storagePrefix := storageSnapshotsKey(accountHash)
it := db.NewIterator(storagePrefix, nil)
for it.Next() {
storageStat.add(common.StorageSize(len(it.Key()) + len(it.Value())))
}
it.Release()
if err := it.Error(); err != nil {
return err
}
// 3. Storage trie nodes: iterate and compute depth distribution
var (
trieStat stat
trieDepths [65]stat
trieStack = make([]string, 0, 65)
)
it = db.NewIterator(storageTrieNodeKey(accountHash, nil), nil)
for it.Next() {
ok, hash, path := ResolveStorageTrieNode(it.Key())
if !ok || hash != accountHash {
break
}
size := common.StorageSize(len(it.Key()) + len(it.Value()))
trieStat.add(size)
pathStr := string(path)
// Stack-based depth: pop until top is strict prefix of current
for len(trieStack) > 0 {
top := trieStack[len(trieStack)-1]
if len(top) < len(pathStr) && pathStr[:len(top)] == top {
break
}
trieStack = trieStack[:len(trieStack)-1]
}
trieDepths[len(trieStack)].add(size)
trieStack = append(trieStack, pathStr)
}
it.Release()
if err := it.Error(); err != nil {
return err
}
log.Info("Inspection complete", "elapsed", common.PrettyDuration(time.Since(start)))
// Print results
fmt.Printf("\n=== Contract Inspection: %s ===\n", address)
fmt.Printf("Account hash: %s\n\n", accountHash)
if len(accountData) == 0 {
fmt.Println("Account snapshot: not found")
} else {
fmt.Printf("Account snapshot: %s\n", common.StorageSize(accountSnapshotSize))
}
fmt.Printf("Snapshot storage: %s slots (%s)\n", storageStat.countString(), storageStat.sizeString())
fmt.Printf("Storage trie: %s nodes (%s)\n", trieStat.countString(), trieStat.sizeString())
fmt.Println("\nStorage Trie Depth Distribution:")
fmt.Println("Depth | Nodes | Size")
fmt.Println("------|---------------|---------------")
for i := 0; i < 65; i++ {
if !trieDepths[i].empty() {
fmt.Printf("%5d | %13s | %13s\n",
i, trieDepths[i].countString(), trieDepths[i].sizeString())
}
}
fmt.Println()
return nil
}