github.com/Finschia/finschia-sdk@v0.48.1/client/keys/show.go (about) 1 package keys 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/Finschia/ostracon/libs/cli" 8 "github.com/spf13/cobra" 9 10 "github.com/Finschia/finschia-sdk/client" 11 "github.com/Finschia/finschia-sdk/crypto/keyring" 12 "github.com/Finschia/finschia-sdk/crypto/keys/multisig" 13 "github.com/Finschia/finschia-sdk/crypto/ledger" 14 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 15 sdk "github.com/Finschia/finschia-sdk/types" 16 sdkerr "github.com/Finschia/finschia-sdk/types/errors" 17 ) 18 19 const ( 20 // FlagAddress is the flag for the user's address on the command line. 21 FlagAddress = "address" 22 // FlagPublicKey represents the user's public key on the command line. 23 FlagPublicKey = "pubkey" 24 // FlagBechPrefix defines a desired Bech32 prefix encoding for a key. 25 FlagBechPrefix = "bech" 26 // FlagDevice indicates that the information should be shown in the device 27 FlagDevice = "device" 28 29 flagMultiSigThreshold = "multisig-threshold" 30 31 defaultMultiSigKeyName = "multi" 32 ) 33 34 // ShowKeysCmd shows key information for a given key name. 35 func ShowKeysCmd() *cobra.Command { 36 cmd := &cobra.Command{ 37 Use: "show [name_or_address [name_or_address...]]", 38 Short: "Retrieve key information by name or address", 39 Long: `Display keys details. If multiple names or addresses are provided, 40 then an ephemeral multisig key will be created under the name "multi" 41 consisting of all the keys provided by name and multisig threshold.`, 42 Args: cobra.MinimumNArgs(1), 43 RunE: runShowCmd, 44 } 45 f := cmd.Flags() 46 f.String(FlagBechPrefix, sdk.PrefixAccount, "The Bech32 prefix encoding for a key (acc|val|cons)") 47 f.BoolP(FlagAddress, "a", false, "Output the address only (overrides --output)") 48 f.BoolP(FlagPublicKey, "p", false, "Output the public key only (overrides --output)") 49 f.BoolP(FlagDevice, "d", false, "Output the address in a ledger device") 50 f.Int(flagMultiSigThreshold, 1, "K out of N required signatures") 51 52 return cmd 53 } 54 55 func runShowCmd(cmd *cobra.Command, args []string) (err error) { 56 var info keyring.Info 57 clientCtx, err := client.GetClientQueryContext(cmd) 58 if err != nil { 59 return err 60 } 61 62 if len(args) == 1 { 63 info, err = fetchKey(clientCtx.Keyring, args[0]) 64 if err != nil { 65 return fmt.Errorf("%s is not a valid name or address: %v", args[0], err) 66 } 67 if info.GetType() == keyring.TypeMulti { 68 info, err = keyring.NewMultiInfo(info.GetName(), info.GetPubKey()) 69 if err != nil { 70 return err 71 } 72 } 73 } else { 74 pks := make([]cryptotypes.PubKey, len(args)) 75 for i, keyref := range args { 76 info, err := fetchKey(clientCtx.Keyring, keyref) 77 if err != nil { 78 return fmt.Errorf("%s is not a valid name or address: %v", keyref, err) 79 } 80 81 pks[i] = info.GetPubKey() 82 } 83 84 multisigThreshold, _ := cmd.Flags().GetInt(flagMultiSigThreshold) 85 err = validateMultisigThreshold(multisigThreshold, len(args)) 86 if err != nil { 87 return err 88 } 89 90 multikey := multisig.NewLegacyAminoPubKey(multisigThreshold, pks) 91 info, err = keyring.NewMultiInfo(defaultMultiSigKeyName, multikey) 92 if err != nil { 93 return err 94 } 95 } 96 97 isShowAddr, _ := cmd.Flags().GetBool(FlagAddress) 98 isShowPubKey, _ := cmd.Flags().GetBool(FlagPublicKey) 99 isShowDevice, _ := cmd.Flags().GetBool(FlagDevice) 100 101 isOutputSet := false 102 tmp := cmd.Flag(cli.OutputFlag) 103 if tmp != nil { 104 isOutputSet = tmp.Changed 105 } 106 107 if isShowAddr && isShowPubKey { 108 return errors.New("cannot use both --address and --pubkey at once") 109 } 110 111 if isOutputSet && (isShowAddr || isShowPubKey) { 112 return errors.New("cannot use --output with --address or --pubkey") 113 } 114 115 bechPrefix, _ := cmd.Flags().GetString(FlagBechPrefix) 116 bechKeyOut, err := getBechKeyOut(bechPrefix) 117 if err != nil { 118 return err 119 } 120 121 if isOutputSet { 122 clientCtx.OutputFormat, _ = cmd.Flags().GetString(cli.OutputFlag) 123 } 124 125 switch { 126 case isShowAddr, isShowPubKey: 127 ko, err := bechKeyOut(info) 128 if err != nil { 129 return err 130 } 131 out := ko.Address 132 if isShowPubKey { 133 out = ko.PubKey 134 } 135 fmt.Fprintln(cmd.OutOrStdout(), out) 136 default: 137 printKeyInfo(cmd.OutOrStdout(), info, bechKeyOut, clientCtx.OutputFormat) 138 } 139 140 if isShowDevice { 141 if isShowPubKey { 142 return fmt.Errorf("the device flag (-d) can only be used for addresses not pubkeys") 143 } 144 if bechPrefix != "acc" { 145 return fmt.Errorf("the device flag (-d) can only be used for accounts") 146 } 147 148 // Override and show in the device 149 if info.GetType() != keyring.TypeLedger { 150 return fmt.Errorf("the device flag (-d) can only be used for accounts stored in devices") 151 } 152 153 hdpath, err := info.GetPath() 154 if err != nil { 155 return nil 156 } 157 158 return ledger.ShowAddress(*hdpath, info.GetPubKey(), sdk.GetConfig().GetBech32AccountAddrPrefix()) 159 } 160 161 return nil 162 } 163 164 func fetchKey(kb keyring.Keyring, keyref string) (keyring.Info, error) { 165 // firstly check if the keyref is a key name of a key registered in a keyring. 166 info, err := kb.Key(keyref) 167 // if the key is not there or if we have a problem with a keyring itself then we move to a 168 // fallback: searching for key by address. 169 if err == nil || !sdkerr.IsOf(err, sdkerr.ErrIO, sdkerr.ErrKeyNotFound) { 170 return info, err 171 } 172 accAddr, err := sdk.AccAddressFromBech32(keyref) 173 if err != nil { 174 return info, err 175 } 176 177 info, err = kb.KeyByAddress(accAddr) 178 return info, sdkerr.Wrap(err, "Invalid key") 179 } 180 181 func validateMultisigThreshold(k, nKeys int) error { 182 if k <= 0 { 183 return fmt.Errorf("threshold must be a positive integer") 184 } 185 if nKeys < k { 186 return fmt.Errorf( 187 "threshold k of n multisignature: %d < %d", nKeys, k) 188 } 189 return nil 190 } 191 192 func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) { 193 switch bechPrefix { 194 case sdk.PrefixAccount: 195 return keyring.MkAccKeyOutput, nil 196 case sdk.PrefixValidator: 197 return keyring.MkValKeyOutput, nil 198 case sdk.PrefixConsensus: 199 return keyring.MkConsKeyOutput, nil 200 } 201 202 return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix) 203 }