github.com/Finschia/finschia-sdk@v0.48.1/client/cmd.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/Finschia/ostracon/libs/cli"
     8  	"github.com/pkg/errors"
     9  	"github.com/spf13/cobra"
    10  	"github.com/spf13/pflag"
    11  
    12  	"github.com/Finschia/finschia-sdk/client/flags"
    13  	"github.com/Finschia/finschia-sdk/crypto/keyring"
    14  	sdk "github.com/Finschia/finschia-sdk/types"
    15  )
    16  
    17  // ClientContextKey defines the context key used to retrieve a client.Context from
    18  // a command's Context.
    19  const ClientContextKey = sdk.ContextKey("client.context")
    20  
    21  // SetCmdClientContextHandler is to be used in a command pre-hook execution to
    22  // read flags that populate a Context and sets that to the command's Context.
    23  func SetCmdClientContextHandler(clientCtx Context, cmd *cobra.Command) (err error) {
    24  	clientCtx, err = ReadPersistentCommandFlags(clientCtx, cmd.Flags())
    25  	if err != nil {
    26  		return err
    27  	}
    28  
    29  	return SetCmdClientContext(cmd, clientCtx)
    30  }
    31  
    32  // ValidateCmd returns unknown command error or Help display if help flag set
    33  func ValidateCmd(cmd *cobra.Command, args []string) error {
    34  	var unknownCmd string
    35  	var skipNext bool
    36  
    37  	for _, arg := range args {
    38  		// search for help flag
    39  		if arg == "--help" || arg == "-h" {
    40  			return cmd.Help()
    41  		}
    42  
    43  		// check if the current arg is a flag
    44  		switch {
    45  		case len(arg) > 0 && (arg[0] == '-'):
    46  			// the next arg should be skipped if the current arg is a
    47  			// flag and does not use "=" to assign the flag's value
    48  			if !strings.Contains(arg, "=") {
    49  				skipNext = true
    50  			} else {
    51  				skipNext = false
    52  			}
    53  		case skipNext:
    54  			// skip current arg
    55  			skipNext = false
    56  		case unknownCmd == "":
    57  			// unknown command found
    58  			// continue searching for help flag
    59  			unknownCmd = arg
    60  		}
    61  	}
    62  
    63  	// return the help screen if no unknown command is found
    64  	if unknownCmd != "" {
    65  		err := fmt.Sprintf("unknown command \"%s\" for \"%s\"", unknownCmd, cmd.CalledAs())
    66  
    67  		// build suggestions for unknown argument
    68  		if suggestions := cmd.SuggestionsFor(unknownCmd); len(suggestions) > 0 {
    69  			err += "\n\nDid you mean this?\n"
    70  			for _, s := range suggestions {
    71  				err += fmt.Sprintf("\t%v\n", s)
    72  			}
    73  		}
    74  		return errors.New(err)
    75  	}
    76  
    77  	return cmd.Help()
    78  }
    79  
    80  // ReadPersistentCommandFlags returns a Context with fields set for "persistent"
    81  // or common flags that do not necessarily change with context.
    82  //
    83  // Note, the provided clientCtx may have field pre-populated. The following order
    84  // of precedence occurs:
    85  //
    86  // - client.Context field not pre-populated & flag not set: uses default flag value
    87  // - client.Context field not pre-populated & flag set: uses set flag value
    88  // - client.Context field pre-populated & flag not set: uses pre-populated value
    89  // - client.Context field pre-populated & flag set: uses set flag value
    90  func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
    91  	if clientCtx.OutputFormat == "" || flagSet.Changed(cli.OutputFlag) {
    92  		output, _ := flagSet.GetString(cli.OutputFlag)
    93  		clientCtx = clientCtx.WithOutputFormat(output)
    94  	}
    95  
    96  	if clientCtx.HomeDir == "" || flagSet.Changed(flags.FlagHome) {
    97  		homeDir, _ := flagSet.GetString(flags.FlagHome)
    98  		clientCtx = clientCtx.WithHomeDir(homeDir)
    99  	}
   100  
   101  	if !clientCtx.Simulate || flagSet.Changed(flags.FlagDryRun) {
   102  		dryRun, _ := flagSet.GetBool(flags.FlagDryRun)
   103  		clientCtx = clientCtx.WithSimulation(dryRun)
   104  	}
   105  
   106  	if clientCtx.KeyringDir == "" || flagSet.Changed(flags.FlagKeyringDir) {
   107  		keyringDir, _ := flagSet.GetString(flags.FlagKeyringDir)
   108  
   109  		// The keyring directory is optional and falls back to the home directory
   110  		// if omitted.
   111  		if keyringDir == "" {
   112  			keyringDir = clientCtx.HomeDir
   113  		}
   114  
   115  		clientCtx = clientCtx.WithKeyringDir(keyringDir)
   116  	}
   117  
   118  	if clientCtx.ChainID == "" || flagSet.Changed(flags.FlagChainID) {
   119  		chainID, _ := flagSet.GetString(flags.FlagChainID)
   120  		clientCtx = clientCtx.WithChainID(chainID)
   121  	}
   122  
   123  	if clientCtx.Keyring == nil || flagSet.Changed(flags.FlagKeyringBackend) {
   124  		keyringBackend, _ := flagSet.GetString(flags.FlagKeyringBackend)
   125  
   126  		if keyringBackend != "" {
   127  			kr, err := NewKeyringFromBackend(clientCtx, keyringBackend)
   128  			if err != nil {
   129  				return clientCtx, err
   130  			}
   131  
   132  			clientCtx = clientCtx.WithKeyring(kr)
   133  		}
   134  	}
   135  
   136  	if clientCtx.Client == nil || flagSet.Changed(flags.FlagNode) {
   137  		rpcURI, _ := flagSet.GetString(flags.FlagNode)
   138  		if rpcURI != "" {
   139  			clientCtx = clientCtx.WithNodeURI(rpcURI)
   140  
   141  			client, err := NewClientFromNode(rpcURI)
   142  			if err != nil {
   143  				return clientCtx, err
   144  			}
   145  
   146  			clientCtx = clientCtx.WithClient(client)
   147  		}
   148  	}
   149  
   150  	return clientCtx, nil
   151  }
   152  
   153  // readQueryCommandFlags returns an updated Context with fields set based on flags
   154  // defined in AddQueryFlagsToCmd. An error is returned if any flag query fails.
   155  //
   156  // Note, the provided clientCtx may have field pre-populated. The following order
   157  // of precedence occurs:
   158  //
   159  // - client.Context field not pre-populated & flag not set: uses default flag value
   160  // - client.Context field not pre-populated & flag set: uses set flag value
   161  // - client.Context field pre-populated & flag not set: uses pre-populated value
   162  // - client.Context field pre-populated & flag set: uses set flag value
   163  func readQueryCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
   164  	if clientCtx.Height == 0 || flagSet.Changed(flags.FlagHeight) {
   165  		height, _ := flagSet.GetInt64(flags.FlagHeight)
   166  		clientCtx = clientCtx.WithHeight(height)
   167  	}
   168  
   169  	if !clientCtx.UseLedger || flagSet.Changed(flags.FlagUseLedger) {
   170  		useLedger, _ := flagSet.GetBool(flags.FlagUseLedger)
   171  		clientCtx = clientCtx.WithUseLedger(useLedger)
   172  	}
   173  
   174  	return ReadPersistentCommandFlags(clientCtx, flagSet)
   175  }
   176  
   177  // readTxCommandFlags returns an updated Context with fields set based on flags
   178  // defined in AddTxFlagsToCmd. An error is returned if any flag query fails.
   179  //
   180  // Note, the provided clientCtx may have field pre-populated. The following order
   181  // of precedence occurs:
   182  //
   183  // - client.Context field not pre-populated & flag not set: uses default flag value
   184  // - client.Context field not pre-populated & flag set: uses set flag value
   185  // - client.Context field pre-populated & flag not set: uses pre-populated value
   186  // - client.Context field pre-populated & flag set: uses set flag value
   187  func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) {
   188  	clientCtx, err := ReadPersistentCommandFlags(clientCtx, flagSet)
   189  	if err != nil {
   190  		return clientCtx, err
   191  	}
   192  
   193  	if !clientCtx.GenerateOnly || flagSet.Changed(flags.FlagGenerateOnly) {
   194  		genOnly, _ := flagSet.GetBool(flags.FlagGenerateOnly)
   195  		clientCtx = clientCtx.WithGenerateOnly(genOnly)
   196  	}
   197  
   198  	if !clientCtx.Offline || flagSet.Changed(flags.FlagOffline) {
   199  		offline, _ := flagSet.GetBool(flags.FlagOffline)
   200  		clientCtx = clientCtx.WithOffline(offline)
   201  	}
   202  
   203  	if !clientCtx.UseLedger || flagSet.Changed(flags.FlagUseLedger) {
   204  		useLedger, _ := flagSet.GetBool(flags.FlagUseLedger)
   205  		clientCtx = clientCtx.WithUseLedger(useLedger)
   206  	}
   207  
   208  	if clientCtx.BroadcastMode == "" || flagSet.Changed(flags.FlagBroadcastMode) {
   209  		bMode, _ := flagSet.GetString(flags.FlagBroadcastMode)
   210  		clientCtx = clientCtx.WithBroadcastMode(bMode)
   211  	}
   212  
   213  	if !clientCtx.SkipConfirm || flagSet.Changed(flags.FlagSkipConfirmation) {
   214  		skipConfirm, _ := flagSet.GetBool(flags.FlagSkipConfirmation)
   215  		clientCtx = clientCtx.WithSkipConfirmation(skipConfirm)
   216  	}
   217  
   218  	if clientCtx.SignModeStr == "" || flagSet.Changed(flags.FlagSignMode) {
   219  		signModeStr, _ := flagSet.GetString(flags.FlagSignMode)
   220  		clientCtx = clientCtx.WithSignModeStr(signModeStr)
   221  	}
   222  
   223  	if clientCtx.FeeGranter == nil || flagSet.Changed(flags.FlagFeeAccount) {
   224  		granter, _ := flagSet.GetString(flags.FlagFeeAccount)
   225  
   226  		if granter != "" {
   227  			granterAcc, err := sdk.AccAddressFromBech32(granter)
   228  			if err != nil {
   229  				return clientCtx, err
   230  			}
   231  
   232  			clientCtx = clientCtx.WithFeeGranterAddress(granterAcc)
   233  		}
   234  	}
   235  
   236  	if clientCtx.From == "" || flagSet.Changed(flags.FlagFrom) {
   237  		from, _ := flagSet.GetString(flags.FlagFrom)
   238  		fromAddr, fromName, keyType, err := GetFromFields(clientCtx.Keyring, from, clientCtx.GenerateOnly)
   239  		if err != nil {
   240  			return clientCtx, err
   241  		}
   242  
   243  		clientCtx = clientCtx.WithFrom(from).WithFromAddress(fromAddr).WithFromName(fromName)
   244  
   245  		// If the `from` signer account is a ledger key, we need to use
   246  		// SIGN_MODE_AMINO_JSON, because ledger doesn't support proto yet.
   247  		// ref: https://github.com/cosmos/cosmos-sdk/issues/8109
   248  		if keyType == keyring.TypeLedger && clientCtx.SignModeStr != flags.SignModeLegacyAminoJSON {
   249  			fmt.Println("Default sign-mode 'direct' not supported by Ledger, using sign-mode 'amino-json'.")
   250  			clientCtx = clientCtx.WithSignModeStr(flags.SignModeLegacyAminoJSON)
   251  		}
   252  	}
   253  	return clientCtx, nil
   254  }
   255  
   256  // GetClientQueryContext returns a Context from a command with fields set based on flags
   257  // defined in AddQueryFlagsToCmd. An error is returned if any flag query fails.
   258  //
   259  // - client.Context field not pre-populated & flag not set: uses default flag value
   260  // - client.Context field not pre-populated & flag set: uses set flag value
   261  // - client.Context field pre-populated & flag not set: uses pre-populated value
   262  // - client.Context field pre-populated & flag set: uses set flag value
   263  func GetClientQueryContext(cmd *cobra.Command) (Context, error) {
   264  	ctx := GetClientContextFromCmd(cmd)
   265  	return readQueryCommandFlags(ctx, cmd.Flags())
   266  }
   267  
   268  // GetClientTxContext returns a Context from a command with fields set based on flags
   269  // defined in AddTxFlagsToCmd. An error is returned if any flag query fails.
   270  //
   271  // - client.Context field not pre-populated & flag not set: uses default flag value
   272  // - client.Context field not pre-populated & flag set: uses set flag value
   273  // - client.Context field pre-populated & flag not set: uses pre-populated value
   274  // - client.Context field pre-populated & flag set: uses set flag value
   275  func GetClientTxContext(cmd *cobra.Command) (Context, error) {
   276  	ctx := GetClientContextFromCmd(cmd)
   277  	return readTxCommandFlags(ctx, cmd.Flags())
   278  }
   279  
   280  // GetClientContextFromCmd returns a Context from a command or an empty Context
   281  // if it has not been set.
   282  func GetClientContextFromCmd(cmd *cobra.Command) Context {
   283  	if v := cmd.Context().Value(ClientContextKey); v != nil {
   284  		clientCtxPtr := v.(*Context)
   285  		return *clientCtxPtr
   286  	}
   287  
   288  	return Context{}
   289  }
   290  
   291  // SetCmdClientContext sets a command's Context value to the provided argument.
   292  func SetCmdClientContext(cmd *cobra.Command, clientCtx Context) error {
   293  	v := cmd.Context().Value(ClientContextKey)
   294  	if v == nil {
   295  		return errors.New("client context not set")
   296  	}
   297  
   298  	clientCtxPtr := v.(*Context)
   299  	*clientCtxPtr = clientCtx
   300  
   301  	return nil
   302  }