Browse Source

crypto/amino: add function to modify key codec (#4112)

## Issue:

Hey, not sure if this is disallowed for any reason specifically, but it would be very beneficial to define additional types to decode tendermint key implementations from bytes, since it uses a static codec. If this is okay, let me know and I will add documentation.

Context: For Ethermint to switch to using Cosmos' keybase, decoding the keys requires this codec to be updated

Just to document, I did experiment with creating a mapping from string to objects to be able to keep track of the key types added to be able to be used in the RegisterAmino(..) call, but because of how go is compiled, cosmos would just use the base types. This may be a useful feature for someone just building on top of Tendermint and not going through Cosmos, but to not add confusion or unnecessary complexity, I left it out.

## Commits:

* Exposes amino codec to be able to decode pk bytes in application

* Change how codec is modified

* Remove unneeded comment

* Fix comment

* Fix comment

* Add registered type to nametable

* Add pending changelog entry

* Reorder change

* Added check if type is registered and added test

* Make test type private

* Remove unnecessary duplicate exists check
pull/4135/head
Austin Abell 5 years ago
committed by Anton Kaliaev
parent
commit
7cd55a7733
3 changed files with 84 additions and 0 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +8
    -0
      crypto/encoding/amino/amino.go
  3. +75
    -0
      crypto/encoding/amino/encode_test.go

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -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:


+ 8
- 0
crypto/encoding/amino/amino.go View File

@ -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


+ 75
- 0
crypto/encoding/amino/encode_test.go View File

@ -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
}

Loading…
Cancel
Save