diff --git a/cmd/horcrux/cmd/config.go b/cmd/horcrux/cmd/config.go index 734ea10e..dfaa87b1 100644 --- a/cmd/horcrux/cmd/config.go +++ b/cmd/horcrux/cmd/config.go @@ -17,26 +17,18 @@ import ( "gopkg.in/yaml.v2" ) -func init() { - nodesCmd.AddCommand(addNodesCmd()) - nodesCmd.AddCommand(removeNodesCmd()) - configCmd.AddCommand(nodesCmd) - - peersCmd.AddCommand(addPeersCmd()) - peersCmd.AddCommand(removePeersCmd()) - peersCmd.AddCommand(setSharesCmd()) - configCmd.AddCommand(peersCmd) - - chainIDCmd.AddCommand(setChainIDCmd()) - configCmd.AddCommand(chainIDCmd) +func configCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "config", + Short: "Commands to configure the horcrux signer", + } - configCmd.AddCommand(initCmd()) - rootCmd.AddCommand(configCmd) -} + cmd.AddCommand(nodesCmd()) + cmd.AddCommand(peersCmd()) + cmd.AddCommand(chainIDCmd()) + cmd.AddCommand(initCmd()) -var configCmd = &cobra.Command{ - Use: "config", - Short: "Commands to configure the horcrux signer", + return cmd } func initCmd() *cobra.Command { @@ -222,9 +214,16 @@ func validateCosignerConfig(cfg DiskConfig) error { return nil } -var nodesCmd = &cobra.Command{ - Use: "nodes", - Short: "Commands to configure the chain nodes", +func nodesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "nodes", + Short: "Commands to configure the chain nodes", + } + + cmd.AddCommand(addNodesCmd()) + cmd.AddCommand(removeNodesCmd()) + + return cmd } func addNodesCmd() *cobra.Command { @@ -315,9 +314,17 @@ func diffSetChainNode(setA, setB []ChainNode) (diff []ChainNode) { return } -var peersCmd = &cobra.Command{ - Use: "peers", - Short: "Commands to configure the peer nodes", +func peersCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "peers", + Short: "Commands to configure the peer nodes", + } + + cmd.AddCommand(addPeersCmd()) + cmd.AddCommand(removePeersCmd()) + cmd.AddCommand(setSharesCmd()) + + return cmd } func addPeersCmd() *cobra.Command { @@ -450,9 +457,15 @@ func diffSetCosignerPeer(setA, setB []CosignerPeer) (diff []CosignerPeer) { return } -var chainIDCmd = &cobra.Command{ - Use: "chain-id", - Short: "Commands to configure the chain ID", +func chainIDCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "chain-id", + Short: "Commands to configure the chain ID", + } + + cmd.AddCommand(setChainIDCmd()) + + return cmd } func setChainIDCmd() *cobra.Command { diff --git a/cmd/horcrux/cmd/config_test.go b/cmd/horcrux/cmd/config_test.go index eea3952a..ea0c61bb 100644 --- a/cmd/horcrux/cmd/config_test.go +++ b/cmd/horcrux/cmd/config_test.go @@ -71,13 +71,14 @@ func TestConfigInitCmd(t *testing.T) { t.Run(tc.name, func(t *testing.T) { tmpConfig := filepath.Join(tc.home, ".horcrux") - t.Setenv("HOME", tc.home) err := os.MkdirAll(tc.home, 0777) require.NoError(t, err) - cmd := initCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) - cmd.SetArgs(tc.args) + args := []string{"--home", tmpConfig, "config", "init"} + args = append(args, tc.args...) + cmd.SetArgs(args) err = cmd.Execute() if tc.expectErr { @@ -108,11 +109,13 @@ func TestConfigInitCmd(t *testing.T) { } func TestConfigChainIDSetCmd(t *testing.T) { - t.Setenv("HOME", t.TempDir()) + tmpConfig := filepath.Join(t.TempDir(), ".horcrux") - cmd := initCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) cmd.SetArgs([]string{ + "--home", tmpConfig, + "config", "init", chainID, "tcp://10.168.0.1:1234", "-c", @@ -159,11 +162,13 @@ func TestConfigChainIDSetCmd(t *testing.T) { } func TestConfigNodesAddAndRemove(t *testing.T) { - t.Setenv("HOME", t.TempDir()) + tmpConfig := filepath.Join(t.TempDir(), ".horcrux") - cmd := initCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) cmd.SetArgs([]string{ + "--home", tmpConfig, + "config", "init", chainID, "tcp://10.168.0.1:1234", "-c", @@ -296,11 +301,13 @@ func TestConfigNodesAddAndRemove(t *testing.T) { } func TestConfigPeersAddAndRemove(t *testing.T) { - t.Setenv("HOME", t.TempDir()) + tmpConfig := filepath.Join(t.TempDir(), ".horcrux") - cmd := initCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) cmd.SetArgs([]string{ + "--home", tmpConfig, + "config", "init", chainID, "tcp://10.168.0.1:1234", "-c", @@ -566,11 +573,13 @@ func TestDiffSetCosignerPeer(t *testing.T) { } func TestSetShares(t *testing.T) { - t.Setenv("HOME", t.TempDir()) + tmpConfig := filepath.Join(t.TempDir(), ".horcrux") - cmd := initCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) cmd.SetArgs([]string{ + "--home", tmpConfig, + "config", "init", chainID, "tcp://10.168.0.1:1234", "-c", diff --git a/cmd/horcrux/cmd/cosigner.go b/cmd/horcrux/cmd/cosigner.go index 07ecf25b..07df2648 100644 --- a/cmd/horcrux/cmd/cosigner.go +++ b/cmd/horcrux/cmd/cosigner.go @@ -18,15 +18,16 @@ import ( "github.com/tendermint/tendermint/types" ) -func init() { - cosignerCmd.AddCommand(StartCosignerCmd()) - cosignerCmd.AddCommand(AddressCmd()) - rootCmd.AddCommand(cosignerCmd) -} +func cosignerCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "cosigner", + Short: "Threshold mpc signer for TM based nodes", + } -var cosignerCmd = &cobra.Command{ - Use: "cosigner", - Short: "Threshold mpc signer for TM based nodes", + cmd.AddCommand(startCosignerCmd()) + cmd.AddCommand(addressCmd()) + + return cmd } type AddressCmdOutput struct { @@ -36,7 +37,7 @@ type AddressCmdOutput struct { ValConsPubAddress string } -func AddressCmd() *cobra.Command { +func addressCmd() *cobra.Command { cmd := &cobra.Command{ Use: "address [bech32]", Short: "Get public key hex address and valcons address", @@ -98,7 +99,7 @@ func AddressCmd() *cobra.Command { return cmd } -func StartCosignerCmd() *cobra.Command { +func startCosignerCmd() *cobra.Command { cmd := &cobra.Command{ Use: "start", Short: "Start cosigner process", diff --git a/cmd/horcrux/cmd/key2shares.go b/cmd/horcrux/cmd/key2shares.go index 25226f10..6eff9d5f 100644 --- a/cmd/horcrux/cmd/key2shares.go +++ b/cmd/horcrux/cmd/key2shares.go @@ -24,12 +24,8 @@ import ( "github.com/tendermint/tendermint/libs/os" ) -func init() { - rootCmd.AddCommand(CreateCosignerSharesCmd()) -} - // CreateCosignerSharesCmd is a cobra command for creating cosigner shares from a priv validator -func CreateCosignerSharesCmd() *cobra.Command { +func createCosignerSharesCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create-shares [priv_validator.json] [threshold] [shares]", Aliases: []string{"shard", "shares"}, diff --git a/cmd/horcrux/cmd/key2shares_test.go b/cmd/horcrux/cmd/key2shares_test.go index 4853a163..4c0d9f77 100644 --- a/cmd/horcrux/cmd/key2shares_test.go +++ b/cmd/horcrux/cmd/key2shares_test.go @@ -57,7 +57,7 @@ func TestKey2Shares(t *testing.T) { for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - cmd := CreateCosignerSharesCmd() + cmd := createCosignerSharesCmd() cmd.SetOutput(io.Discard) cmd.SetArgs(tc.args) err := cmd.Execute() diff --git a/cmd/horcrux/cmd/leader_election.go b/cmd/horcrux/cmd/leader_election.go index 68163f6b..586c4ceb 100644 --- a/cmd/horcrux/cmd/leader_election.go +++ b/cmd/horcrux/cmd/leader_election.go @@ -15,127 +15,127 @@ import ( "google.golang.org/grpc/credentials/insecure" ) -func init() { - rootCmd.AddCommand(leaderElectionCmd) - rootCmd.AddCommand(getLeaderCmd) -} - -var leaderElectionCmd = &cobra.Command{ - Use: "elect [node_id]", - Short: "Elect new raft leader", - Long: `To choose the next eligible leader, pass no argument. +func leaderElectionCmd() *cobra.Command { + return &cobra.Command{ + Use: "elect [node_id]", + Short: "Elect new raft leader", + Long: `To choose the next eligible leader, pass no argument. To choose a specific leader, pass that leader's ID as an argument. `, - Args: cobra.RangeArgs(0, 1), - Example: `horcrux elect # elect next eligible leader + Args: cobra.RangeArgs(0, 1), + Example: `horcrux elect # elect next eligible leader horcrux elect 2 # elect specific leader`, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) (err error) { - if config.Config.CosignerConfig == nil { - return fmt.Errorf("cosigner configuration is not present in config file") - } - - if len(config.Config.CosignerConfig.Peers) == 0 { - return fmt.Errorf("cosigner configuration has no peers") - } - - serviceConfig := `{"healthCheckConfig": {"serviceName": "Leader"}, "loadBalancingConfig": [ { "round_robin": {} } ]}` - retryOpts := []grpc_retry.CallOption{ - grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100 * time.Millisecond)), - grpc_retry.WithMax(5), - } - - grpcAddress, err := config.Config.CosignerConfig.LeaderElectMultiAddress() - if err != nil { - return err - } - - fmt.Printf("Broadcasting to address: %s\n", grpcAddress) - conn, err := grpc.Dial(grpcAddress, - grpc.WithDefaultServiceConfig(serviceConfig), grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), - grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))) - if err != nil { - log.Fatalf("dialing failed: %v", err) - } - defer conn.Close() - - leaderID := "" - - if len(args) > 0 { - leaderID = args[0] - } - - ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) - defer cancelFunc() - - grpcClient := proto.NewCosignerGRPCClient(conn) - _, err = grpcClient.TransferLeadership( - ctx, - &proto.CosignerGRPCTransferLeadershipRequest{LeaderID: leaderID}, - ) - if err != nil { - return err - } - - res, err := grpcClient.GetLeader(ctx, &proto.CosignerGRPCGetLeaderRequest{}) - if err != nil { - return err - } - - fmt.Printf("Leader election successful. New leader: %s\n", res.Leader) - - return nil - }, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + if config.Config.CosignerConfig == nil { + return fmt.Errorf("cosigner configuration is not present in config file") + } + + if len(config.Config.CosignerConfig.Peers) == 0 { + return fmt.Errorf("cosigner configuration has no peers") + } + + serviceConfig := `{"healthCheckConfig": {"serviceName": "Leader"}, "loadBalancingConfig": [ { "round_robin": {} } ]}` + retryOpts := []grpc_retry.CallOption{ + grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100 * time.Millisecond)), + grpc_retry.WithMax(5), + } + + grpcAddress, err := config.Config.CosignerConfig.LeaderElectMultiAddress() + if err != nil { + return err + } + + fmt.Printf("Broadcasting to address: %s\n", grpcAddress) + conn, err := grpc.Dial(grpcAddress, + grpc.WithDefaultServiceConfig(serviceConfig), grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), + grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))) + if err != nil { + log.Fatalf("dialing failed: %v", err) + } + defer conn.Close() + + leaderID := "" + + if len(args) > 0 { + leaderID = args[0] + } + + ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelFunc() + + grpcClient := proto.NewCosignerGRPCClient(conn) + _, err = grpcClient.TransferLeadership( + ctx, + &proto.CosignerGRPCTransferLeadershipRequest{LeaderID: leaderID}, + ) + if err != nil { + return err + } + + res, err := grpcClient.GetLeader(ctx, &proto.CosignerGRPCGetLeaderRequest{}) + if err != nil { + return err + } + + fmt.Printf("Leader election successful. New leader: %s\n", res.Leader) + + return nil + }, + } } -var getLeaderCmd = &cobra.Command{ - Use: "leader", - Short: "Get current raft leader", - Args: cobra.NoArgs, - Example: `horcrux leader`, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) (err error) { - if config.Config.CosignerConfig == nil { - return fmt.Errorf("cosigner configuration is not present in config file") - } - - if len(config.Config.CosignerConfig.Peers) == 0 { - return fmt.Errorf("cosigner configuration has no peers") - } - - retryOpts := []grpc_retry.CallOption{ - grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100 * time.Millisecond)), - grpc_retry.WithMax(5), - } - - grpcAddress, err := client.SanitizeAddress(config.Config.CosignerConfig.P2PListen) - if err != nil { - return err - } - - fmt.Printf("Request address: %s\n", grpcAddress) - conn, err := grpc.Dial(grpcAddress, - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), - grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))) - if err != nil { - log.Fatalf("dialing failed: %v", err) - } - defer conn.Close() - - ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) - defer cancelFunc() - - grpcClient := proto.NewCosignerGRPCClient(conn) - - res, err := grpcClient.GetLeader(ctx, &proto.CosignerGRPCGetLeaderRequest{}) - if err != nil { - return err - } - - fmt.Printf("Current leader: %s\n", res.Leader) - - return nil - }, +func getLeaderCmd() *cobra.Command { + return &cobra.Command{ + Use: "leader", + Short: "Get current raft leader", + Args: cobra.NoArgs, + Example: `horcrux leader`, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + if config.Config.CosignerConfig == nil { + return fmt.Errorf("cosigner configuration is not present in config file") + } + + if len(config.Config.CosignerConfig.Peers) == 0 { + return fmt.Errorf("cosigner configuration has no peers") + } + + retryOpts := []grpc_retry.CallOption{ + grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100 * time.Millisecond)), + grpc_retry.WithMax(5), + } + + grpcAddress, err := client.SanitizeAddress(config.Config.CosignerConfig.P2PListen) + if err != nil { + return err + } + + fmt.Printf("Request address: %s\n", grpcAddress) + conn, err := grpc.Dial(grpcAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), + grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))) + if err != nil { + log.Fatalf("dialing failed: %v", err) + } + defer conn.Close() + + ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelFunc() + + grpcClient := proto.NewCosignerGRPCClient(conn) + + res, err := grpcClient.GetLeader(ctx, &proto.CosignerGRPCGetLeaderRequest{}) + if err != nil { + return err + } + + fmt.Printf("Current leader: %s\n", res.Leader) + + return nil + }, + } + } diff --git a/cmd/horcrux/cmd/root.go b/cmd/horcrux/cmd/root.go index 9024058b..23c323b1 100644 --- a/cmd/horcrux/cmd/root.go +++ b/cmd/horcrux/cmd/root.go @@ -11,20 +11,37 @@ import ( "gopkg.in/yaml.v2" ) -var ( - homeDir string - config RuntimeConfig -) +var config RuntimeConfig + +func rootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "horcrux", + Short: "A tendermint remote signer with both single signer and threshold signer modes", + } + + cmd.AddCommand(configCmd()) + cmd.AddCommand(cosignerCmd()) + cmd.AddCommand(createCosignerSharesCmd()) + cmd.AddCommand(leaderElectionCmd()) + cmd.AddCommand(getLeaderCmd()) + cmd.AddCommand(signerCmd()) + cmd.AddCommand(stateCmd()) + cmd.AddCommand(versionCmd()) + + cmd.PersistentFlags().StringVar( + &config.HomeDir, + "home", + "", + "Directory for config and data (default is $HOME/.horcrux)", + ) -var rootCmd = &cobra.Command{ - Use: "horcrux", - Short: "A tendermint remote signer with both single signer and threshold signer modes", + return cmd } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { - if err := rootCmd.Execute(); err != nil { + if err := rootCmd().Execute(); err != nil { // Cobra will print the error os.Exit(1) } @@ -32,18 +49,17 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) - rootCmd.PersistentFlags().StringVar(&homeDir, "home", "", "Directory for config and data (default is $HOME/.horcrux)") } // initConfig reads in config file and ENV variables if set. func initConfig() { var home string - if homeDir == "" { + if config.HomeDir == "" { userHome, err := homedir.Dir() handleInitError(err) home = filepath.Join(userHome, ".horcrux") } else { - home = homeDir + home = config.HomeDir } config = RuntimeConfig{ HomeDir: home, diff --git a/cmd/horcrux/cmd/signer.go b/cmd/horcrux/cmd/signer.go index d8023e47..7b1ebd07 100644 --- a/cmd/horcrux/cmd/signer.go +++ b/cmd/horcrux/cmd/signer.go @@ -12,17 +12,17 @@ import ( "github.com/tendermint/tendermint/types" ) -func init() { - signerCmd.AddCommand(StartSignerCmd()) - rootCmd.AddCommand(signerCmd) -} +func signerCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "signer", + Short: "Remote tx signer for TM based nodes.", + } + cmd.AddCommand(startSignerCmd()) -var signerCmd = &cobra.Command{ - Use: "signer", - Short: "Remote tx signer for TM based nodes.", + return cmd } -func StartSignerCmd() *cobra.Command { +func startSignerCmd() *cobra.Command { cmd := &cobra.Command{ Use: "start", Short: "Start single signer process", diff --git a/cmd/horcrux/cmd/state.go b/cmd/horcrux/cmd/state.go index b7b4c37c..64c0f27a 100644 --- a/cmd/horcrux/cmd/state.go +++ b/cmd/horcrux/cmd/state.go @@ -23,17 +23,17 @@ type FilePVLastSignState struct { Step int8 `json:"step"` } -func init() { - stateCmd.AddCommand(showStateCmd()) - stateCmd.AddCommand(setStateCmd()) - stateCmd.AddCommand(importStateCmd()) +func stateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "state", + Short: "Commands to configure the horcrux signer's state", + } - rootCmd.AddCommand(stateCmd) -} + cmd.AddCommand(showStateCmd()) + cmd.AddCommand(setStateCmd()) + cmd.AddCommand(importStateCmd()) -var stateCmd = &cobra.Command{ - Use: "state", - Short: "Commands to configure the horcrux signer's state", + return cmd } func showStateCmd() *cobra.Command { diff --git a/cmd/horcrux/cmd/state_test.go b/cmd/horcrux/cmd/state_test.go index 053e549e..754640f6 100644 --- a/cmd/horcrux/cmd/state_test.go +++ b/cmd/horcrux/cmd/state_test.go @@ -16,11 +16,11 @@ func TestStateSetCmd(t *testing.T) { tmpConfig := filepath.Join(tmpHome, ".horcrux") chainid := "horcrux-1" - t.Setenv("HOME", tmpHome) - - cmd := initCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) cmd.SetArgs([]string{ + "--home", tmpConfig, + "config", "init", chainid, "tcp://10.168.0.1:1234", "-c", diff --git a/cmd/horcrux/cmd/version.go b/cmd/horcrux/cmd/version.go index a99b05d1..f53834a6 100644 --- a/cmd/horcrux/cmd/version.go +++ b/cmd/horcrux/cmd/version.go @@ -35,10 +35,6 @@ var ( TMVersion = "" ) -func init() { - rootCmd.AddCommand(versionCmd) -} - // Info defines the application version information. type Info struct { Version string `json:"version" yaml:"version"` @@ -67,16 +63,18 @@ func NewInfo() Info { } // versionCmd represents the version command -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Version information for horcrux", - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) error { - bz, err := json.MarshalIndent(NewInfo(), "", " ") - if err != nil { - return err - } - cmd.Println(string(bz)) - return nil - }, +func versionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Version information for horcrux", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + bz, err := json.MarshalIndent(NewInfo(), "", " ") + if err != nil { + return err + } + cmd.Println(string(bz)) + return nil + }, + } }