diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 844ca9aeb..0fa986f0d 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -29,6 +29,7 @@ program](https://hackerone.com/tendermint). - [p2p] [\#3991](https://github.com/tendermint/tendermint/issues/3991) Log "has been established or dialed" as debug log instead of Error for connected peers (@whunmr) - [rpc] [\#4077](https://github.com/tendermint/tendermint/pull/4077) Added support for `EXISTS` clause to the Websocket query interface. - [privval] Add `SignerDialerEndpointRetryWaitInterval` option (@cosmostuba) +- [crypto] Add `RegisterKeyType` to amino to allow external key types registration (@austinabell) ### BUG FIXES: diff --git a/crypto/encoding/amino/amino.go b/crypto/encoding/amino/amino.go index f7be3a203..1aed88115 100644 --- a/crypto/encoding/amino/amino.go +++ b/crypto/encoding/amino/amino.go @@ -60,11 +60,19 @@ func RegisterAmino(cdc *amino.Codec) { secp256k1.PrivKeyAminoName, nil) } +// RegisterKeyType registers an external key type to allow decoding it from bytes +func RegisterKeyType(o interface{}, name string) { + cdc.RegisterConcrete(o, name, nil) + nameTable[reflect.TypeOf(o)] = name +} + +// PrivKeyFromBytes unmarshals private key bytes and returns a PrivKey func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) { err = cdc.UnmarshalBinaryBare(privKeyBytes, &privKey) return } +// PubKeyFromBytes unmarshals public key bytes and returns a PubKey func PubKeyFromBytes(pubKeyBytes []byte) (pubKey crypto.PubKey, err error) { err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) return diff --git a/crypto/encoding/amino/encode_test.go b/crypto/encoding/amino/encode_test.go index d4e349454..e73478b54 100644 --- a/crypto/encoding/amino/encode_test.go +++ b/crypto/encoding/amino/encode_test.go @@ -2,10 +2,13 @@ package cryptoAmino import ( "os" + "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/multisig" @@ -147,3 +150,75 @@ func TestPubkeyAminoName(t *testing.T) { } } } + +var _ crypto.PrivKey = testPriv{} +var _ crypto.PubKey = testPub{} +var testCdc = amino.NewCodec() + +type testPriv []byte + +func (privkey testPriv) PubKey() crypto.PubKey { return testPub{} } +func (privkey testPriv) Bytes() []byte { + return testCdc.MustMarshalBinaryBare(privkey) +} +func (privkey testPriv) Sign(msg []byte) ([]byte, error) { return []byte{}, nil } +func (privkey testPriv) Equals(other crypto.PrivKey) bool { return true } + +type testPub []byte + +func (key testPub) Address() crypto.Address { return crypto.Address{} } +func (key testPub) Bytes() []byte { + return testCdc.MustMarshalBinaryBare(key) +} +func (key testPub) VerifyBytes(msg []byte, sig []byte) bool { return true } +func (key testPub) Equals(other crypto.PubKey) bool { return true } + +var ( + privAminoName = "registerTest/Priv" + pubAminoName = "registerTest/Pub" +) + +func TestRegisterKeyType(t *testing.T) { + RegisterAmino(testCdc) + testCdc.RegisterConcrete(testPriv{}, privAminoName, nil) + testCdc.RegisterConcrete(testPub{}, pubAminoName, nil) + + pub := testPub{0x1} + priv := testPriv{0x2} + + // Check to make sure key cannot be decoded before registering + _, err := PrivKeyFromBytes(priv.Bytes()) + require.Error(t, err) + _, err = PubKeyFromBytes(pub.Bytes()) + require.Error(t, err) + + // Check that name is not registered + _, found := PubkeyAminoName(testCdc, pub) + require.False(t, found) + + // Register key types + RegisterKeyType(testPriv{}, privAminoName) + RegisterKeyType(testPub{}, pubAminoName) + + // Name should exist after registering + name, found := PubkeyAminoName(testCdc, pub) + require.True(t, found) + require.Equal(t, name, pubAminoName) + + // Decode keys using the encoded bytes from encoding with the other codec + decodedPriv, err := PrivKeyFromBytes(priv.Bytes()) + require.NoError(t, err) + require.Equal(t, priv, decodedPriv) + + decodedPub, err := PubKeyFromBytes(pub.Bytes()) + require.NoError(t, err) + require.Equal(t, pub, decodedPub) + + // Reset module codec after testing + cdc = amino.NewCodec() + nameTable = make(map[reflect.Type]string, 3) + RegisterAmino(cdc) + nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName + nameTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoName + nameTable[reflect.TypeOf(multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute +}