crypto/keyring: change addrKey to store chain-agnostic addresses (#5858)

Keyrings store keys by name and hexbytes representation
of address. This turns keyring internal storage more
chain-agnostic and types.Config independent.

Obsolete Keybase internal state representation is not affected.
This commit is contained in:
Alessio Treglia 2020-03-30 16:54:01 +02:00 committed by GitHub
parent b6c45b76a7
commit db76afe840
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 82 additions and 50 deletions

View File

@ -69,13 +69,14 @@ and provided directly the IAVL store.
* (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) Move account balance logic and APIs from `x/auth` to `x/bank`.
* (types) [\#5533](https://github.com/cosmos/cosmos-sdk/pull/5533) Refactored `AppModuleBasic` and `AppModuleGenesis`
to now accept a `codec.JSONMarshaler` for modular serialization of genesis state.
* (crypto/keys) [\#5735](https://github.com/cosmos/cosmos-sdk/pull/5735) Keyring's `Update()` function is now no-op.
* (crypto/keyring) [\#5735](https://github.com/cosmos/cosmos-sdk/pull/5735) Keyring's `Update()` function is now no-op.
* (types/rest) [\#5779](https://github.com/cosmos/cosmos-sdk/pull/5779) Drop unused Parse{Int64OrReturnBadRequest,QueryParamBool}() functions.
* (keys) [\#5820](https://github.com/cosmos/cosmos-sdk/pull/5820/) Removed method CloseDB from Keybase interface.
* (baseapp) [\#5837](https://github.com/cosmos/cosmos-sdk/issues/5837) Transaction simulation now returns a `SimulationResponse` which contains the `GasInfo` and
`Result` from the execution.
* (crypto/keys) [\#5866](https://github.com/cosmos/cosmos-sdk/pull/5866) Move `Keyring` and `Keybase` implementations and their associated types from `crypto/keys/` to `crypto/keybase/`.
* (crypto/keyring) [\#5866](https://github.com/cosmos/cosmos-sdk/pull/5866) Move `Keyring` and `Keybase` implementations and their associated types from `crypto/keys/` to `crypto/keyring/`.
* (crypto) [\#5880](https://github.com/cosmos/cosmos-sdk/pull/5880) Merge `crypto/keys/mintkey` into `crypto`.
* (crypto/keyring) [\#5858](https://github.com/cosmos/cosmos-sdk/pull/5858) Make Keyring store keys by name and address's hexbytes representation.
### Features
@ -85,7 +86,7 @@ to now accept a `codec.JSONMarshaler` for modular serialization of genesis state
* (types) [\#5741](https://github.com/cosmos/cosmos-sdk/issues/5741) Prevent ChainAnteDecorators() from panicking when empty AnteDecorator slice is supplied.
* (modules) [\#5569](https://github.com/cosmos/cosmos-sdk/issues/5569) `InitGenesis`, for the relevant modules, now ensures module accounts exist.
* (crypto/keys) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) Keybase/Keyring `Sign()` methods no longer decode amino signatures
* (crypto/keyring) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) Keybase/Keyring `Sign()` methods no longer decode amino signatures
when method receivers are offline/multisig keys.
### State Machine Breaking

View File

@ -291,7 +291,7 @@ func printCreate(cmd *cobra.Command, info keyring.Info, showMnemonic bool, mnemo
switch output {
case OutputFormatText:
cmd.PrintErrln()
printKeyInfo(info, keyring.Bech32KeyOutput)
printKeyInfo(cmd.OutOrStdout(), info, keyring.Bech32KeyOutput)
// print mnemonic unless requested not to.
if showMnemonic {

View File

@ -36,12 +36,12 @@ func runListCmd(cmd *cobra.Command, _ []string) error {
return err
}
cmd.SetOut(cmd.OutOrStdout())
if !viper.GetBool(flagListNames) {
printInfos(infos)
printInfos(cmd.OutOrStdout(), infos)
return nil
}
cmd.SetOut(cmd.OutOrStdout())
for _, info := range infos {
cmd.Println(info.GetName())
}

View File

@ -4,6 +4,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"github.com/spf13/cobra"
@ -83,38 +84,39 @@ hexadecimal into bech32 cosmos prefixed format and vice versa.
return cmd
}
func parseKey(_ *cobra.Command, args []string) error {
func parseKey(cmd *cobra.Command, args []string) error {
addr := strings.TrimSpace(args[0])
outstream := cmd.OutOrStdout()
if len(addr) == 0 {
return errors.New("couldn't parse empty input")
}
if !(runFromBech32(addr) || runFromHex(addr)) {
if !(runFromBech32(outstream, addr) || runFromHex(outstream, addr)) {
return errors.New("couldn't find valid bech32 nor hex data")
}
return nil
}
// print info from bech32
func runFromBech32(bech32str string) bool {
func runFromBech32(w io.Writer, bech32str string) bool {
hrp, bz, err := bech32.DecodeAndConvert(bech32str)
if err != nil {
return false
}
displayParseKeyInfo(newHexOutput(hrp, bz))
displayParseKeyInfo(w, newHexOutput(hrp, bz))
return true
}
// print info from hex
func runFromHex(hexstr string) bool {
func runFromHex(w io.Writer, hexstr string) bool {
bz, err := hex.DecodeString(hexstr)
if err != nil {
return false
}
displayParseKeyInfo(newBech32Output(bz))
displayParseKeyInfo(w, newBech32Output(bz))
return true
}
func displayParseKeyInfo(stringer fmt.Stringer) {
func displayParseKeyInfo(w io.Writer, stringer fmt.Stringer) {
var out []byte
var err error
@ -136,5 +138,5 @@ func displayParseKeyInfo(stringer fmt.Stringer) {
panic(err)
}
fmt.Println(string(out))
fmt.Fprintln(w, string(out))
}

View File

@ -23,7 +23,7 @@ func TestParseKey(t *testing.T) {
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.wantErr, parseKey(nil, tt.args) != nil)
require.Equal(t, tt.wantErr, parseKey(ParseKeyStringCommand(), tt.args) != nil)
})
}
}

View File

@ -35,10 +35,10 @@ const (
// ShowKeysCmd shows key information for a given key name.
func ShowKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show [name [name...]]",
Short: "Show key info for the given name",
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"
Use: "show [name_or_address [name_or_address...]]",
Short: "Retrieve key information by name or address",
Long: `Display keys details. If multiple names or addresses 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,
@ -62,16 +62,16 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
return err
}
if len(args) == 1 {
info, err = kb.Get(args[0])
info, err = fetchKey(kb, args[0])
if err != nil {
return err
return fmt.Errorf("%s is not a valid name or address: %v", args[0], err)
}
} else {
pks := make([]tmcrypto.PubKey, len(args))
for i, keyName := range args {
info, err := kb.Get(keyName)
for i, keyref := range args {
info, err := fetchKey(kb, keyref)
if err != nil {
return err
return fmt.Errorf("%s is not a valid name or address: %v", keyref, err)
}
pks[i] = info.GetPubKey()
@ -112,11 +112,11 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
switch {
case isShowAddr:
printKeyAddress(info, bechKeyOut)
printKeyAddress(cmd.OutOrStdout(), info, bechKeyOut)
case isShowPubKey:
printPubKey(info, bechKeyOut)
printPubKey(cmd.OutOrStdout(), info, bechKeyOut)
default:
printKeyInfo(info, bechKeyOut)
printKeyInfo(cmd.OutOrStdout(), info, bechKeyOut)
}
if isShowDevice {
@ -142,6 +142,22 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) {
return nil
}
func fetchKey(kb keyring.Keybase, keyref string) (keyring.Info, error) {
info, err := kb.Get(keyref)
if err != nil {
accAddr, err := sdk.AccAddressFromBech32(keyref)
if err != nil {
return info, err
}
info, err = kb.GetByAddress(accAddr)
if err != nil {
return info, errors.New("key not found")
}
}
return info, nil
}
func validateMultisigThreshold(k, nKeys int) error {
if k <= 0 {
return fmt.Errorf("threshold must be a positive integer")

View File

@ -38,8 +38,8 @@ func Test_runShowCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := ShowKeysCmd()
mockIn, _, _ := tests.ApplyMockIO(cmd)
require.EqualError(t, runShowCmd(cmd, []string{"invalid"}), "The specified item could not be found in the keyring")
require.EqualError(t, runShowCmd(cmd, []string{"invalid1", "invalid2"}), "The specified item could not be found in the keyring")
require.EqualError(t, runShowCmd(cmd, []string{"invalid"}), "invalid is not a valid name or address: decoding bech32 failed: invalid bech32 string length 7")
require.EqualError(t, runShowCmd(cmd, []string{"invalid1", "invalid2"}), "invalid1 is not a valid name or address: decoding bech32 failed: invalid index of 1")
// Prepare a key base
// Now add a temporary keybase
@ -78,7 +78,13 @@ func Test_runShowCmd(t *testing.T) {
if runningUnattended {
mockIn.Reset("testpass1\n")
}
// try fetch by name
require.NoError(t, runShowCmd(cmd, []string{fakeKeyName1}))
// try fetch by addr
info, err := kb.Get(fakeKeyName1)
require.NoError(t, err)
require.NoError(t, runShowCmd(cmd, []string{info.GetAddress().String()}))
// Now try multisig key - set bech to acc
viper.Set(FlagBechPrefix, sdk.PrefixAccount)

View File

@ -2,12 +2,13 @@ package keys
import (
"fmt"
"io"
"path/filepath"
"github.com/99designs/keyring"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
yaml "gopkg.in/yaml.v2"
"gopkg.in/yaml.v2"
"github.com/cosmos/cosmos-sdk/client/flags"
cryptokeyring "github.com/cosmos/cosmos-sdk/crypto/keyring"
@ -37,7 +38,7 @@ func getLazyKeyBaseFromDir(rootDir string, opts ...cryptokeyring.KeybaseOption)
return cryptokeyring.New(defaultKeyDBName, filepath.Join(rootDir, "keys"), opts...), nil
}
func printKeyInfo(keyInfo cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
func printKeyInfo(w io.Writer, keyInfo cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(keyInfo)
if err != nil {
panic(err)
@ -45,7 +46,7 @@ func printKeyInfo(keyInfo cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
switch viper.Get(cli.OutputFlag) {
case OutputFormatText:
printTextInfos([]cryptokeyring.KeyOutput{ko})
printTextInfos(w, []cryptokeyring.KeyOutput{ko})
case OutputFormatJSON:
var out []byte
@ -59,11 +60,11 @@ func printKeyInfo(keyInfo cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
panic(err)
}
fmt.Println(string(out))
fmt.Fprintln(w, string(out))
}
}
func printInfos(infos []cryptokeyring.Info) {
func printInfos(w io.Writer, infos []cryptokeyring.Info) {
kos, err := cryptokeyring.Bech32KeysOutput(infos)
if err != nil {
panic(err)
@ -71,7 +72,7 @@ func printInfos(infos []cryptokeyring.Info) {
switch viper.Get(cli.OutputFlag) {
case OutputFormatText:
printTextInfos(kos)
printTextInfos(w, kos)
case OutputFormatJSON:
var out []byte
@ -86,34 +87,34 @@ func printInfos(infos []cryptokeyring.Info) {
if err != nil {
panic(err)
}
fmt.Printf("%s", out)
fmt.Fprintf(w, "%s", out)
}
}
func printTextInfos(kos []cryptokeyring.KeyOutput) {
func printTextInfos(w io.Writer, kos []cryptokeyring.KeyOutput) {
out, err := yaml.Marshal(&kos)
if err != nil {
panic(err)
}
fmt.Println(string(out))
fmt.Fprintln(w, string(out))
}
func printKeyAddress(info cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
func printKeyAddress(w io.Writer, info cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(info)
if err != nil {
panic(err)
}
fmt.Println(ko.Address)
fmt.Fprintln(w, ko.Address)
}
func printPubKey(info cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
func printPubKey(w io.Writer, info cryptokeyring.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(info)
if err != nil {
panic(err)
}
fmt.Println(ko.PubKey)
fmt.Fprintln(w, ko.PubKey)
}
func isRunningUnattended() bool {

View File

@ -1,6 +1,7 @@
package keyring
import (
"encoding/hex"
"fmt"
"reflect"
"strings"
@ -126,7 +127,7 @@ func (kb dbKeybase) Get(name string) (Info, error) {
// GetByAddress returns Info based on a provided AccAddress. An error is returned
// if the address does not exist.
func (kb dbKeybase) GetByAddress(address types.AccAddress) (Info, error) {
ik, err := kb.db.Get(addrKey(address))
ik, err := kb.db.Get(addrStringKey(address))
if err != nil {
return nil, err
}
@ -344,7 +345,7 @@ func (kb dbKeybase) Delete(name, passphrase string, skipPass bool) error {
batch := kb.db.NewBatch()
defer batch.Close()
batch.Delete(addrKey(info.GetAddress()))
batch.Delete(addrStringKey(info.GetAddress()))
batch.Delete(infoKey(name))
return batch.WriteSync()
@ -414,13 +415,18 @@ func (kb dbKeybase) writeInfo(name string, info Info) {
kb.db.SetSync(key, serializedInfo)
// store a pointer to the infokey by address for fast lookup
kb.db.SetSync(addrKey(info.GetAddress()), key)
kb.db.SetSync(addrStringKey(info.GetAddress()), key)
}
func addrKey(address types.AccAddress) []byte {
// this is to be removed together with dbKeybase and the old Keybase interface
func addrStringKey(address types.AccAddress) []byte {
return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix))
}
func addrHexKey(address types.AccAddress) []byte {
return []byte(fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix))
}
func infoKey(name string) []byte {
return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
}

View File

@ -178,7 +178,7 @@ func (kb keyringKeybase) Get(name string) (Info, error) {
// GetByAddress fetches a key by address and returns its public information.
func (kb keyringKeybase) GetByAddress(address types.AccAddress) (Info, error) {
ik, err := kb.db.Get(string(addrKey(address)))
ik, err := kb.db.Get(string(addrHexKey(address)))
if err != nil {
return nil, err
}
@ -313,7 +313,7 @@ func (kb keyringKeybase) Import(name string, armor string) error {
kb.writeInfo(name, info)
err = kb.db.Set(keyring.Item{
Key: string(addrKey(info.GetAddress())),
Key: string(addrHexKey(info.GetAddress())),
Data: infoKey(name),
})
if err != nil {
@ -401,7 +401,7 @@ func (kb keyringKeybase) Delete(name, _ string, _ bool) error {
return err
}
err = kb.db.Remove(string(addrKey(info.GetAddress())))
err = kb.db.Remove(string(addrHexKey(info.GetAddress())))
if err != nil {
return err
}
@ -454,7 +454,7 @@ func (kb keyringKeybase) writeInfo(name string, info Info) {
}
err = kb.db.Set(keyring.Item{
Key: string(addrKey(info.GetAddress())),
Key: string(addrHexKey(info.GetAddress())),
Data: key,
})
if err != nil {