github.com/cosmos/cosmos-sdk@v0.50.10/client/keys/show.go (about)

     1  package keys
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/spf13/cobra"
     8  
     9  	errorsmod "cosmossdk.io/errors"
    10  
    11  	"github.com/cosmos/cosmos-sdk/client"
    12  	"github.com/cosmos/cosmos-sdk/client/flags"
    13  	"github.com/cosmos/cosmos-sdk/crypto/keyring"
    14  	"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
    15  	"github.com/cosmos/cosmos-sdk/crypto/ledger"
    16  	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    17  	sdk "github.com/cosmos/cosmos-sdk/types"
    18  	sdkerr "github.com/cosmos/cosmos-sdk/types/errors"
    19  )
    20  
    21  const (
    22  	// FlagAddress is the flag for the user's address on the command line.
    23  	FlagAddress = "address"
    24  	// FlagPublicKey represents the user's public key on the command line.
    25  	FlagPublicKey = "pubkey"
    26  	// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
    27  	FlagBechPrefix = "bech"
    28  	// FlagDevice indicates that the information should be shown in the device
    29  	FlagDevice = "device"
    30  
    31  	flagMultiSigThreshold = "multisig-threshold"
    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 (cannot be used with --output)")
    48  	f.BoolP(FlagPublicKey, "p", false, "Output the public key only (cannot be used with --output)")
    49  	f.BoolP(FlagDevice, "d", false, "Output the address in a ledger device (cannot be used with --pubkey)")
    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  	k := new(keyring.Record)
    57  	clientCtx, err := client.GetClientQueryContext(cmd)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	outputFormat := clientCtx.OutputFormat
    62  
    63  	if len(args) == 1 {
    64  		k, err = fetchKey(clientCtx.Keyring, args[0])
    65  		if err != nil {
    66  			return fmt.Errorf("%s is not a valid name or address: %v", args[0], err)
    67  		}
    68  	} else {
    69  		pks := make([]cryptotypes.PubKey, len(args))
    70  		for i, keyref := range args {
    71  			k, err := fetchKey(clientCtx.Keyring, keyref)
    72  			if err != nil {
    73  				return fmt.Errorf("%s is not a valid name or address: %v", keyref, err)
    74  			}
    75  			key, err := k.GetPubKey()
    76  			if err != nil {
    77  				return err
    78  			}
    79  			pks[i] = key
    80  		}
    81  
    82  		multisigThreshold, _ := cmd.Flags().GetInt(flagMultiSigThreshold)
    83  
    84  		if err := validateMultisigThreshold(multisigThreshold, len(args)); err != nil {
    85  			return err
    86  		}
    87  
    88  		multikey := multisig.NewLegacyAminoPubKey(multisigThreshold, pks)
    89  		k, err = keyring.NewMultiRecord(k.Name, multikey)
    90  		if err != nil {
    91  			return err
    92  		}
    93  	}
    94  
    95  	isShowAddr, _ := cmd.Flags().GetBool(FlagAddress)
    96  	isShowPubKey, _ := cmd.Flags().GetBool(FlagPublicKey)
    97  	isShowDevice, _ := cmd.Flags().GetBool(FlagDevice)
    98  
    99  	isOutputSet := false
   100  	tmp := cmd.Flag(flags.FlagOutput)
   101  	if tmp != nil {
   102  		isOutputSet = tmp.Changed
   103  	}
   104  
   105  	if isShowAddr && isShowPubKey {
   106  		return errors.New("cannot use both --address and --pubkey at once")
   107  	}
   108  
   109  	if isOutputSet && (isShowAddr || isShowPubKey) {
   110  		return errors.New("cannot use --output with --address or --pubkey")
   111  	}
   112  
   113  	bechPrefix, _ := cmd.Flags().GetString(FlagBechPrefix)
   114  	bechKeyOut, err := getBechKeyOut(bechPrefix)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	if isOutputSet {
   120  		clientCtx.OutputFormat, _ = cmd.Flags().GetString(flags.FlagOutput)
   121  	}
   122  
   123  	switch {
   124  	case isShowAddr, isShowPubKey:
   125  		ko, err := bechKeyOut(k)
   126  		if err != nil {
   127  			return err
   128  		}
   129  		out := ko.Address
   130  		if isShowPubKey {
   131  			out = ko.PubKey
   132  		}
   133  
   134  		if _, err := fmt.Fprintln(cmd.OutOrStdout(), out); err != nil {
   135  			return err
   136  		}
   137  	default:
   138  		if err := printKeyringRecord(cmd.OutOrStdout(), k, bechKeyOut, outputFormat); err != nil {
   139  			return err
   140  		}
   141  	}
   142  
   143  	if isShowDevice {
   144  		if isShowPubKey {
   145  			return fmt.Errorf("the device flag (-d) can only be used for addresses not pubkeys")
   146  		}
   147  		if bechPrefix != "acc" {
   148  			return fmt.Errorf("the device flag (-d) can only be used for accounts")
   149  		}
   150  
   151  		// Override and show in the device
   152  		if k.GetType() != keyring.TypeLedger {
   153  			return fmt.Errorf("the device flag (-d) can only be used for accounts stored in devices")
   154  		}
   155  
   156  		ledgerItem := k.GetLedger()
   157  		if ledgerItem == nil {
   158  			return errors.New("unable to get ledger item")
   159  		}
   160  
   161  		pk, err := k.GetPubKey()
   162  		if err != nil {
   163  			return err
   164  		}
   165  
   166  		return ledger.ShowAddress(*ledgerItem.Path, pk, sdk.GetConfig().GetBech32AccountAddrPrefix())
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func fetchKey(kb keyring.Keyring, keyref string) (*keyring.Record, error) {
   173  	// firstly check if the keyref is a key name of a key registered in a keyring.
   174  	k, err := kb.Key(keyref)
   175  	// if the key is not there or if we have a problem with a keyring itself then we move to a
   176  	// fallback: searching for key by address.
   177  
   178  	if err == nil || !errorsmod.IsOf(err, sdkerr.ErrIO, sdkerr.ErrKeyNotFound) {
   179  		return k, err
   180  	}
   181  
   182  	accAddr, err := sdk.AccAddressFromBech32(keyref)
   183  	if err != nil {
   184  		return k, err
   185  	}
   186  
   187  	k, err = kb.KeyByAddress(accAddr)
   188  	return k, errorsmod.Wrap(err, "Invalid key")
   189  }
   190  
   191  func validateMultisigThreshold(k, nKeys int) error {
   192  	if k <= 0 {
   193  		return fmt.Errorf("threshold must be a positive integer")
   194  	}
   195  	if nKeys < k {
   196  		return fmt.Errorf(
   197  			"threshold k of n multisignature: %d < %d", nKeys, k)
   198  	}
   199  	return nil
   200  }
   201  
   202  func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
   203  	switch bechPrefix {
   204  	case sdk.PrefixAccount:
   205  		return MkAccKeyOutput, nil
   206  	case sdk.PrefixValidator:
   207  		return MkValKeyOutput, nil
   208  	case sdk.PrefixConsensus:
   209  		return MkConsKeyOutput, nil
   210  	}
   211  
   212  	return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
   213  }