-
Notifications
You must be signed in to change notification settings - Fork 117
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
195 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
// Originally forked from https://github.com/cosmos/iavl/blob/v0.20.0/cmd/iaviewer/main.go | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
|
||
dbm "github.com/cometbft/cometbft-db" | ||
|
||
"github.com/cosmos/iavl" | ||
) | ||
|
||
// TODO: make this configurable? | ||
const ( | ||
DefaultCacheSize int = 10000 | ||
) | ||
|
||
func main() { | ||
args := os.Args[1:] | ||
if len(args) < 3 || (args[0] != "data" && args[0] != "shape" && args[0] != "versions") { | ||
fmt.Fprintln(os.Stderr, "Usage: iaviewer <data|shape|versions> <leveldb dir> <prefix> [version number]") | ||
fmt.Fprintln(os.Stderr, "<prefix> is the prefix of db, and the iavl tree of different modules in cosmos-sdk uses ") | ||
fmt.Fprintln(os.Stderr, "different <prefix> to identify, just like \"s/k:gov/\" represents the prefix of gov module") | ||
os.Exit(1) | ||
} | ||
|
||
version := 0 | ||
if len(args) == 4 { | ||
var err error | ||
version, err = strconv.Atoi(args[3]) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Invalid version number: %s\n", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
tree, err := ReadTree(args[1], version, []byte(args[2])) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Error reading data: %s\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
switch args[0] { | ||
case "data": | ||
PrintKeys(tree) | ||
hash, err := tree.Hash() | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Error hashing tree: %s\n", err) | ||
os.Exit(1) | ||
} | ||
fmt.Printf("Hash: %X\n", hash) | ||
fmt.Printf("Size: %X\n", tree.Size()) | ||
case "shape": | ||
PrintShape(tree) | ||
case "versions": | ||
PrintVersions(tree) | ||
} | ||
} | ||
|
||
func OpenDB(dir string) (dbm.DB, error) { | ||
switch { | ||
case strings.HasSuffix(dir, ".db"): | ||
dir = dir[:len(dir)-3] | ||
case strings.HasSuffix(dir, ".db/"): | ||
dir = dir[:len(dir)-4] | ||
default: | ||
return nil, fmt.Errorf("database directory must end with .db") | ||
} | ||
|
||
dir, err := filepath.Abs(dir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// TODO: doesn't work on windows! | ||
cut := strings.LastIndex(dir, "/") | ||
if cut == -1 { | ||
return nil, fmt.Errorf("cannot cut paths on %s", dir) | ||
} | ||
name := dir[cut+1:] | ||
db, err := dbm.NewGoLevelDB(name, dir[:cut]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return db, nil | ||
} | ||
|
||
func PrintDBStats(db dbm.DB) { | ||
count := 0 | ||
prefix := map[string]int{} | ||
itr, err := db.Iterator(nil, nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
defer itr.Close() | ||
for ; itr.Valid(); itr.Next() { | ||
key := itr.Key()[:1] | ||
prefix[string(key)]++ | ||
count++ | ||
} | ||
if err := itr.Error(); err != nil { | ||
panic(err) | ||
} | ||
fmt.Printf("DB contains %d entries\n", count) | ||
for k, v := range prefix { | ||
fmt.Printf(" %s: %d\n", k, v) | ||
} | ||
} | ||
|
||
// ReadTree loads an iavl tree from the directory | ||
// If version is 0, load latest, otherwise, load named version | ||
// The prefix represents which iavl tree you want to read. The iaviwer will always set a prefix. | ||
func ReadTree(dir string, version int, prefix []byte) (*iavl.MutableTree, error) { | ||
db, err := OpenDB(dir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if len(prefix) != 0 { | ||
db = dbm.NewPrefixDB(db, prefix) | ||
} | ||
|
||
tree, err := iavl.NewMutableTree(db, DefaultCacheSize, false) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ver, err := tree.LoadVersion(int64(version)) | ||
fmt.Printf("Got version: %d\n", ver) | ||
return tree, err | ||
} | ||
|
||
func PrintKeys(tree *iavl.MutableTree) { | ||
fmt.Println("Printing all keys with hashed values (to detect diff)") | ||
tree.Iterate(func(key []byte, value []byte) bool { //nolint:errcheck | ||
printKey := parseWeaveKey(key) | ||
digest := sha256.Sum256(value) | ||
fmt.Printf(" %s\n %X\n", printKey, digest) | ||
return false | ||
}) | ||
} | ||
|
||
// parseWeaveKey assumes a separating : where all in front should be ascii, | ||
// and all afterwards may be ascii or binary | ||
func parseWeaveKey(key []byte) string { | ||
cut := bytes.IndexRune(key, ':') | ||
if cut == -1 { | ||
return encodeID(key) | ||
} | ||
prefix := key[:cut] | ||
id := key[cut+1:] | ||
return fmt.Sprintf("%s:%s", encodeID(prefix), encodeID(id)) | ||
} | ||
|
||
// casts to a string if it is printable ascii, hex-encodes otherwise | ||
func encodeID(id []byte) string { | ||
for _, b := range id { | ||
if b < 0x20 || b >= 0x80 { | ||
return strings.ToUpper(hex.EncodeToString(id)) | ||
} | ||
} | ||
return string(id) | ||
} | ||
|
||
func PrintShape(tree *iavl.MutableTree) { | ||
// shape := tree.RenderShape(" ", nil) | ||
// TODO: handle this error | ||
shape, _ := tree.RenderShape(" ", nodeEncoder) | ||
fmt.Println(strings.Join(shape, "\n")) | ||
} | ||
|
||
func nodeEncoder(id []byte, depth int, isLeaf bool) string { | ||
prefix := fmt.Sprintf("-%d ", depth) | ||
if isLeaf { | ||
prefix = fmt.Sprintf("*%d ", depth) | ||
} | ||
if len(id) == 0 { | ||
return fmt.Sprintf("%s<nil>", prefix) | ||
} | ||
return fmt.Sprintf("%s%s", prefix, parseWeaveKey(id)) | ||
} | ||
|
||
func PrintVersions(tree *iavl.MutableTree) { | ||
versions := tree.AvailableVersions() | ||
fmt.Println("Available versions:") | ||
for _, v := range versions { | ||
fmt.Printf(" %d\n", v) | ||
} | ||
} |