From 7c10b2482c132506d8fa49167d60a13dfa233e22 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 24 Aug 2023 14:49:57 +0200 Subject: [PATCH] feat(hubl): support keyring (#17107) --- docs/docs/building-apps/01-app-go-v2.md | 2 +- tools/hubl/go.mod | 2 +- tools/hubl/go.sum | 4 +- tools/hubl/internal/config.go | 68 --------------- tools/hubl/internal/config/config.go | 108 +++++++++++++++++++++++ tools/hubl/internal/flags/flags.go | 18 ++++ tools/hubl/internal/keyring.go | 109 ++++++++++++++++++++++++ tools/hubl/internal/load.go | 9 +- tools/hubl/internal/registry.go | 8 +- tools/hubl/internal/remote.go | 105 ++++++++--------------- tools/hubl/internal/root.go | 39 +++++++++ tools/hubl/internal/util.go | 29 +++++++ tools/hubl/internal/version.go | 4 +- 13 files changed, 352 insertions(+), 153 deletions(-) delete mode 100644 tools/hubl/internal/config.go create mode 100644 tools/hubl/internal/config/config.go create mode 100644 tools/hubl/internal/flags/flags.go create mode 100644 tools/hubl/internal/keyring.go create mode 100644 tools/hubl/internal/root.go create mode 100644 tools/hubl/internal/util.go diff --git a/docs/docs/building-apps/01-app-go-v2.md b/docs/docs/building-apps/01-app-go-v2.md index 00b6924718..c5301413c7 100644 --- a/docs/docs/building-apps/01-app-go-v2.md +++ b/docs/docs/building-apps/01-app-go-v2.md @@ -90,7 +90,7 @@ modules: "@type": cosmos.staking.module.v1.Module - name: tx config: - "@type": cosmos.tx.module.v1.Module + "@type": cosmos.tx.config.v1.Config ``` A more complete example of `app.yaml` can be found [here](https://github.com/cosmos/cosmos-sdk/blob/91b1d83f1339e235a1dfa929ecc00084101a19e3/simapp/app.yaml). diff --git a/tools/hubl/go.mod b/tools/hubl/go.mod index b6f3fe970c..1d34945857 100644 --- a/tools/hubl/go.mod +++ b/tools/hubl/go.mod @@ -7,7 +7,7 @@ require ( cosmossdk.io/client/v2 v2.0.0-20230815130322-dded2e9921f0 cosmossdk.io/errors v1.0.0 github.com/cockroachdb/errors v1.10.0 - github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230815152400-f42d52f7e531 + github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230823104347-c6b0bb62ac70 github.com/manifoldco/promptui v0.9.0 github.com/pelletier/go-toml/v2 v2.0.9 github.com/spf13/cobra v1.7.0 diff --git a/tools/hubl/go.sum b/tools/hubl/go.sum index 735f933d25..c2369829fd 100644 --- a/tools/hubl/go.sum +++ b/tools/hubl/go.sum @@ -184,8 +184,8 @@ github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0 github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= -github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230815152400-f42d52f7e531 h1:wMqsKQzHof2ikpTlM5O1PZG4yr+15am+0ROtD/5wJLA= -github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230815152400-f42d52f7e531/go.mod h1:yQzBSxaplSkNZd34HOEqF4NhOHikbpFwWhWAQSteXrw= +github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230823104347-c6b0bb62ac70 h1:sK7w1WJAThRUXXtnpn7B8dBoSG8yFbYeuXNC+S69ANI= +github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230823104347-c6b0bb62ac70/go.mod h1:lUps0DsgRmykXPyjoEBIkJa0Ybq666DFdubg6AB+WCg= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= diff --git a/tools/hubl/internal/config.go b/tools/hubl/internal/config.go deleted file mode 100644 index 358eab41db..0000000000 --- a/tools/hubl/internal/config.go +++ /dev/null @@ -1,68 +0,0 @@ -package internal - -import ( - "bytes" - "os" - "path" - - "github.com/pelletier/go-toml/v2" - - "cosmossdk.io/errors" -) - -type Config struct { - Chains map[string]*ChainConfig `toml:"chains"` -} - -type ChainConfig struct { - GRPCEndpoints []GRPCEndpoint `toml:"trusted-grpc-endpoints"` - Bech32Prefix string `toml:"bech32-prefix"` -} - -type GRPCEndpoint struct { - Endpoint string `toml:"endpoint"` - Insecure bool `toml:"insecure"` -} - -func LoadConfig(configDir string) (*Config, error) { - configPath := configFilename(configDir) - - if _, err := os.Stat(configPath); os.IsNotExist(err) { - return &Config{Chains: map[string]*ChainConfig{}}, nil - } - - bz, err := os.ReadFile(configPath) - if err != nil { - return nil, errors.Wrapf(err, "can't read config file: %s", configPath) - } - - config := &Config{} - if err = toml.Unmarshal(bz, config); err != nil { - return nil, errors.Wrapf(err, "can't load config file: %s", configPath) - } - - return config, err -} - -func SaveConfig(configDir string, config *Config) error { - buf := &bytes.Buffer{} - enc := toml.NewEncoder(buf) - if err := enc.Encode(config); err != nil { - return err - } - - if err := os.MkdirAll(configDir, 0o755); err != nil { - return err - } - - configPath := configFilename(configDir) - if err := os.WriteFile(configPath, buf.Bytes(), 0o600); err != nil { - return err - } - - return nil -} - -func configFilename(configDir string) string { - return path.Join(configDir, "config.toml") -} diff --git a/tools/hubl/internal/config/config.go b/tools/hubl/internal/config/config.go new file mode 100644 index 0000000000..2a6bd8e0c3 --- /dev/null +++ b/tools/hubl/internal/config/config.go @@ -0,0 +1,108 @@ +package config + +import ( + "bytes" + "os" + "path" + + "github.com/pelletier/go-toml/v2" + + "cosmossdk.io/errors" + "cosmossdk.io/tools/hubl/internal/flags" +) + +const ( + DefaultConfigDirName = ".hubl" + GlobalKeyringDirName = "global" +) + +type Config struct { + Chains map[string]*ChainConfig `toml:"chains"` + KeyringBackend string `toml:"keyring-backend"` +} + +type ChainConfig struct { + GRPCEndpoints []GRPCEndpoint `toml:"trusted-grpc-endpoints"` + AddressPrefix string `toml:"address-prefix"` + KeyringBackend string `toml:"keyring-backend"` +} + +type GRPCEndpoint struct { + Endpoint string `toml:"endpoint"` + Insecure bool `toml:"insecure"` +} + +var EmptyConfig = &Config{ + Chains: map[string]*ChainConfig{}, + KeyringBackend: flags.DefaultKeyringBackend, +} + +func (cfg *Config) GetKeyringBackend(chainName string) (string, error) { + if chainName == GlobalKeyringDirName { + return cfg.KeyringBackend, nil + } else { + chainCfg, ok := cfg.Chains[chainName] + if ok { + return chainCfg.KeyringBackend, nil + } + } + + return flags.DefaultKeyringBackend, nil +} + +func GetConfigDir() (string, error) { + homeDir, err := os.UserHomeDir() + if err != nil { + return "", err + } + + configDir := path.Join(homeDir, DefaultConfigDirName) + if _, err := os.Stat(configDir); os.IsNotExist(err) { + return configDir, os.MkdirAll(configDir, 0o750) + } + + return configDir, nil +} + +func Load(configDir string) (*Config, error) { + configPath := configFilename(configDir) + + if _, err := os.Stat(configPath); os.IsNotExist(err) { + return EmptyConfig, nil + } + + bz, err := os.ReadFile(configPath) + if err != nil { + return nil, errors.Wrapf(err, "can't read config file: %s", configPath) + } + + config := &Config{} + if err = toml.Unmarshal(bz, config); err != nil { + return nil, errors.Wrapf(err, "can't load config file: %s", configPath) + } + + return config, err +} + +func Save(configDir string, config *Config) error { + buf := &bytes.Buffer{} + enc := toml.NewEncoder(buf) + if err := enc.Encode(config); err != nil { + return err + } + + if err := os.MkdirAll(configDir, 0o750); err != nil { + return err + } + + configPath := configFilename(configDir) + if err := os.WriteFile(configPath, buf.Bytes(), 0o600); err != nil { + return err + } + + return nil +} + +func configFilename(configDir string) string { + return path.Join(configDir, "config.toml") +} diff --git a/tools/hubl/internal/flags/flags.go b/tools/hubl/internal/flags/flags.go new file mode 100644 index 0000000000..0379c73bfb --- /dev/null +++ b/tools/hubl/internal/flags/flags.go @@ -0,0 +1,18 @@ +package flags + +const ( + FlagInsecure = "insecure" + FlagUpdate = "update" + FlagConfig = "config" + FlagLong = "long" + FlagOutput = "output" + + FlagKeyringBackend = "keyring-backend" +) + +const ( + OutputFormatText = "text" + OutputFormatJSON = "json" + + DefaultKeyringBackend = "os" +) diff --git a/tools/hubl/internal/keyring.go b/tools/hubl/internal/keyring.go new file mode 100644 index 0000000000..562487c0cd --- /dev/null +++ b/tools/hubl/internal/keyring.go @@ -0,0 +1,109 @@ +package internal + +import ( + "bufio" + "context" + "fmt" + "path" + + "github.com/spf13/cobra" + + _ "cosmossdk.io/api/cosmos/crypto/ed25519" + _ "cosmossdk.io/api/cosmos/crypto/secp256k1" + _ "cosmossdk.io/api/cosmos/crypto/secp256r1" + "cosmossdk.io/tools/hubl/internal/config" + "cosmossdk.io/tools/hubl/internal/flags" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" +) + +func KeyringCmd(chainName string) *cobra.Command { + shortDesc := fmt.Sprintf("Keyring management for %s", chainName) + if chainName == "" { + chainName = config.GlobalKeyringDirName + shortDesc = "Global keyring management for Hubl" + } + + keyringCmd := &cobra.Command{ + Use: "keys", + Short: shortDesc, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + registry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + cdc := codec.NewProtoCodec(registry) + + configDir, err := config.GetConfigDir() + if err != nil { + return err + } + + cfg, err := config.Load(configDir) + if err != nil { + return err + } + + backend, err := cfg.GetKeyringBackend(chainName) + if err != nil { + return err + } + + if changed := cmd.Flags().Changed(flags.FlagKeyringBackend); changed { + b, err := cmd.Flags().GetString(flags.FlagKeyringBackend) + if err != nil { + return err + } + + backend = b + } + + keyringDir := path.Join(configDir, "keyring", chainName) + inBuf := bufio.NewReader(cmd.InOrStdin()) + kr, err := keyring.New(chainName, backend, keyringDir, inBuf, cdc) + if err != nil { + return err + } + + addressCodec, validatorAddressCodec, consensusAddressCodec, err := getAddressCodecFromConfig(cfg, chainName) + if err != nil { + return err + } + + clientCtx := client.Context{}. + WithKeyring(kr). + WithCodec(cdc). + WithKeyringDir(keyringDir). + WithInput(inBuf). + WithAddressCodec(addressCodec). + WithValidatorAddressCodec(validatorAddressCodec). + WithConsensusAddressCodec(consensusAddressCodec) + + cmd.SetContext(context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)) + if err := client.SetCmdClientContext(cmd, clientCtx); err != nil { + return err + } + + return nil + }, + } + + keyringCmd.AddCommand( + keys.AddKeyCommand(), + keys.DeleteKeyCommand(), + keys.ExportKeyCommand(), + keys.ImportKeyCommand(), + keys.ImportKeyHexCommand(), + keys.ListKeysCmd(), + keys.ParseKeyStringCommand(), + keys.RenameKeyCommand(), + keys.ShowKeysCmd(), + ) + keyringCmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test|memory)") + keyringCmd.PersistentFlags().String(flags.FlagOutput, flags.OutputFormatText, "Output format (text|json)") + + return keyringCmd +} diff --git a/tools/hubl/internal/load.go b/tools/hubl/internal/load.go index 6572beafa7..3750d6a951 100644 --- a/tools/hubl/internal/load.go +++ b/tools/hubl/internal/load.go @@ -20,23 +20,22 @@ import ( autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + "cosmossdk.io/tools/hubl/internal/config" ) -const DefaultConfigDirName = ".hubl" - type ChainInfo struct { client *grpc.ClientConn Context context.Context ConfigDir string Chain string - Config *ChainConfig + Config *config.ChainConfig ProtoFiles *protoregistry.Files ModuleOptions map[string]*autocliv1.ModuleOptions } -func NewChainInfo(configDir, chain string, config *ChainConfig) *ChainInfo { +func NewChainInfo(configDir, chain string, config *config.ChainConfig) *ChainInfo { return &ChainInfo{ Context: context.Background(), Config: config, @@ -47,7 +46,7 @@ func NewChainInfo(configDir, chain string, config *ChainConfig) *ChainInfo { func (c *ChainInfo) getCacheDir() (string, error) { cacheDir := path.Join(c.ConfigDir, "cache") - return cacheDir, os.MkdirAll(cacheDir, 0o755) + return cacheDir, os.MkdirAll(cacheDir, 0o750) } func (c *ChainInfo) fdsCacheFilename() (string, error) { diff --git a/tools/hubl/internal/registry.go b/tools/hubl/internal/registry.go index eb170d7319..acbf1ef0c6 100644 --- a/tools/hubl/internal/registry.go +++ b/tools/hubl/internal/registry.go @@ -11,11 +11,9 @@ import ( ) type ChainRegistryEntry struct { - APIs ChainRegistryAPIs `json:"apis"` -} - -type ChainRegistryAPIs struct { - GRPC []*APIEntry `json:"grpc"` + APIs struct { + GRPC []*APIEntry `json:"grpc"` + } `json:"apis"` } type APIEntry struct { diff --git a/tools/hubl/internal/remote.go b/tools/hubl/internal/remote.go index 327d1cd84d..9fdcee4143 100644 --- a/tools/hubl/internal/remote.go +++ b/tools/hubl/internal/remote.go @@ -3,8 +3,6 @@ package internal import ( "context" "fmt" - "os" - "path" "strings" "github.com/spf13/cobra" @@ -15,74 +13,33 @@ import ( "cosmossdk.io/client/v2/autocli" "cosmossdk.io/client/v2/autocli/flag" + "cosmossdk.io/tools/hubl/internal/config" + "cosmossdk.io/tools/hubl/internal/flags" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" - addresscodec "github.com/cosmos/cosmos-sdk/codec/address" ) -var ( - flagInsecure = "insecure" - flagUpdate = "update" - flagConfig = "config" - flagLong = "long" -) - -func RootCommand() (*cobra.Command, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, err - } - - configDir := path.Join(homeDir, DefaultConfigDirName) - config, err := LoadConfig(configDir) - if err != nil { - return nil, err - } - - cmd := &cobra.Command{ - Use: "hubl", - Short: "Hubl is a CLI for interacting with Cosmos SDK chains", - Long: "Hubl is a CLI for interacting with Cosmos SDK chains", - } - - // add commands - commands, err := RemoteCommand(config, configDir) - if err != nil { - return nil, err - } - commands = append( - commands, - InitCommand(config, configDir), - VersionCmd(), - ) - - cmd.AddCommand(commands...) - return cmd, nil -} - -func InitCommand(config *Config, configDir string) *cobra.Command { +func InitCmd(config *config.Config, configDir string) *cobra.Command { var insecure bool cmd := &cobra.Command{ Use: "init [foochain]", Short: "Initialize a new chain", - Long: `To configure a new chain just run this command using the --init flag and the name of the chain as it's listed in the chain registry (https://github.com/cosmos/chain-registry). + Long: `To configure a new chain, run this command using the --init flag and the name of the chain as it's listed in the chain registry (https://github.com/cosmos/chain-registry). If the chain is not listed in the chain registry, you can use any unique name.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { chainName := strings.ToLower(args[0]) - return reconfigure(cmd, config, configDir, chainName) }, } - cmd.Flags().BoolVar(&insecure, flagInsecure, false, "allow setting up insecure gRPC connection") + cmd.Flags().BoolVar(&insecure, flags.FlagInsecure, false, "allow setting up insecure gRPC connection") return cmd } -func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) { +func RemoteCommand(config *config.Config, configDir string) ([]*cobra.Command, error) { commands := []*cobra.Command{} for chain, chainConfig := range config.Chains { @@ -103,11 +60,16 @@ func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) { ModuleOptions: chainInfo.ModuleOptions, } + addressCodec, validatorAddressCodec, consensusAddressCodec, err := getAddressCodecFromConfig(config, chain) + if err != nil { + return nil, err + } + builder := &autocli.Builder{ Builder: flag.Builder{ - AddressCodec: addresscodec.NewBech32Codec(chainConfig.Bech32Prefix), - ValidatorAddressCodec: addresscodec.NewBech32Codec(fmt.Sprintf("%svaloper", chainConfig.Bech32Prefix)), - ConsensusAddressCodec: addresscodec.NewBech32Codec(fmt.Sprintf("%svalcons", chainConfig.Bech32Prefix)), + AddressCodec: addressCodec, + ValidatorAddressCodec: validatorAddressCodec, + ConsensusAddressCodec: consensusAddressCodec, TypeResolver: &dynamicTypeResolver{chainInfo}, FileResolver: chainInfo.ProtoFiles, }, @@ -132,17 +94,20 @@ func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) { case reconfig: return reconfigure(cmd, config, configDir, chain) case update: - cmd.Printf("Updating autocli data for %s\n", chain) + cmd.Printf("Updating AutoCLI data for %s\n", chain) return chainInfo.Load(true) default: return cmd.Help() } }, } - chainCmd.Flags().BoolVar(&update, flagUpdate, false, "update the CLI commands for the selected chain (should be used after every chain upgrade)") - chainCmd.Flags().BoolVar(&reconfig, flagConfig, false, "re-configure the selected chain (allows choosing a new gRPC endpoint and refreshes data") - chainCmd.Flags().BoolVar(&insecure, flagInsecure, false, "allow re-configuring the selected chain using an insecure gRPC connection") - chainCmd.PersistentFlags().StringVar(&output, flags.FlagOutput, flags.OutputFormatJSON, "output format (text|json)") + chainCmd.Flags().BoolVar(&update, flags.FlagUpdate, false, "update the CLI commands for the selected chain (should be used after every chain upgrade)") + chainCmd.Flags().BoolVar(&reconfig, flags.FlagConfig, false, "re-configure the selected chain (allows choosing a new gRPC endpoint and refreshes data") + chainCmd.Flags().BoolVar(&insecure, flags.FlagInsecure, false, "allow re-configuring the selected chain using an insecure gRPC connection") + chainCmd.PersistentFlags().StringVar(&output, flags.FlagOutput, flags.OutputFormatJSON, fmt.Sprintf("output format (%s|%s)", flags.OutputFormatText, flags.OutputFormatJSON)) + + // add chain specific keyring + chainCmd.AddCommand(KeyringCmd(chainInfo.Chain)) if err := appOpts.EnhanceRootCommandWithBuilder(chainCmd, builder); err != nil { return nil, err @@ -154,25 +119,24 @@ func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) { return commands, nil } -func RemoteErrorCommand(config *Config, configDir, chain string, chainConfig *ChainConfig, err error) *cobra.Command { +func RemoteErrorCommand(cfg *config.Config, configDir, chain string, chainConfig *config.ChainConfig, err error) *cobra.Command { cmd := &cobra.Command{ Use: chain, - Short: "Unable to load data", - Long: "Unable to load data, reconfiguration needed.", + Short: fmt.Sprintf("Unable to load %s data", chain), + Long: fmt.Sprintf("Unable to load %s data, reconfiguration needed.", chain), RunE: func(cmd *cobra.Command, args []string) error { cmd.Printf("Error loading chain data for %s: %+v\n", chain, err) - - return reconfigure(cmd, config, configDir, chain) + return reconfigure(cmd, cfg, configDir, chain) }, } - cmd.Flags().Bool(flagInsecure, chainConfig.GRPCEndpoints[0].Insecure, "allow setting up insecure gRPC connection") + cmd.Flags().Bool(flags.FlagInsecure, chainConfig.GRPCEndpoints[0].Insecure, "allow setting up insecure gRPC connection") return cmd } -func reconfigure(cmd *cobra.Command, config *Config, configDir, chain string) error { - insecure, _ := cmd.Flags().GetBool(flagInsecure) +func reconfigure(cmd *cobra.Command, cfg *config.Config, configDir, chain string) error { + insecure, _ := cmd.Flags().GetBool(flags.FlagInsecure) cmd.Printf("Configuring %s\n", chain) endpoint, err := SelectGRPCEndpoints(chain) @@ -181,8 +145,8 @@ func reconfigure(cmd *cobra.Command, config *Config, configDir, chain string) er } cmd.Printf("%s endpoint selected\n", endpoint) - chainConfig := &ChainConfig{ - GRPCEndpoints: []GRPCEndpoint{ + chainConfig := &config.ChainConfig{ + GRPCEndpoints: []config.GRPCEndpoint{ { Endpoint: endpoint, Insecure: insecure, @@ -205,10 +169,11 @@ func reconfigure(cmd *cobra.Command, config *Config, configDir, chain string) er return err } - chainConfig.Bech32Prefix = addressPrefix - config.Chains[chain] = chainConfig + chainConfig.KeyringBackend = flags.DefaultKeyringBackend + chainConfig.AddressPrefix = addressPrefix + cfg.Chains[chain] = chainConfig - if err := SaveConfig(configDir, config); err != nil { + if err := config.Save(configDir, cfg); err != nil { return err } diff --git a/tools/hubl/internal/root.go b/tools/hubl/internal/root.go new file mode 100644 index 0000000000..518b56b5f6 --- /dev/null +++ b/tools/hubl/internal/root.go @@ -0,0 +1,39 @@ +package internal + +import ( + "github.com/spf13/cobra" + + "cosmossdk.io/tools/hubl/internal/config" +) + +func RootCommand() (*cobra.Command, error) { + configDir, err := config.GetConfigDir() + if err != nil { + return nil, err + } + + cfg, err := config.Load(configDir) + if err != nil { + return nil, err + } + + cmd := &cobra.Command{ + Use: "hubl", + Short: "Hubl is a CLI for interacting with Cosmos SDK chains", + } + + // add commands + commands, err := RemoteCommand(cfg, configDir) + if err != nil { + return nil, err + } + commands = append( + commands, + InitCmd(cfg, configDir), + KeyringCmd(""), + VersionCmd(), + ) + + cmd.AddCommand(commands...) + return cmd, nil +} diff --git a/tools/hubl/internal/util.go b/tools/hubl/internal/util.go new file mode 100644 index 0000000000..95e76d2f8d --- /dev/null +++ b/tools/hubl/internal/util.go @@ -0,0 +1,29 @@ +package internal + +import ( + "fmt" + + "cosmossdk.io/core/address" + "cosmossdk.io/tools/hubl/internal/config" + + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" +) + +// getAddressCodecFromConfig returns the address codecs for the given chain name +func getAddressCodecFromConfig(cfg *config.Config, chainName string) (address.Codec, address.Codec, address.Codec, error) { + addressPrefix := "cosmos" + + if chainName != config.GlobalKeyringDirName { + chainConfig, ok := cfg.Chains[chainName] + if !ok { + return nil, nil, nil, fmt.Errorf("chain %s not found in config", chainName) + } + + addressPrefix = chainConfig.AddressPrefix + } + + return addresscodec.NewBech32Codec(addressPrefix), + addresscodec.NewBech32Codec(fmt.Sprintf("%svaloper", addressPrefix)), + addresscodec.NewBech32Codec(fmt.Sprintf("%svalcons", addressPrefix)), + nil +} diff --git a/tools/hubl/internal/version.go b/tools/hubl/internal/version.go index 7b7c4d6807..87a0d57c1d 100644 --- a/tools/hubl/internal/version.go +++ b/tools/hubl/internal/version.go @@ -6,6 +6,8 @@ import ( "strings" "github.com/spf13/cobra" + + "cosmossdk.io/tools/hubl/internal/flags" ) func VersionCmd() *cobra.Command { @@ -34,7 +36,7 @@ func VersionCmd() *cobra.Command { }, } - versionCmd.Flags().BoolVar(&long, flagLong, false, "display long version information") + versionCmd.Flags().BoolVar(&long, flags.FlagLong, false, "display long version information") return versionCmd }