Merge PR #3748: Multisig Display UX Improvements
This commit is contained in:
committed by
Jack Zampolin
parent
c2aecb8b0e
commit
47a44fb580
+3
-3
@@ -142,7 +142,7 @@ func runAddCmd(_ *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks)
|
||||
if _, err := kb.CreateOffline(name, pk); err != nil {
|
||||
if _, err := kb.CreateMulti(name, pk); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error {
|
||||
switch output {
|
||||
case OutputFormatText:
|
||||
fmt.Fprintln(os.Stderr)
|
||||
printKeyInfo(info, Bech32KeyOutput)
|
||||
printKeyInfo(info, keys.Bech32KeyOutput)
|
||||
|
||||
// print mnemonic unless requested not to.
|
||||
if showMnemonic {
|
||||
@@ -273,7 +273,7 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error {
|
||||
fmt.Fprintln(os.Stderr, mnemonic)
|
||||
}
|
||||
case OutputFormatJSON:
|
||||
out, err := Bech32KeyOutput(info)
|
||||
out, err := keys.Bech32KeyOutput(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+15
-13
@@ -6,35 +6,37 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
)
|
||||
|
||||
type testCases struct {
|
||||
Keys []KeyOutput
|
||||
Answers []KeyOutput
|
||||
Keys []keys.KeyOutput
|
||||
Answers []keys.KeyOutput
|
||||
JSON [][]byte
|
||||
}
|
||||
|
||||
func getTestCases() testCases {
|
||||
return testCases{
|
||||
[]KeyOutput{
|
||||
{"A", "B", "C", "D", "E"},
|
||||
{"A", "B", "C", "D", ""},
|
||||
{"", "B", "C", "D", ""},
|
||||
{"", "", "", "", ""},
|
||||
[]keys.KeyOutput{
|
||||
{"A", "B", "C", "D", "E", 0, nil},
|
||||
{"A", "B", "C", "D", "", 0, nil},
|
||||
{"", "B", "C", "D", "", 0, nil},
|
||||
{"", "", "", "", "", 0, nil},
|
||||
},
|
||||
make([]KeyOutput, 4),
|
||||
make([]keys.KeyOutput, 4),
|
||||
[][]byte{
|
||||
[]byte(`{"name":"A","type":"B","address":"C","pub_key":"D","mnemonic":"E"}`),
|
||||
[]byte(`{"name":"A","type":"B","address":"C","pub_key":"D"}`),
|
||||
[]byte(`{"name":"","type":"B","address":"C","pub_key":"D"}`),
|
||||
[]byte(`{"name":"","type":"","address":"","pub_key":""}`),
|
||||
[]byte(`{"name":"A","type":"B","address":"C","pubkey":"D","mnemonic":"E"}`),
|
||||
[]byte(`{"name":"A","type":"B","address":"C","pubkey":"D"}`),
|
||||
[]byte(`{"name":"","type":"B","address":"C","pubkey":"D"}`),
|
||||
[]byte(`{"name":"","type":"","address":"","pubkey":""}`),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalJSON(t *testing.T) {
|
||||
type args struct {
|
||||
o KeyOutput
|
||||
o keys.KeyOutput
|
||||
}
|
||||
|
||||
data := getTestCases()
|
||||
|
||||
+22
-31
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -27,39 +26,29 @@ const (
|
||||
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
|
||||
FlagDevice = "device"
|
||||
|
||||
flagMultiSigThreshold = "multisig-threshold"
|
||||
flagMultiSigThreshold = "multisig-threshold"
|
||||
flagShowMultiSig = "show-multisig"
|
||||
|
||||
defaultMultiSigKeyName = "multi"
|
||||
)
|
||||
|
||||
var _ keys.Info = (*multiSigKey)(nil)
|
||||
|
||||
type multiSigKey struct {
|
||||
name string
|
||||
key tmcrypto.PubKey
|
||||
}
|
||||
|
||||
func (m multiSigKey) GetName() string { return m.name }
|
||||
func (m multiSigKey) GetType() keys.KeyType { return keys.TypeLocal }
|
||||
func (m multiSigKey) GetPubKey() tmcrypto.PubKey { return m.key }
|
||||
func (m multiSigKey) GetAddress() sdk.AccAddress { return sdk.AccAddress(m.key.Address()) }
|
||||
func (m multiSigKey) GetPath() (*hd.BIP44Params, error) {
|
||||
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
|
||||
}
|
||||
|
||||
func showKeysCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "show [name]",
|
||||
Use: "show [name [name...]]",
|
||||
Short: "Show key info for the given name",
|
||||
Long: `Return public details of one local key.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: runShowCmd,
|
||||
Long: `Return public details of a single local key. If multiple names are
|
||||
provided, then an ephemeral multisig key will be created under the name "multi"
|
||||
consisting of all the keys provided by name and multisig threshold.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: runShowCmd,
|
||||
}
|
||||
|
||||
cmd.Flags().String(FlagBechPrefix, sdk.PrefixAccount, "The Bech32 prefix encoding for a key (acc|val|cons)")
|
||||
cmd.Flags().BoolP(FlagAddress, "a", false, "output the address only (overrides --output)")
|
||||
cmd.Flags().BoolP(FlagPublicKey, "p", false, "output the public key only (overrides --output)")
|
||||
cmd.Flags().BoolP(FlagDevice, "d", false, "output the address in the device")
|
||||
cmd.Flags().BoolP(FlagAddress, "a", false, "Output the address only (overrides --output)")
|
||||
cmd.Flags().BoolP(FlagPublicKey, "p", false, "Output the public key only (overrides --output)")
|
||||
cmd.Flags().BoolP(FlagDevice, "d", false, "Output the address in the device")
|
||||
cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures")
|
||||
cmd.Flags().BoolP(flagShowMultiSig, "m", false, "Output multisig pubkey constituents, threshold, and weights")
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -79,6 +68,7 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pks[i] = info.GetPubKey()
|
||||
}
|
||||
|
||||
@@ -87,16 +77,15 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
multikey := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks)
|
||||
info = multiSigKey{
|
||||
name: defaultMultiSigKeyName,
|
||||
key: multikey,
|
||||
}
|
||||
info = keys.NewMultiInfo(defaultMultiSigKeyName, multikey)
|
||||
}
|
||||
|
||||
isShowAddr := viper.GetBool(FlagAddress)
|
||||
isShowPubKey := viper.GetBool(FlagPublicKey)
|
||||
isShowDevice := viper.GetBool(FlagDevice)
|
||||
isShowMultiSig := viper.GetBool(flagShowMultiSig)
|
||||
|
||||
isOutputSet := false
|
||||
tmp := cmd.Flag(cli.OutputFlag)
|
||||
@@ -122,6 +111,8 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
|
||||
printKeyAddress(info, bechKeyOut)
|
||||
case isShowPubKey:
|
||||
printPubKey(info, bechKeyOut)
|
||||
case isShowMultiSig:
|
||||
printMultiSigKeyInfo(info, bechKeyOut)
|
||||
default:
|
||||
printKeyInfo(info, bechKeyOut)
|
||||
}
|
||||
@@ -163,11 +154,11 @@ func validateMultisigThreshold(k, nKeys int) error {
|
||||
func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
||||
switch bechPrefix {
|
||||
case sdk.PrefixAccount:
|
||||
return Bech32KeyOutput, nil
|
||||
return keys.Bech32KeyOutput, nil
|
||||
case sdk.PrefixValidator:
|
||||
return Bech32ValKeyOutput, nil
|
||||
return keys.Bech32ValKeyOutput, nil
|
||||
case sdk.PrefixConsensus:
|
||||
return Bech32ConsKeyOutput, nil
|
||||
return keys.Bech32ConsKeyOutput, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
|
||||
|
||||
+10
-11
@@ -10,22 +10,21 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/multisig"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
)
|
||||
|
||||
func Test_multiSigKey_Properties(t *testing.T) {
|
||||
tmpKey1 := secp256k1.GenPrivKeySecp256k1([]byte("mySecret"))
|
||||
|
||||
tmp := multiSigKey{
|
||||
name: "myMultisig",
|
||||
key: tmpKey1.PubKey(),
|
||||
}
|
||||
pk := multisig.NewPubKeyMultisigThreshold(1, []crypto.PubKey{tmpKey1.PubKey()})
|
||||
tmp := keys.NewMultiInfo("myMultisig", pk)
|
||||
|
||||
assert.Equal(t, "myMultisig", tmp.GetName())
|
||||
assert.Equal(t, keys.TypeLocal, tmp.GetType())
|
||||
assert.Equal(t, "015ABFFB09DB738A45745A91E8C401423ECE4016", tmp.GetPubKey().Address().String())
|
||||
assert.Equal(t, "cosmos1q9dtl7cfmdec53t5t2g733qpgglvusqk6xdntl", tmp.GetAddress().String())
|
||||
assert.Equal(t, keys.TypeMulti, tmp.GetType())
|
||||
assert.Equal(t, "79BF2B5B418A85329EC2149D1854D443F56F5A9F", tmp.GetPubKey().Address().String())
|
||||
assert.Equal(t, "cosmos10xljkk6p32zn98kzzjw3s4x5g06k7k5lz6flcv", tmp.GetAddress().String())
|
||||
}
|
||||
|
||||
func Test_showKeysCmd(t *testing.T) {
|
||||
@@ -134,9 +133,9 @@ func Test_getBechKeyOut(t *testing.T) {
|
||||
}{
|
||||
{"empty", args{""}, nil, true},
|
||||
{"wrong", args{"???"}, nil, true},
|
||||
{"acc", args{sdk.PrefixAccount}, Bech32KeyOutput, false},
|
||||
{"val", args{sdk.PrefixValidator}, Bech32ValKeyOutput, false},
|
||||
{"cons", args{sdk.PrefixConsensus}, Bech32ConsKeyOutput, false},
|
||||
{"acc", args{sdk.PrefixAccount}, keys.Bech32KeyOutput, false},
|
||||
{"val", args{sdk.PrefixValidator}, keys.Bech32ValKeyOutput, false},
|
||||
{"cons", args{sdk.PrefixConsensus}, keys.Bech32ConsKeyOutput, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
package keys
|
||||
|
||||
// used for outputting keys.Info over REST
|
||||
type KeyOutput struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"pub_key"`
|
||||
Mnemonic string `json:"mnemonic,omitempty"`
|
||||
}
|
||||
|
||||
// AddNewKey request a new key
|
||||
type AddNewKey struct {
|
||||
|
||||
+24
-61
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// available output formats.
|
||||
@@ -21,7 +20,7 @@ const (
|
||||
defaultKeyDBName = "keys"
|
||||
)
|
||||
|
||||
type bechKeyOutFn func(keyInfo keys.Info) (KeyOutput, error)
|
||||
type bechKeyOutFn func(keyInfo keys.Info) (keys.KeyOutput, error)
|
||||
|
||||
// GetKeyInfo returns key info for a given name. An error is returned if the
|
||||
// keybase cannot be retrieved or getting the info fails.
|
||||
@@ -90,68 +89,22 @@ func getLazyKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
|
||||
return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys")), nil
|
||||
}
|
||||
|
||||
// create a list of KeyOutput in bech32 format
|
||||
func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) {
|
||||
kos := make([]KeyOutput, len(infos))
|
||||
for i, info := range infos {
|
||||
ko, err := Bech32KeyOutput(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kos[i] = ko
|
||||
}
|
||||
return kos, nil
|
||||
func printKeyTextHeader() {
|
||||
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
|
||||
}
|
||||
|
||||
// create a KeyOutput in bech32 format
|
||||
func Bech32KeyOutput(info keys.Info) (KeyOutput, error) {
|
||||
accAddr := sdk.AccAddress(info.GetPubKey().Address().Bytes())
|
||||
bechPubKey, err := sdk.Bech32ifyAccPub(info.GetPubKey())
|
||||
if err != nil {
|
||||
return KeyOutput{}, err
|
||||
}
|
||||
|
||||
return KeyOutput{
|
||||
Name: info.GetName(),
|
||||
Type: info.GetType().String(),
|
||||
Address: accAddr.String(),
|
||||
PubKey: bechPubKey,
|
||||
}, nil
|
||||
func printMultiSigKeyTextHeader() {
|
||||
fmt.Printf("WEIGHT:\tTHRESHOLD:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
|
||||
}
|
||||
|
||||
// Bech32ConsKeyOutput returns key output for a consensus node's key
|
||||
// information.
|
||||
func Bech32ConsKeyOutput(keyInfo keys.Info) (KeyOutput, error) {
|
||||
consAddr := sdk.ConsAddress(keyInfo.GetPubKey().Address().Bytes())
|
||||
|
||||
bechPubKey, err := sdk.Bech32ifyConsPub(keyInfo.GetPubKey())
|
||||
func printMultiSigKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
|
||||
ko, err := bechKeyOut(keyInfo)
|
||||
if err != nil {
|
||||
return KeyOutput{}, err
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return KeyOutput{
|
||||
Name: keyInfo.GetName(),
|
||||
Type: keyInfo.GetType().String(),
|
||||
Address: consAddr.String(),
|
||||
PubKey: bechPubKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Bech32ValKeyOutput returns key output for a validator's key information.
|
||||
func Bech32ValKeyOutput(keyInfo keys.Info) (KeyOutput, error) {
|
||||
valAddr := sdk.ValAddress(keyInfo.GetPubKey().Address().Bytes())
|
||||
|
||||
bechPubKey, err := sdk.Bech32ifyValPub(keyInfo.GetPubKey())
|
||||
if err != nil {
|
||||
return KeyOutput{}, err
|
||||
}
|
||||
|
||||
return KeyOutput{
|
||||
Name: keyInfo.GetName(),
|
||||
Type: keyInfo.GetType().String(),
|
||||
Address: valAddr.String(),
|
||||
PubKey: bechPubKey,
|
||||
}, nil
|
||||
printMultiSigKeyTextHeader()
|
||||
printMultiSigKeyOutput(ko)
|
||||
}
|
||||
|
||||
func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
|
||||
@@ -162,8 +115,9 @@ func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case OutputFormatText:
|
||||
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
printKeyTextHeader()
|
||||
printKeyOutput(ko)
|
||||
|
||||
case "json":
|
||||
out, err := MarshalJSON(ko)
|
||||
if err != nil {
|
||||
@@ -175,29 +129,38 @@ func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
|
||||
}
|
||||
|
||||
func printInfos(infos []keys.Info) {
|
||||
kos, err := Bech32KeysOutput(infos)
|
||||
kos, err := keys.Bech32KeysOutput(infos)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case OutputFormatText:
|
||||
fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
printKeyTextHeader()
|
||||
for _, ko := range kos {
|
||||
printKeyOutput(ko)
|
||||
}
|
||||
|
||||
case OutputFormatJSON:
|
||||
out, err := MarshalJSON(kos)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func printKeyOutput(ko KeyOutput) {
|
||||
func printKeyOutput(ko keys.KeyOutput) {
|
||||
fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey)
|
||||
}
|
||||
|
||||
func printMultiSigKeyOutput(ko keys.KeyOutput) {
|
||||
for _, pk := range ko.PubKeys {
|
||||
fmt.Printf("%d\t%d\t\t%s\t%s\n", pk.Weight, ko.Threshold, pk.Address, pk.PubKey)
|
||||
}
|
||||
}
|
||||
|
||||
func printKeyAddress(info keys.Info, bechKeyOut bechKeyOutFn) {
|
||||
ko, err := bechKeyOut(info)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,8 +19,9 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
@@ -558,7 +559,7 @@ func getKeys(t *testing.T, port string) []keys.KeyOutput {
|
||||
|
||||
// POST /keys Create a new account locally
|
||||
func doKeysPost(t *testing.T, port, name, password, mnemonic string, account int, index int) keys.KeyOutput {
|
||||
pk := keys.AddNewKey{name, password, mnemonic, account, index}
|
||||
pk := clientkeys.AddNewKey{name, password, mnemonic, account, index}
|
||||
req, err := cdc.MarshalJSON(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -584,7 +585,7 @@ func getKeysSeed(t *testing.T, port string) string {
|
||||
|
||||
// POST /keys/{name}/recove Recover a account from a seed
|
||||
func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, mnemonic string, account uint32, index uint32) {
|
||||
pk := keys.RecoverKey{recoverPassword, mnemonic, int(account), int(index)}
|
||||
pk := clientkeys.RecoverKey{recoverPassword, mnemonic, int(account), int(index)}
|
||||
req, err := cdc.MarshalJSON(pk)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -612,7 +613,7 @@ func getKey(t *testing.T, port, name string) keys.KeyOutput {
|
||||
|
||||
// PUT /keys/{name} Update the password for this account in the KMS
|
||||
func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail bool) {
|
||||
kr := keys.UpdateKeyReq{oldPassword, newPassword}
|
||||
kr := clientkeys.UpdateKeyReq{oldPassword, newPassword}
|
||||
req, err := cdc.MarshalJSON(kr)
|
||||
require.NoError(t, err)
|
||||
keyEndpoint := fmt.Sprintf("/keys/%s", name)
|
||||
@@ -626,7 +627,7 @@ func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail b
|
||||
|
||||
// DELETE /keys/{name} Remove an account
|
||||
func deleteKey(t *testing.T, port, name, password string) {
|
||||
dk := keys.DeleteKeyReq{password}
|
||||
dk := clientkeys.DeleteKeyReq{password}
|
||||
req, err := cdc.MarshalJSON(dk)
|
||||
require.NoError(t, err)
|
||||
keyEndpoint := fmt.Sprintf("/keys/%s", name)
|
||||
|
||||
Reference in New Issue
Block a user