mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 12:17:28 +00:00 
			
		
		
		
	feat(cli): add iavlviewer command (#1922)
* port iavlviewer to kava v0.26.x to debug app hash * add hash subcommand to iavlviewer additionally, use better error handling * update changelog * separate iavlviewer command into subcommands --------- Co-authored-by: Nick DeLuca <nickdeluca08@gmail.com>
This commit is contained in:
		
							parent
							
								
									110adcab2c
								
							
						
					
					
						commit
						2e8c7ce337
					
				@ -38,6 +38,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
- (precisebank) [#1906] Add new `x/precisebank` module with bank decimal extension for EVM usage.
 | 
			
		||||
- (cli) [#1922] Add `iavlviewer` CLI command for low-level iavl db debugging.
 | 
			
		||||
 | 
			
		||||
### Improvements
 | 
			
		||||
- (rocksdb) [#1903] Bump cometbft-db dependency for use with rocksdb v8.10.0
 | 
			
		||||
@ -336,6 +337,7 @@ the [changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/CHANGELOG.md).
 | 
			
		||||
- [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
 | 
			
		||||
  large-scale simulations remotely using aws-batch
 | 
			
		||||
 | 
			
		||||
[#1922]: https://github.com/Kava-Labs/kava/pull/1922
 | 
			
		||||
[#1906]: https://github.com/Kava-Labs/kava/pull/1906
 | 
			
		||||
[#1903]: https://github.com/Kava-Labs/kava/pull/1903
 | 
			
		||||
[#1846]: https://github.com/Kava-Labs/kava/pull/1846
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										53
									
								
								cmd/kava/cmd/iavlviewer/data.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								cmd/kava/cmd/iavlviewer/data.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
package iavlviewer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/iavl"
 | 
			
		||||
	ethermintserver "github.com/evmos/ethermint/server"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newDataCmd(opts ethermintserver.StartOptions) *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "data <prefix> [version number]",
 | 
			
		||||
		Short: "View all keys, hash, & size of tree.",
 | 
			
		||||
		Args:  cobra.RangeArgs(1, 2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			prefix := args[0]
 | 
			
		||||
			version := 0
 | 
			
		||||
			if len(args) == 2 {
 | 
			
		||||
				var err error
 | 
			
		||||
				version, err = parseVersion(args[1])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			tree, err := openPrefixTree(opts, cmd, prefix, version)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			printKeys(tree)
 | 
			
		||||
			hash := tree.Hash()
 | 
			
		||||
			fmt.Printf("Hash: %X\n", hash)
 | 
			
		||||
			fmt.Printf("Size: %X\n", tree.Size())
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								cmd/kava/cmd/iavlviewer/hash.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								cmd/kava/cmd/iavlviewer/hash.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
package iavlviewer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	ethermintserver "github.com/evmos/ethermint/server"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newHashCmd(opts ethermintserver.StartOptions) *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "hash <prefix> [version number]",
 | 
			
		||||
		Short: "Print the root hash of the iavl tree.",
 | 
			
		||||
		Args:  cobra.RangeArgs(1, 2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			prefix := args[0]
 | 
			
		||||
			version := 0
 | 
			
		||||
			if len(args) == 2 {
 | 
			
		||||
				var err error
 | 
			
		||||
				version, err = parseVersion(args[1])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			tree, err := openPrefixTree(opts, cmd, prefix, version)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fmt.Printf("Hash: %X\n", tree.Hash())
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								cmd/kava/cmd/iavlviewer/root.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								cmd/kava/cmd/iavlviewer/root.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
			
		||||
package iavlviewer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"cosmossdk.io/log"
 | 
			
		||||
	dbm "github.com/cosmos/cosmos-db"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/server"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/store/wrapper"
 | 
			
		||||
	ethermintserver "github.com/evmos/ethermint/server"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/iavl"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DefaultCacheSize int = 10000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewCmd(opts ethermintserver.StartOptions) *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "iavlviewer <data|hash|shape|versions> <prefix> [version number]",
 | 
			
		||||
		Short: "Output various data, hashes, and calculations for an iavl tree",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.AddCommand(newDataCmd(opts))
 | 
			
		||||
	cmd.AddCommand(newHashCmd(opts))
 | 
			
		||||
	cmd.AddCommand(newShapeCmd(opts))
 | 
			
		||||
	cmd.AddCommand(newVersionsCmd(opts))
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseVersion(arg string) (int, error) {
 | 
			
		||||
	version, err := strconv.Atoi(arg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, fmt.Errorf("invalid version number: '%s'", arg)
 | 
			
		||||
	}
 | 
			
		||||
	return version, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func openPrefixTree(opts ethermintserver.StartOptions, cmd *cobra.Command, prefix string, version int) (*iavl.MutableTree, error) {
 | 
			
		||||
	clientCtx := client.GetClientContextFromCmd(cmd)
 | 
			
		||||
	ctx := server.GetServerContextFromCmd(cmd)
 | 
			
		||||
	ctx.Config.SetRoot(clientCtx.HomeDir)
 | 
			
		||||
 | 
			
		||||
	db, err := opts.DBOpener(ctx.Viper, clientCtx.HomeDir, server.GetAppDBBackend(ctx.Viper))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to open database at %s: %s", clientCtx.HomeDir, err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := db.Close(); err != nil {
 | 
			
		||||
			ctx.Logger.Error("error closing db", "error", err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	cosmosdb := wrapper.NewCosmosDB(db)
 | 
			
		||||
 | 
			
		||||
	tree, err := readTree(cosmosdb, version, []byte(prefix))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to read tree with prefix %s: %s", prefix, err)
 | 
			
		||||
	}
 | 
			
		||||
	return tree, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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(db dbm.DB, version int, prefix []byte) (*iavl.MutableTree, error) {
 | 
			
		||||
	if len(prefix) != 0 {
 | 
			
		||||
		db = dbm.NewPrefixDB(db, prefix)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tree := iavl.NewMutableTree(db, DefaultCacheSize, false, log.NewLogger(os.Stdout))
 | 
			
		||||
	ver, err := tree.LoadVersion(int64(version))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Latest version: %d\n", ver)
 | 
			
		||||
	fmt.Printf("Got version: %d\n", version)
 | 
			
		||||
	return tree, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								cmd/kava/cmd/iavlviewer/shape.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								cmd/kava/cmd/iavlviewer/shape.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
package iavlviewer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/iavl"
 | 
			
		||||
	ethermintserver "github.com/evmos/ethermint/server"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newShapeCmd(opts ethermintserver.StartOptions) *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "shape <prefix> [version number]",
 | 
			
		||||
		Short: "View shape of iavl tree.",
 | 
			
		||||
		Args:  cobra.RangeArgs(1, 2),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			prefix := args[0]
 | 
			
		||||
			version := 0
 | 
			
		||||
			if len(args) == 2 {
 | 
			
		||||
				var err error
 | 
			
		||||
				version, err = parseVersion(args[1])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			tree, err := openPrefixTree(opts, cmd, prefix, version)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			printShape(tree)
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func printShape(tree *iavl.MutableTree) {
 | 
			
		||||
	// shape := tree.RenderShape("  ", nil)
 | 
			
		||||
	// TODO: handle this error
 | 
			
		||||
	shape, _ := tree.RenderShape("  ", nodeEncoder)
 | 
			
		||||
	fmt.Println(strings.Join(shape, "\n"))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								cmd/kava/cmd/iavlviewer/versions.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								cmd/kava/cmd/iavlviewer/versions.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
			
		||||
package iavlviewer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosmos/iavl"
 | 
			
		||||
	ethermintserver "github.com/evmos/ethermint/server"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newVersionsCmd(opts ethermintserver.StartOptions) *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "versions <prefix>",
 | 
			
		||||
		Short: "Print all versions of iavl tree",
 | 
			
		||||
		Args:  cobra.ExactArgs(1),
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			prefix := args[0]
 | 
			
		||||
			tree, err := openPrefixTree(opts, cmd, prefix, 15)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			printVersions(tree)
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func printVersions(tree *iavl.MutableTree) {
 | 
			
		||||
	versions := tree.AvailableVersions()
 | 
			
		||||
	fmt.Println("Available versions:")
 | 
			
		||||
	for _, v := range versions {
 | 
			
		||||
		fmt.Printf("  %d\n", v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 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))
 | 
			
		||||
}
 | 
			
		||||
@ -25,6 +25,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/app/params"
 | 
			
		||||
	"github.com/kava-labs/kava/cmd/kava/cmd/iavlviewer"
 | 
			
		||||
	"github.com/kava-labs/kava/cmd/kava/cmd/rocksdb"
 | 
			
		||||
	"github.com/kava-labs/kava/cmd/kava/opendb"
 | 
			
		||||
)
 | 
			
		||||
@ -134,5 +135,6 @@ func addSubCmds(rootCmd *cobra.Command, encodingConfig params.EncodingConfig, de
 | 
			
		||||
		keyCommands(app.DefaultNodeHome),
 | 
			
		||||
		rocksdb.RocksDBCmd,
 | 
			
		||||
		newShardCmd(opts),
 | 
			
		||||
		iavlviewer.NewCmd(opts),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.mod
									
									
									
									
									
								
							@ -4,15 +4,18 @@ go 1.21
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	cosmossdk.io/errors v1.0.1
 | 
			
		||||
	cosmossdk.io/log v1.3.1
 | 
			
		||||
	cosmossdk.io/math v1.3.0
 | 
			
		||||
	cosmossdk.io/simapp v0.0.0-20231127212628-044ff4d8c015
 | 
			
		||||
	github.com/cenkalti/backoff/v4 v4.1.3
 | 
			
		||||
	github.com/cometbft/cometbft v0.37.4
 | 
			
		||||
	github.com/cometbft/cometbft-db v0.9.1
 | 
			
		||||
	github.com/cosmos/cosmos-db v1.0.0
 | 
			
		||||
	github.com/cosmos/cosmos-proto v1.0.0-beta.4
 | 
			
		||||
	github.com/cosmos/cosmos-sdk v0.47.10
 | 
			
		||||
	github.com/cosmos/go-bip39 v1.0.0
 | 
			
		||||
	github.com/cosmos/gogoproto v1.4.10
 | 
			
		||||
	github.com/cosmos/iavl v1.0.0
 | 
			
		||||
	github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.1.3
 | 
			
		||||
	github.com/cosmos/ibc-go/v7 v7.4.0
 | 
			
		||||
	github.com/ethereum/go-ethereum v1.10.26
 | 
			
		||||
@ -47,7 +50,6 @@ require (
 | 
			
		||||
	cosmossdk.io/api v0.3.1 // indirect
 | 
			
		||||
	cosmossdk.io/core v0.6.1 // indirect
 | 
			
		||||
	cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
 | 
			
		||||
	cosmossdk.io/log v1.3.1 // indirect
 | 
			
		||||
	cosmossdk.io/tools/rosetta v0.2.1 // indirect
 | 
			
		||||
	filippo.io/edwards25519 v1.0.0 // indirect
 | 
			
		||||
	github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
 | 
			
		||||
@ -77,9 +79,7 @@ require (
 | 
			
		||||
	github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect
 | 
			
		||||
	github.com/confio/ics23/go v0.9.0 // indirect
 | 
			
		||||
	github.com/cosmos/btcutil v1.0.5 // indirect
 | 
			
		||||
	github.com/cosmos/cosmos-db v1.0.0 // indirect
 | 
			
		||||
	github.com/cosmos/gogogateway v1.2.0 // indirect
 | 
			
		||||
	github.com/cosmos/iavl v1.0.0 // indirect
 | 
			
		||||
	github.com/cosmos/ics23/go v0.10.0 // indirect
 | 
			
		||||
	github.com/cosmos/ledger-cosmos-go v0.13.1 // indirect
 | 
			
		||||
	github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user