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 }