diff --git a/Gopkg.lock b/Gopkg.lock index 06a17d015b6c..68350442d84d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -683,6 +683,7 @@ "github.com/tendermint/tendermint/crypto/ed25519", "github.com/tendermint/tendermint/crypto/encoding/amino", "github.com/tendermint/tendermint/crypto/merkle", + "github.com/tendermint/tendermint/crypto/multisig", "github.com/tendermint/tendermint/crypto/secp256k1", "github.com/tendermint/tendermint/crypto/tmhash", "github.com/tendermint/tendermint/crypto/xsalsa20symmetric", diff --git a/PENDING.md b/PENDING.md index 7ad396b7b449..58a31f82b7cd 100644 --- a/PENDING.md +++ b/PENDING.md @@ -149,6 +149,7 @@ FEATURES * [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced new commission flags for validator commands `create-validator` and `edit-validator`. * [stake][cli] [\#1890](https://github.com/cosmos/cosmos-sdk/issues/1890) Add `--genesis-format` flag to `gaiacli tx create-validator` to produce transactions in genesis-friendly format. + * [cli][\#2554](https://github.com/cosmos/cosmos-sdk/issues/2554) Make `gaiacli keys show` multisig ready. * Gaia * [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address` diff --git a/client/keys/show.go b/client/keys/show.go index 71123e7da77d..89b2f1b39de6 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -2,12 +2,16 @@ package keys import ( "fmt" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/tendermint/tendermint/crypto" "net/http" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/gorilla/mux" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/tendermint/tendermint/crypto/multisig" "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" ) @@ -19,30 +23,68 @@ const ( FlagPublicKey = "pubkey" // FlagBechPrefix defines a desired Bech32 prefix encoding for a key. FlagBechPrefix = "bech" + + flagMultiSigThreshold = "multisig-threshold" + defaultMultiSigKeyName = "multi" ) +var _ keys.Info = (*multiSigKey)(nil) + +type multiSigKey struct { + name string + key crypto.PubKey +} + +func (m multiSigKey) GetName() string { return m.name } +func (m multiSigKey) GetType() keys.KeyType { return keys.TypeLocal } +func (m multiSigKey) GetPubKey() crypto.PubKey { return m.key } +func (m multiSigKey) GetAddress() sdk.AccAddress { return sdk.AccAddress(m.key.Address()) } + func showKeysCmd() *cobra.Command { cmd := &cobra.Command{ Use: "show [name]", Short: "Show key info for the given name", Long: `Return public details of one local key.`, - Args: cobra.ExactArgs(1), + Args: cobra.MinimumNArgs(1), RunE: runShowCmd, } cmd.Flags().String(FlagBechPrefix, "acc", "The Bech32 prefix encoding for a key (acc|val|cons)") cmd.Flags().Bool(FlagAddress, false, "output the address only (overrides --output)") cmd.Flags().Bool(FlagPublicKey, false, "output the public key only (overrides --output)") + cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures") return cmd } -func runShowCmd(cmd *cobra.Command, args []string) error { - name := args[0] +func runShowCmd(cmd *cobra.Command, args []string) (err error) { + var info keys.Info - info, err := GetKeyInfo(name) - if err != nil { - return err + if len(args) == 1 { + info, err = GetKeyInfo(args[0]) + if err != nil { + return err + } + } else { + pks := make([]crypto.PubKey, len(args)) + for i, keyName := range args { + info, err := GetKeyInfo(keyName) + if err != nil { + return err + } + pks[i] = info.GetPubKey() + } + + multisigThreshold := viper.GetInt(flagMultiSigThreshold) + err = validateMultisigThreshold(multisigThreshold, len(args)) + if err != nil { + return err + } + multikey := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks) + info = multiSigKey{ + name: defaultMultiSigKeyName, + key: multikey, + } } isShowAddr := viper.GetBool(FlagAddress) @@ -74,6 +116,17 @@ func runShowCmd(cmd *cobra.Command, args []string) error { return nil } +func validateMultisigThreshold(k, nKeys int) error { + if k <= 0 { + return fmt.Errorf("threshold must be a positive integer") + } + if nKeys < k { + return fmt.Errorf( + "threshold k of n multisignature: %d < %d", nKeys, k) + } + return nil +} + func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) { switch bechPrefix { case "acc": diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 9637b3b4beb8..4703c492185c 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -74,6 +74,16 @@ gaiad tendermint show-validator We strongly recommend _NOT_ using the same passphrase for multiple keys. The Tendermint team and the Interchain Foundation will not be responsible for the loss of funds. ::: +#### Multisig public keys + +You can generate and print a multisig public key by typing: + +```bash +gaiacli show -m K key1 key2...keyK +``` + +`K` is the minimum weight, e.g. minimum number of private keys that must have signed the transactions that carry the generated public key. + ### Account #### Get Tokens