diff --git a/crypto/keyring/errors.go b/crypto/keyring/errors.go index 4ec6d2e465fc..3ebbb2409481 100644 --- a/crypto/keyring/errors.go +++ b/crypto/keyring/errors.go @@ -1,13 +1,38 @@ package keyring -import "github.com/pkg/errors" +import "github.com/cockroachdb/errors" var ( // ErrUnsupportedSigningAlgo is raised when the caller tries to use a // different signing scheme than secp256k1. ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo") - // ErrUnsupportedLanguage is raised when the caller tries to use a // different language than english for creating a mnemonic sentence. ErrUnsupportedLanguage = errors.New("unsupported language: only english is supported") + // ErrUnknownBacked is raised when the keyring backend is unknown + ErrUnknownBacked = errors.New("unknown keyring backend") + // ErrOverwriteKey is raised when a key cannot be overwritten + ErrOverwriteKey = errors.New("cannot overwrite key") + // ErrKeyAlreadyExists is raised when creating a key that already exists + ErrKeyAlreadyExists = errors.Newf("key already exists") + // ErrInvalidSignMode is raised when trying to sign with an invaled method + ErrInvalidSignMode = errors.New("invalid sign mode, expected LEGACY_AMINO_JSON or TEXTUAL") + // ErrMaxPassPhraseAttempts is raised when the maxPassphraseEntryAttempts is reached + ErrMaxPassPhraseAttempts = errors.New("too many failed passphrase attempts") + // ErrUnableToSerialize is raised when codec fails to serialize + ErrUnableToSerialize = errors.New("unable to serialize record") + // ErrOfflineSign is raised when trying to sign offline record. + ErrOfflineSign = errors.New("cannot sign with offline keys") + // ErrDuplicatedAddress is raised when creating a key with the same address as a key that already exists. + ErrDuplicatedAddress = errors.New("duplicated address created") + // ErrLedgerGenerateKey is raised when a ledger can't generate a key + ErrLedgerGenerateKey = errors.New("failed to generate ledger key") + // ErrNotLedgerObj is raised when record.GetLedger() returns nil. + ErrNotLedgerObj = errors.New("not a ledger object") + // ErrLedgerInvalidSignature is raised when ledger generates an invalid signature. + ErrLedgerInvalidSignature = errors.New("Ledger generated an invalid signature. Perhaps you have multiple ledgers and need to try another one") + // ErrLegacyToRecord is raised when cannot be converted to a Record + ErrLegacyToRecord = errors.New("unable to convert LegacyInfo to Record") + // ErrUnknownLegacyType is raised when a LegacyInfo type is unknown. + ErrUnknownLegacyType = errors.New("unknown LegacyInfo type") ) diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go index 126475b9fb3c..1c0e81338050 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -11,8 +11,8 @@ import ( "strings" "github.com/99designs/keyring" + "github.com/cockroachdb/errors" cmtcrypto "github.com/cometbft/cometbft/crypto" - "github.com/pkg/errors" "github.com/cosmos/cosmos-sdk/client/input" "github.com/cosmos/cosmos-sdk/codec" @@ -70,7 +70,7 @@ type Keyring interface { DeleteByAddress(address sdk.Address) error // Rename an existing key from the Keyring - Rename(from string, to string) error + Rename(from, to string) error // NewMnemonic generates a new mnemonic, derives a hierarchical deterministic key from it, and // persists the key to storage. Returns the generated mnemonic and the key Info. @@ -116,7 +116,7 @@ type Importer interface { ImportPrivKey(uid, armor, passphrase string) error // ImportPubKey imports ASCII armored public keys. - ImportPubKey(uid string, armor string) error + ImportPubKey(uid, armor string) error } // Migrator is implemented by key stores and enables migration of keys from amino to proto @@ -194,7 +194,7 @@ func New( case BackendPass: db, err = keyring.Open(newPassBackendKeyringConfig(appName, rootDir, userInput)) default: - return nil, fmt.Errorf("unknown keyring backend %v", backend) + return nil, errors.Wrap(ErrUnknownBacked, backend) } if err != nil { @@ -317,7 +317,7 @@ func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassp func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error { if k, err := ks.Key(uid); err == nil { if uid == k.Name { - return fmt.Errorf("cannot overwrite key: %s", uid) + return errors.Wrap(ErrOverwriteKey, uid) } } @@ -334,9 +334,9 @@ func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error { return nil } -func (ks keystore) ImportPubKey(uid string, armor string) error { +func (ks keystore) ImportPubKey(uid, armor string) error { if _, err := ks.Key(uid); err == nil { - return fmt.Errorf("cannot overwrite key: %s", uid) + return errors.Wrap(ErrOverwriteKey, uid) } pubBytes, _, err := crypto.UnarmorPubKeyBytes(armor) @@ -386,8 +386,7 @@ func (ks keystore) Sign(uid string, msg []byte, signMode signing.SignMode) ([]by if err != nil { return nil, nil, err } - - return nil, pub, errors.New("cannot sign with offline keys") + return nil, pub, ErrOfflineSign } } @@ -402,17 +401,14 @@ func (ks keystore) SignByAddress(address sdk.Address, msg []byte, signMode signi func (ks keystore) SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (*Record, error) { if !ks.options.SupportedAlgosLedger.Contains(algo) { - return nil, fmt.Errorf( - "%w: signature algo %s is not defined in the keyring options", - ErrUnsupportedSigningAlgo, algo.Name(), - ) + return nil, errors.Wrap(ErrUnsupportedSigningAlgo, fmt.Sprintf("signature algo %s is not defined in the keyring options", algo.Name())) } hdPath := hd.NewFundraiserParams(account, coinType, index) priv, _, err := ledger.NewPrivKeySecp256k1(*hdPath, hrp) if err != nil { - return nil, fmt.Errorf("failed to generate ledger key: %w", err) + return nil, errors.CombineErrors(ErrLedgerGenerateKey, err) } return ks.writeLedgerKey(uid, priv.PubKey(), hdPath) @@ -452,7 +448,7 @@ func (ks keystore) DeleteByAddress(address sdk.Address) error { func (ks keystore) Rename(oldName, newName string) error { _, err := ks.Key(newName) if err == nil { - return fmt.Errorf("rename failed: %s already exists in the keyring", newName) + return errors.Wrap(ErrKeyAlreadyExists, fmt.Sprintf("rename failed, %s", newName)) } armor, err := ks.ExportPrivKeyArmor(oldName, passPhrase) @@ -512,7 +508,7 @@ func (ks keystore) KeyByAddress(address sdk.Address) (*Record, error) { func wrapKeyNotFound(err error, msg string) error { if err == keyring.ErrKeyNotFound { - return sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, msg) + return errors.Wrap(sdkerrors.ErrKeyNotFound, msg) } return err } @@ -554,7 +550,7 @@ func (ks keystore) NewMnemonic(uid string, language Language, hdPath, bip39Passp return k, mnemonic, nil } -func (ks keystore) NewAccount(name string, mnemonic string, bip39Passphrase string, hdPath string, algo SignatureAlgo) (*Record, error) { +func (ks keystore) NewAccount(name, mnemonic, bip39Passphrase, hdPath string, algo SignatureAlgo) (*Record, error) { if !ks.isSupportedSigningAlgo(algo) { return nil, ErrUnsupportedSigningAlgo } @@ -567,11 +563,11 @@ func (ks keystore) NewAccount(name string, mnemonic string, bip39Passphrase stri privKey := algo.Generate()(derivedPriv) - // check if the a key already exists with the same address and return an error + // check if the key already exists with the same address and return an error // if found address := sdk.AccAddress(privKey.PubKey().Address()) if _, err := ks.KeyByAddress(address); err == nil { - return nil, errors.New("duplicated address created") + return nil, ErrDuplicatedAddress } return ks.writeLocalKey(name, privKey) @@ -602,7 +598,7 @@ func (ks keystore) SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) { func SignWithLedger(k *Record, msg []byte, signMode signing.SignMode) (sig []byte, pub types.PubKey, err error) { ledgerInfo := k.GetLedger() if ledgerInfo == nil { - return nil, nil, errors.New("not a ledger object") + return nil, nil, ErrNotLedgerObj } path := ledgerInfo.GetPath() @@ -624,11 +620,11 @@ func SignWithLedger(k *Record, msg []byte, signMode signing.SignMode) (sig []byt return nil, nil, err } default: - return nil, nil, fmt.Errorf("got invalid sign mode %d, expected LEGACY_AMINO_JSON or TEXTUAL", signMode) + return nil, nil, errors.Wrap(ErrInvalidSignMode, fmt.Sprintf("%v", signMode)) } if !priv.PubKey().VerifySignature(msg, sig) { - return nil, nil, errors.New("Ledger generated an invalid signature. Perhaps you have multiple ledgers and need to try another one") + return nil, nil, ErrLedgerInvalidSignature } return sig, priv.PubKey(), nil @@ -697,7 +693,7 @@ func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { case err == nil: keyhash, err = os.ReadFile(keyhashFilePath) if err != nil { - return "", fmt.Errorf("failed to read %s: %v", keyhashFilePath, err) + return "", errors.Wrap(err, fmt.Sprintf("failed to read %s", keyhashFilePath)) } keyhashStored = true @@ -706,7 +702,7 @@ func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { keyhashStored = false default: - return "", fmt.Errorf("failed to open %s: %v", keyhashFilePath, err) + return "", errors.Wrap(err, fmt.Sprintf("failed to open %s", keyhashFilePath)) } failureCounter := 0 @@ -714,7 +710,7 @@ func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { for { failureCounter++ if failureCounter > maxPassphraseEntryAttempts { - return "", fmt.Errorf("too many failed passphrase attempts") + return "", ErrMaxPassPhraseAttempts } buf := bufio.NewReader(buf) @@ -795,12 +791,12 @@ func (ks keystore) writeRecord(k *Record) error { return err } if exists { - return fmt.Errorf("public key %s already exists in keybase", key) + return errors.Wrap(ErrKeyAlreadyExists, key) } serializedRecord, err := ks.cdc.Marshal(k) if err != nil { - return fmt.Errorf("unable to serialize record; %+w", err) + return errors.CombineErrors(ErrUnableToSerialize, err) } item := keyring.Item{ @@ -927,7 +923,7 @@ func (ks keystore) migrate(key string) (*Record, error) { } if len(item.Data) == 0 { - return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, key) + return nil, errors.Wrap(sdkerrors.ErrKeyNotFound, key) } // 2. Try to deserialize using proto @@ -940,18 +936,18 @@ func (ks keystore) migrate(key string) (*Record, error) { // 4. Try to decode with amino legacyInfo, err := unMarshalLegacyInfo(item.Data) if err != nil { - return nil, fmt.Errorf("unable to unmarshal item.Data, err: %w", err) + return nil, errors.Wrap(err, "unable to unmarshal item.Data") } // 5. Convert and serialize info using proto k, err = ks.convertFromLegacyInfo(legacyInfo) if err != nil { - return nil, fmt.Errorf("convertFromLegacyInfo, err: %w", err) + return nil, errors.Wrap(err, "convertFromLegacyInfo") } serializedRecord, err := ks.cdc.Marshal(k) if err != nil { - return nil, fmt.Errorf("unable to serialize record, err: %w", err) + return nil, errors.CombineErrors(ErrUnableToSerialize, err) } item = keyring.Item{ @@ -961,7 +957,7 @@ func (ks keystore) migrate(key string) (*Record, error) { // 6. Overwrite the keyring entry with the new proto-encoded key. if err := ks.SetItem(item); err != nil { - return nil, fmt.Errorf("unable to set keyring.Item, err: %w", err) + return nil, errors.Wrap(err, "unable to set keyring.Item") } fmt.Printf("Successfully migrated key %s.\n", key) @@ -984,7 +980,7 @@ func (ks keystore) SetItem(item keyring.Item) error { func (ks keystore) convertFromLegacyInfo(info LegacyInfo) (*Record, error) { if info == nil { - return nil, errors.New("unable to convert LegacyInfo to Record cause info is nil") + return nil, errors.Wrap(ErrLegacyToRecord, "info is nil") } name := info.GetName() @@ -1010,7 +1006,7 @@ func (ks keystore) convertFromLegacyInfo(info LegacyInfo) (*Record, error) { return NewLedgerRecord(name, pk, path) default: - return nil, errors.New("unknown LegacyInfo type") + return nil, ErrUnknownLegacyType } } diff --git a/crypto/keyring/keyring_ledger_test.go b/crypto/keyring/keyring_ledger_test.go index abd0fbe93a22..ca1b79bfbf99 100644 --- a/crypto/keyring/keyring_ledger_test.go +++ b/crypto/keyring/keyring_ledger_test.go @@ -5,9 +5,9 @@ package keyring import ( "bytes" - "strings" "testing" + "github.com/cockroachdb/errors" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -101,8 +101,7 @@ func TestAltKeyring_SaveLedgerKey(t *testing.T) { // Test unsupported Algo _, err = kr.SaveLedgerKey("key", notSupportedAlgo{}, "cosmos", 118, 0, 0) - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), ErrUnsupportedSigningAlgo.Error())) + require.True(t, errors.Is(err, ErrUnsupportedSigningAlgo)) k, err := kr.SaveLedgerKey("some_account", hd.Secp256k1, "cosmos", 118, 3, 1) if err != nil { diff --git a/crypto/keyring/keyring_test.go b/crypto/keyring/keyring_test.go index f4af9d612631..9545d89b2c39 100644 --- a/crypto/keyring/keyring_test.go +++ b/crypto/keyring/keyring_test.go @@ -2,6 +2,7 @@ package keyring import ( "encoding/hex" + "errors" "fmt" "os" "path/filepath" @@ -52,7 +53,7 @@ func TestNewKeyring(t *testing.T) { nilKr, err := New("cosmos", "fuzzy", dir, mockIn, cdc) require.Error(t, err) require.Nil(t, nilKr) - require.Equal(t, "unknown keyring backend fuzzy", err.Error()) + require.True(t, errors.Is(err, ErrUnknownBacked)) mockIn.Reset("password\npassword\n") k, _, err := kr.NewMnemonic("foo", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) @@ -442,7 +443,7 @@ func TestKeyringKeybaseExportImportPrivKey(t *testing.T) { // overwrite is not allowed err = kb.ImportPrivKey("john2", keystr, "password") - require.Equal(t, "cannot overwrite key: john2", err.Error()) + require.True(t, errors.Is(err, ErrOverwriteKey)) // try export non existing key _, err = kb.ExportPrivKeyArmor("john3", "wrongpassword") @@ -1254,7 +1255,7 @@ func TestAltKeyring_ImportExportPrivKey(t *testing.T) { // Should fail importing private key on existing key. err = kr.ImportPrivKey(newUID, armor, passphrase) - require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) + require.True(t, errors.Is(err, ErrOverwriteKey)) } func TestAltKeyring_ImportExportPrivKey_ByAddress(t *testing.T) { @@ -1284,7 +1285,7 @@ func TestAltKeyring_ImportExportPrivKey_ByAddress(t *testing.T) { // Should fail importing private key on existing key. err = kr.ImportPrivKey(newUID, armor, passphrase) - require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) + require.True(t, errors.Is(err, ErrOverwriteKey)) } func TestAltKeyring_ImportExportPubKey(t *testing.T) { @@ -1307,7 +1308,7 @@ func TestAltKeyring_ImportExportPubKey(t *testing.T) { // Should fail importing private key on existing key. err = kr.ImportPubKey(newUID, armor) - require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) + require.True(t, errors.Is(err, ErrOverwriteKey)) } func TestAltKeyring_ImportExportPubKey_ByAddress(t *testing.T) { @@ -1332,7 +1333,7 @@ func TestAltKeyring_ImportExportPubKey_ByAddress(t *testing.T) { // Should fail importing private key on existing key. err = kr.ImportPubKey(newUID, armor) - require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) + require.True(t, errors.Is(err, ErrOverwriteKey)) } func TestAltKeyring_UnsafeExportPrivKeyHex(t *testing.T) { @@ -1426,7 +1427,7 @@ func TestRenameKey(t *testing.T) { newKeyRecord(t, kr, key1) newKeyRecord(t, kr, key2) err := kr.Rename(key2, key1) - require.Equal(t, fmt.Errorf("rename failed: %s already exists in the keyring", key1), err) + require.True(t, errors.Is(err, ErrKeyAlreadyExists)) assertKeysExist(t, kr, key1, key2) // keys should still exist after failed rename }, }, @@ -1436,7 +1437,7 @@ func TestRenameKey(t *testing.T) { keyName := "keyName" newKeyRecord(t, kr, keyName) err := kr.Rename(keyName, keyName) - require.Equal(t, fmt.Errorf("rename failed: %s already exists in the keyring", keyName), err) + require.True(t, errors.Is(err, ErrKeyAlreadyExists)) assertKeysExist(t, kr, keyName) }, }, diff --git a/crypto/keyring/record.go b/crypto/keyring/record.go index 1b1885648e5e..16a1b9adebcb 100644 --- a/crypto/keyring/record.go +++ b/crypto/keyring/record.go @@ -1,7 +1,7 @@ package keyring import ( - "errors" + "github.com/cockroachdb/errors" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -9,8 +9,14 @@ import ( "github.com/cosmos/cosmos-sdk/types" ) -// ErrPrivKeyExtr is used to output an error if extraction of a private key from Local item fails -var ErrPrivKeyExtr = errors.New("private key extraction works only for Local") +var ( + // ErrPrivKeyExtr is used to output an error if extraction of a private key from Local item fails. + ErrPrivKeyExtr = errors.New("private key extraction works only for Local") + // ErrPrivKeyNotAvailable is used when a Record_Local.PrivKey is nil. + ErrPrivKeyNotAvailable = errors.New("private key is not available") + // ErrCastAny is used to output an error if cast from types.Any fails. + ErrCastAny = errors.New("unable to cast to cryptotypes") +) func newRecord(name string, pk cryptotypes.PubKey, item isRecord_Item) (*Record, error) { any, err := codectypes.NewAnyWithValue(pk) @@ -63,7 +69,7 @@ func NewMultiRecord(name string, pk cryptotypes.PubKey) (*Record, error) { func (k *Record) GetPubKey() (cryptotypes.PubKey, error) { pk, ok := k.PubKey.GetCachedValue().(cryptotypes.PubKey) if !ok { - return nil, errors.New("unable to cast any to cryptotypes.PubKey") + return nil, errors.Wrap(ErrCastAny, "PubKey") } return pk, nil @@ -120,12 +126,12 @@ func extractPrivKeyFromRecord(k *Record) (cryptotypes.PrivKey, error) { func extractPrivKeyFromLocal(rl *Record_Local) (cryptotypes.PrivKey, error) { if rl.PrivKey == nil { - return nil, errors.New("private key is not available") + return nil, ErrPrivKeyNotAvailable } priv, ok := rl.PrivKey.GetCachedValue().(cryptotypes.PrivKey) if !ok { - return nil, errors.New("unable to cast any to cryptotypes.PrivKey") + return nil, errors.Wrap(ErrCastAny, "PrivKey") } return priv, nil diff --git a/crypto/keyring/signing_algorithms.go b/crypto/keyring/signing_algorithms.go index 920dd7b55310..4a22a98a8e19 100644 --- a/crypto/keyring/signing_algorithms.go +++ b/crypto/keyring/signing_algorithms.go @@ -1,9 +1,10 @@ package keyring import ( - "fmt" "strings" + "github.com/cockroachdb/errors" + "github.com/cosmos/cosmos-sdk/crypto/hd" ) @@ -21,7 +22,7 @@ func NewSigningAlgoFromString(str string, algoList SigningAlgoList) (SignatureAl return algo, nil } } - return nil, fmt.Errorf("provided algorithm %q is not supported", str) + return nil, errors.Wrap(ErrUnsupportedSigningAlgo, str) } // SigningAlgoList is a slice of signature algorithms diff --git a/crypto/keyring/signing_algorithms_test.go b/crypto/keyring/signing_algorithms_test.go index 131ade58b4e2..3ec31da6bf17 100644 --- a/crypto/keyring/signing_algorithms_test.go +++ b/crypto/keyring/signing_algorithms_test.go @@ -29,7 +29,7 @@ func TestNewSigningAlgoByString(t *testing.T) { "notsupportedalgo", false, nil, - fmt.Errorf("provided algorithm \"notsupportedalgo\" is not supported"), + ErrUnsupportedSigningAlgo, }, } @@ -41,7 +41,7 @@ func TestNewSigningAlgoByString(t *testing.T) { if tt.isSupported { require.Equal(t, hd.Secp256k1, algorithm) } else { - require.EqualError(t, err, tt.expectedErr.Error()) + require.ErrorIs(t, err, tt.expectedErr) } }) } diff --git a/crypto/keyring/types.go b/crypto/keyring/types.go index 0b893ea4cccc..b718413104e2 100644 --- a/crypto/keyring/types.go +++ b/crypto/keyring/types.go @@ -65,7 +65,7 @@ func (kt KeyType) String() string { type ( // DeriveKeyFunc defines the function to derive a new key from a seed and hd path - DeriveKeyFunc func(mnemonic string, bip39Passphrase, hdPath string, algo hd.PubKeyType) ([]byte, error) + DeriveKeyFunc func(mnemonic, bip39Passphrase, hdPath string, algo hd.PubKeyType) ([]byte, error) // PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key PrivKeyGenFunc func(bz []byte, algo hd.PubKeyType) (cryptotypes.PrivKey, error) ) diff --git a/go.mod b/go.mod index dd5f33dd453c..fb32c600bee3 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/cockroachdb/apd/v2 v2.0.2 + github.com/cockroachdb/errors v1.9.1 github.com/cometbft/cometbft v0.0.0-20230203130311-387422ac220d github.com/cosmos/btcutil v1.0.5 github.com/cosmos/cosmos-db v1.0.0-rc.1 @@ -71,7 +72,6 @@ require ( github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/pebble v0.0.0-20230203182935-f2e58dc4a0e1 // indirect github.com/cockroachdb/redact v1.1.3 // indirect