github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/iavl/cmd/iaviewer/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/hex" 7 "fmt" 8 "os" 9 "strconv" 10 "strings" 11 12 "github.com/fibonacci-chain/fbc/libs/iavl" 13 dbm "github.com/fibonacci-chain/fbc/libs/tm-db" 14 ) 15 16 // TODO: make this configurable? 17 const ( 18 DefaultCacheSize int = 10000 19 ) 20 21 func main() { 22 args := os.Args[1:] 23 if len(args) < 2 || (args[0] != "data" && args[0] != "shape" && args[0] != "versions") { 24 fmt.Fprintln(os.Stderr, "Usage: iaviewer <data|shape|versions> <leveldb dir> [version number]") 25 os.Exit(1) 26 } 27 28 version := 0 29 if len(args) == 3 { 30 var err error 31 version, err = strconv.Atoi(args[2]) 32 if err != nil { 33 fmt.Fprintf(os.Stderr, "Invalid version number: %s\n", err) 34 os.Exit(1) 35 } 36 } 37 38 tree, err := ReadTree(args[1], version) 39 if err != nil { 40 fmt.Fprintf(os.Stderr, "Error reading data: %s\n", err) 41 os.Exit(1) 42 } 43 44 switch args[0] { 45 case "data": 46 PrintKeys(tree) 47 fmt.Printf("Hash: %X\n", tree.Hash()) 48 fmt.Printf("Size: %X\n", tree.Size()) 49 case "shape": 50 PrintShape(tree) 51 case "versions": 52 PrintVersions(tree) 53 } 54 } 55 56 func OpenDB(dir string) (dbm.DB, error) { 57 switch { 58 case strings.HasSuffix(dir, ".db"): 59 dir = dir[:len(dir)-3] 60 case strings.HasSuffix(dir, ".db/"): 61 dir = dir[:len(dir)-4] 62 default: 63 return nil, fmt.Errorf("database directory must end with .db") 64 } 65 // TODO: doesn't work on windows! 66 cut := strings.LastIndex(dir, "/") 67 if cut == -1 { 68 return nil, fmt.Errorf("cannot cut paths on %s", dir) 69 } 70 name := dir[cut+1:] 71 db, err := dbm.NewGoLevelDB(name, dir[:cut]) 72 if err != nil { 73 return nil, err 74 } 75 return db, nil 76 } 77 78 // nolint: unused,deadcode 79 func PrintDBStats(db dbm.DB) { 80 count := 0 81 prefix := map[string]int{} 82 iter, err := db.Iterator(nil, nil) 83 if err != nil { 84 panic(err) 85 } 86 for ; iter.Valid(); iter.Next() { 87 key := string(iter.Key()[:1]) 88 prefix[key]++ 89 count++ 90 } 91 iter.Close() 92 fmt.Printf("DB contains %d entries\n", count) 93 for k, v := range prefix { 94 fmt.Printf(" %s: %d\n", k, v) 95 } 96 } 97 98 // ReadTree loads an iavl tree from the directory 99 // If version is 0, load latest, otherwise, load named version 100 func ReadTree(dir string, version int) (*iavl.MutableTree, error) { 101 db, err := OpenDB(dir) 102 if err != nil { 103 return nil, err 104 } 105 tree, err := iavl.NewMutableTree(db, DefaultCacheSize) 106 if err != nil { 107 return nil, err 108 } 109 ver, err := tree.LoadVersion(int64(version)) 110 fmt.Printf("Got version: %d\n", ver) 111 return tree, err 112 } 113 114 func PrintKeys(tree *iavl.MutableTree) { 115 fmt.Println("Printing all keys with hashed values (to detect diff)") 116 tree.Iterate(func(key []byte, value []byte) bool { 117 printKey := parseWeaveKey(key) 118 digest := sha256.Sum256(value) 119 fmt.Printf(" %s\n %X\n", printKey, digest) 120 return false 121 }) 122 } 123 124 // parseWeaveKey assumes a separating : where all in front should be ascii, 125 // and all afterwards may be ascii or binary 126 func parseWeaveKey(key []byte) string { 127 cut := bytes.IndexRune(key, ':') 128 if cut == -1 { 129 return encodeID(key) 130 } 131 prefix := key[:cut] 132 id := key[cut+1:] 133 return fmt.Sprintf("%s:%s", encodeID(prefix), encodeID(id)) 134 } 135 136 // casts to a string if it is printable ascii, hex-encodes otherwise 137 func encodeID(id []byte) string { 138 for _, b := range id { 139 if b < 0x20 || b >= 0x80 { 140 return strings.ToUpper(hex.EncodeToString(id)) 141 } 142 } 143 return string(id) 144 } 145 146 func PrintShape(tree *iavl.MutableTree) { 147 // shape := tree.RenderShape(" ", nil) 148 shape := tree.RenderShape(" ", nodeEncoder) 149 fmt.Println(strings.Join(shape, "\n")) 150 } 151 152 func nodeEncoder(id []byte, depth int, isLeaf bool) string { 153 prefix := fmt.Sprintf("-%d ", depth) 154 if isLeaf { 155 prefix = fmt.Sprintf("*%d ", depth) 156 } 157 if len(id) == 0 { 158 return fmt.Sprintf("%s<nil>", prefix) 159 } 160 return fmt.Sprintf("%s%s", prefix, parseWeaveKey(id)) 161 } 162 163 func PrintVersions(tree *iavl.MutableTree) { 164 versions := tree.AvailableVersions() 165 fmt.Println("Available versions:") 166 for _, v := range versions { 167 fmt.Printf(" %d\n", v) 168 } 169 }