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  }