diff --git a/crypto/composite/composite.go b/crypto/composite/composite.go index 4115b61c7..8db3b1edb 100644 --- a/crypto/composite/composite.go +++ b/crypto/composite/composite.go @@ -1,8 +1,7 @@ package composite import ( - "bytes" - + "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls" "github.com/tendermint/tendermint/crypto/ed25519" @@ -10,12 +9,30 @@ import ( ) // PubKeyComposite and PrivKeyComposite are intended to allow public key algorithms to be selected for each function. - const ( - PubKeyCompositeAminoName = "tendermint/PubKeyComposite" - PrivKeyCompositeAminoName = "tendermint/PrivKeyComposite" + PrivKeyAminoName = "tendermint/PrivKeyComposite" + PubKeyAminoName = "tendermint/PubKeyComposite" ) +var cdc = amino.NewCodec() + +func init() { + cdc.RegisterInterface((*crypto.PubKey)(nil), nil) + cdc.RegisterConcrete(PubKeyComposite{}, + PubKeyAminoName, nil) + cdc.RegisterConcrete(bls.PubKeyBLS12{}, + bls.PubKeyAminoName, nil) + cdc.RegisterConcrete(ed25519.PubKeyEd25519{}, + ed25519.PubKeyAminoName, nil) + cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) + cdc.RegisterConcrete(PrivKeyComposite{}, + PrivKeyAminoName, nil) + cdc.RegisterConcrete(bls.PrivKeyBLS12{}, + bls.PrivKeyAminoName, nil) + cdc.RegisterConcrete(ed25519.PrivKeyEd25519{}, + ed25519.PrivKeyAminoName, nil) +} + type PubKeyComposite struct { SignKey crypto.PubKey `json:"sign"` VrfKey crypto.PubKey `json:"vrf"` @@ -30,9 +47,11 @@ func (pk PubKeyComposite) Address() crypto.Address { } func (pk PubKeyComposite) Bytes() []byte { - msg := bytes.NewBuffer(pk.SignKey.Bytes()) - msg.Write(pk.VrfKey.Bytes()) - return msg.Bytes() + bz, err := cdc.MarshalBinaryBare(pk) + if err != nil { + panic(err) + } + return bz } func (pk PubKeyComposite) VerifyBytes(msg []byte, sig []byte) bool { @@ -67,7 +86,7 @@ func (sk PrivKeyComposite) Identity() crypto.PrivKey { } func (sk PrivKeyComposite) Bytes() []byte { - return sk.Identity().Bytes() + return cdc.MustMarshalBinaryBare(sk) } func (sk PrivKeyComposite) Sign(msg []byte) ([]byte, error) { diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 29ca85b35..695a53679 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -185,8 +185,8 @@ func TestEnvironmentalCompatibility(t *testing.T) { cdc.RegisterConcrete(bls.PrivKeyBLS12{}, bls.PrivKeyAminoName, nil) cdc.RegisterConcrete(ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoName, nil) cdc.RegisterConcrete(ed25519.PrivKeyEd25519{}, ed25519.PrivKeyAminoName, nil) - cdc.RegisterConcrete(composite.PubKeyComposite{}, composite.PubKeyCompositeAminoName, nil) - cdc.RegisterConcrete(composite.PrivKeyComposite{}, composite.PrivKeyCompositeAminoName, nil) + cdc.RegisterConcrete(composite.PubKeyComposite{}, composite.PubKeyAminoName, nil) + cdc.RegisterConcrete(composite.PrivKeyComposite{}, composite.PrivKeyAminoName, nil) t.Run("MarshalCompositeKey", func(t *testing.T) { privKey := composite.GenPrivKey() @@ -253,7 +253,7 @@ func TestEnvironmentalCompatibility(t *testing.T) { // compare addresses to assumed value compositePrivKey := composite.NewPrivKeyComposite(blsPrivKey, ed25519PrivKey) compositePubKey := compositePrivKey.PubKey() - address, err := hex.DecodeString("7A68265205CB115AE35A13515C423F1721E87BB4") + address, err := hex.DecodeString("72dd758835404175940f698cf3ddc29dd0d04afa") if err != nil { t.Fatal(err) } diff --git a/crypto/encoding/amino/amino.go b/crypto/encoding/amino/amino.go index 028edc08b..4024c5eb9 100644 --- a/crypto/encoding/amino/amino.go +++ b/crypto/encoding/amino/amino.go @@ -38,7 +38,7 @@ func init() { nameTable[reflect.TypeOf(sr25519.PubKeySr25519{})] = sr25519.PubKeyAminoName nameTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoName nameTable[reflect.TypeOf(multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute - nameTable[reflect.TypeOf(composite.PubKeyComposite{})] = composite.PubKeyCompositeAminoName + nameTable[reflect.TypeOf(composite.PubKeyComposite{})] = composite.PubKeyAminoName } // PubkeyAminoName returns the amino route of a pubkey @@ -64,7 +64,7 @@ func RegisterAmino(cdc *amino.Codec) { cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, nil) cdc.RegisterConcrete(composite.PubKeyComposite{}, - composite.PubKeyCompositeAminoName, nil) + composite.PubKeyAminoName, nil) cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) cdc.RegisterConcrete(bls.PrivKeyBLS12{}, @@ -76,7 +76,7 @@ func RegisterAmino(cdc *amino.Codec) { cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{}, secp256k1.PrivKeyAminoName, nil) cdc.RegisterConcrete(composite.PrivKeyComposite{}, - composite.PrivKeyCompositeAminoName, nil) + composite.PrivKeyAminoName, nil) } // RegisterKeyType registers an external key type to allow decoding it from bytes diff --git a/crypto/encoding/amino/encode_test.go b/crypto/encoding/amino/encode_test.go index e3f38d152..e0ec626d6 100644 --- a/crypto/encoding/amino/encode_test.go +++ b/crypto/encoding/amino/encode_test.go @@ -5,6 +5,10 @@ import ( "reflect" "testing" + "github.com/tendermint/tendermint/crypto/bls" + + "github.com/tendermint/tendermint/crypto/composite" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,7 +35,6 @@ func checkAminoBinary(t *testing.T, src, dst interface{}, size int) { } // Make sure we have the expected length. assert.Equal(t, size, len(bz), "Amino binary size mismatch") - // Unmarshal. err = cdc.UnmarshalBinaryBare(bz, dst) require.Nil(t, err, "%+v", err) @@ -94,10 +97,21 @@ func TestKeyEncodings(t *testing.T) { pubSize: 38, sigSize: 65, }, + { + privKey: bls.GenPrivKey(), + privSize: 37, + pubSize: 53, + sigSize: 97, + }, + { + privKey: *composite.GenPrivKey(), + privSize: 114, + pubSize: 98, + sigSize: 97, + }, } for tcIndex, tc := range cases { - // Check (de/en)codings of PrivKeys. var priv2, priv3 crypto.PrivKey checkAminoBinary(t, tc.privKey, &priv2, tc.privSize) @@ -155,6 +169,8 @@ func TestPubkeyAminoName(t *testing.T) { {ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoName, true}, {sr25519.PubKeySr25519{}, sr25519.PubKeyAminoName, true}, {secp256k1.PubKeySecp256k1{}, secp256k1.PubKeyAminoName, true}, + {bls.PubKeyBLS12{}, bls.PubKeyAminoName, true}, + {composite.PubKeyComposite{}, composite.PubKeyAminoName, true}, {multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, true}, } for i, tc := range tests { diff --git a/libs/bech32/bech32.go b/libs/bech32/bech32.go index a4db86d5f..510ce5f60 100644 --- a/libs/bech32/bech32.go +++ b/libs/bech32/bech32.go @@ -1,27 +1,26 @@ package bech32 import ( - "github.com/btcsuite/btcutil/bech32" "github.com/pkg/errors" ) //ConvertAndEncode converts from a base64 encoded byte string to base32 encoded byte string and then to bech32 func ConvertAndEncode(hrp string, data []byte) (string, error) { - converted, err := bech32.ConvertBits(data, 8, 5, true) + converted, err := ConvertBits(data, 8, 5, true) if err != nil { return "", errors.Wrap(err, "encoding bech32 failed") } - return bech32.Encode(hrp, converted) + return Encode(hrp, converted) } //DecodeAndConvert decodes a bech32 encoded string and converts to base64 encoded bytes func DecodeAndConvert(bech string) (string, []byte, error) { - hrp, data, err := bech32.Decode(bech) + hrp, data, err := Decode(bech) if err != nil { return "", nil, errors.Wrap(err, "decoding bech32 failed") } - converted, err := bech32.ConvertBits(data, 5, 8, false) + converted, err := ConvertBits(data, 5, 8, false) if err != nil { return "", nil, errors.Wrap(err, "decoding bech32 failed") } diff --git a/libs/bech32/bech32plus.go b/libs/bech32/bech32plus.go new file mode 100644 index 000000000..a14822135 --- /dev/null +++ b/libs/bech32/bech32plus.go @@ -0,0 +1,257 @@ +// TODO our copyright + +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "fmt" + "strings" +) + +const ( + charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + LIMIT = 200 +) + +var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + +// Decode decodes a bech32 encoded string, returning the human-readable +// part and the data part excluding the checksum. +func Decode(bech string) (string, []byte, error) { + // The maximum allowed length for a bech32 string is LIMIT which is constant variable. It must also + // be at least 8 characters, since it needs a non-empty HRP, a + // separator, and a 6 character checksum. + if len(bech) < 8 || len(bech) > LIMIT { + return "", nil, fmt.Errorf("invalid bech32 string length %d", + len(bech)) + } + // Only ASCII characters between 33 and 126 are allowed. + for i := 0; i < len(bech); i++ { + if bech[i] < 33 || bech[i] > 126 { + return "", nil, fmt.Errorf("invalid character in "+ + "string: '%c'", bech[i]) + } + } + + // The characters must be either all lowercase or all uppercase. + lower := strings.ToLower(bech) + upper := strings.ToUpper(bech) + if bech != lower && bech != upper { + return "", nil, fmt.Errorf("string not all lowercase or all " + + "uppercase") + } + + // We'll work with the lowercase string from now on. + bech = lower + + // The string is invalid if the last '1' is non-existent, it is the + // first character of the string (no human-readable part) or one of the + // last 6 characters of the string (since checksum cannot contain '1'), + // or if the string is more than 90 characters in total. + one := strings.LastIndexByte(bech, '1') + if one < 1 || one+7 > len(bech) { + return "", nil, fmt.Errorf("invalid index of 1") + } + + // The human-readable part is everything before the last '1'. + hrp := bech[:one] + data := bech[one+1:] + + // Each character corresponds to the byte with value of the index in + // 'charset'. + decoded, err := toBytes(data) + if err != nil { + return "", nil, fmt.Errorf("failed converting data to bytes: "+ + "%v", err) + } + + if !bech32VerifyChecksum(hrp, decoded) { + moreInfo := "" + checksum := bech[len(bech)-6:] + expected, err := toChars(bech32Checksum(hrp, + decoded[:len(decoded)-6])) + if err == nil { + moreInfo = fmt.Sprintf("Expected %v, got %v.", + expected, checksum) + } + return "", nil, fmt.Errorf("checksum failed. " + moreInfo) + } + + // We exclude the last 6 bytes, which is the checksum. + return hrp, decoded[:len(decoded)-6], nil +} + +// Encode encodes a byte slice into a bech32 string with the +// human-readable part hrb. Note that the bytes must each encode 5 bits +// (base32). +func Encode(hrp string, data []byte) (string, error) { + // Calculate the checksum of the data and append it at the end. + checksum := bech32Checksum(hrp, data) + combined := append(data, checksum...) + + // The resulting bech32 string is the concatenation of the hrp, the + // separator 1, data and checksum. Everything after the separator is + // represented using the specified charset. + dataChars, err := toChars(combined) + if err != nil { + return "", fmt.Errorf("unable to convert data bytes to chars: "+ + "%v", err) + } + return hrp + "1" + dataChars, nil +} + +// toBytes converts each character in the string 'chars' to the value of the +// index of the correspoding character in 'charset'. +func toBytes(chars string) ([]byte, error) { + decoded := make([]byte, 0, len(chars)) + for i := 0; i < len(chars); i++ { + index := strings.IndexByte(charset, chars[i]) + if index < 0 { + return nil, fmt.Errorf("invalid character not part of "+ + "charset: %v", chars[i]) + } + decoded = append(decoded, byte(index)) + } + return decoded, nil +} + +// toChars converts the byte slice 'data' to a string where each byte in 'data' +// encodes the index of a character in 'charset'. +func toChars(data []byte) (string, error) { + result := make([]byte, 0, len(data)) + for _, b := range data { + if int(b) >= len(charset) { + return "", fmt.Errorf("invalid data byte: %v", b) + } + result = append(result, charset[b]) + } + return string(result), nil +} + +// ConvertBits converts a byte slice where each byte is encoding fromBits bits, +// to a byte slice where each byte is encoding toBits bits. +func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) { + if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { + return nil, fmt.Errorf("only bit groups between 1 and 8 allowed") + } + + // The final bytes, each byte encoding toBits bits. + var regrouped []byte + + // Keep track of the next byte we create and how many bits we have + // added to it out of the toBits goal. + nextByte := byte(0) + filledBits := uint8(0) + + for _, b := range data { + + // Discard unused bits. + b = b << (8 - fromBits) + + // How many bits remaining to extract from the input data. + remFromBits := fromBits + for remFromBits > 0 { + // How many bits remaining to be added to the next byte. + remToBits := toBits - filledBits + + // The number of bytes to next extract is the minimum of + // remFromBits and remToBits. + toExtract := remFromBits + if remToBits < toExtract { + toExtract = remToBits + } + + // Add the next bits to nextByte, shifting the already + // added bits to the left. + nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) + + // Discard the bits we just extracted and get ready for + // next iteration. + b = b << toExtract + remFromBits -= toExtract + filledBits += toExtract + + // If the nextByte is completely filled, we add it to + // our regrouped bytes and start on the next byte. + if filledBits == toBits { + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + } + } + + // We pad any unfinished group if specified. + if pad && filledBits > 0 { + nextByte = nextByte << (toBits - filledBits) + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + + // Any incomplete group must be <= 4 bits, and all zeroes. + if filledBits > 0 && (filledBits > 4 || nextByte != 0) { + return nil, fmt.Errorf("invalid incomplete group") + } + + return regrouped, nil +} + +// For more details on the checksum calculation, please refer to BIP 173. +func bech32Checksum(hrp string, data []byte) []byte { + // Convert the bytes to list of integers, as this is needed for the + // checksum calculation. + integers := make([]int, len(data)) + for i, b := range data { + integers[i] = int(b) + } + values := append(bech32HrpExpand(hrp), integers...) + values = append(values, []int{0, 0, 0, 0, 0, 0}...) + polymod := bech32Polymod(values) ^ 1 + var res []byte + for i := 0; i < 6; i++ { + res = append(res, byte((polymod>>uint(5*(5-i)))&31)) + } + return res +} + +// For more details on the polymod calculation, please refer to BIP 173. +func bech32Polymod(values []int) int { + chk := 1 + for _, v := range values { + b := chk >> 25 + chk = (chk&0x1ffffff)<<5 ^ v + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + } + return chk +} + +// For more details on HRP expansion, please refer to BIP 173. +func bech32HrpExpand(hrp string) []int { + v := make([]int, 0, len(hrp)*2+1) + for i := 0; i < len(hrp); i++ { + v = append(v, int(hrp[i]>>5)) + } + v = append(v, 0) + for i := 0; i < len(hrp); i++ { + v = append(v, int(hrp[i]&31)) + } + return v +} + +// For more details on the checksum verification, please refer to BIP 173. +func bech32VerifyChecksum(hrp string, data []byte) bool { + integers := make([]int, len(data)) + for i, b := range data { + integers[i] = int(b) + } + concat := append(bech32HrpExpand(hrp), integers...) + return bech32Polymod(concat) == 1 +} diff --git a/libs/bech32/bech32plus_test.go b/libs/bech32/bech32plus_test.go new file mode 100644 index 000000000..9aca390b4 --- /dev/null +++ b/libs/bech32/bech32plus_test.go @@ -0,0 +1,74 @@ +// TODO our copyright + +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32_test + +import ( + "strings" + "testing" + + "github.com/tendermint/tendermint/libs/bech32" +) + +func TestBech32(t *testing.T) { + tests := []struct { + str string + valid bool + }{ + {"A12UEL5L", true}, + {"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", true}, + {"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", true}, + {"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", true}, + {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", true}, + {"split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", false}, // invalid checksum + {"s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", false}, // invalid character (space) in hrp + {"spl" + string(rune(127)) + "t1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", false}, // invalid character (DEL) in hrp + {"split1cheo2y9e2w", false}, // invalid character (o) in data part + {"split1a2y9w", false}, // too short data part + {"1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", false}, // empty hrp + // 165 characters plus using LIMIT + {"linkvalconspub1668lhsfs4zxq4gakkq5yj8ujn22g43s7qz7498el7k57cw88t30y56sxa6eszusd470ncx3j79slqg3aphy2c93ymejzqxzpu36pjrv5u0wphpf5eluc7am08m0ekkfns4jcnrj7u2hc5f6240tlze", true}, + // 201 characters plus using LIMIT + {"linkvalconspub1668lhsfs4zxq4gakkq5yj8ujn22g43s7qz7498el7k57cw88t30y56sxa6eszusd470ncx3j79slqg3aphy2c93ymejzqxzpu36pjrv5u0wphpf5eluc7am08m0ekkfns4jcnrj7u2hc5f6240tlze668lhsfs4zxq4gakkq5yj8ujn22g43s7qz74", false}, + } + + for _, test := range tests { + str := test.str + hrp, decoded, err := bech32.Decode(str) + if !test.valid { + // Invalid string decoding should result in error. + if err == nil { + t.Error("expected decoding to fail for "+ + "invalid string %v", test.str) + } + continue + } + + // Valid string decoding should result in no error. + if err != nil { + t.Errorf("expected string to be valid bech32: %v", err) + } + + // Check that it encodes to the same string + encoded, err := bech32.Encode(hrp, decoded) + if err != nil { + t.Errorf("encoding failed: %v", err) + } + + if encoded != strings.ToLower(str) { + t.Errorf("expected data to encode to %v, but got %v", + str, encoded) + } + + // Flip a bit in the string an make sure it is caught. + pos := strings.LastIndexAny(str, "1") + flipped := str[:pos+1] + string((str[pos+1] ^ 1)) + str[pos+2:] + _, _, err = bech32.Decode(flipped) + if err == nil { + t.Error("expected decoding to fail") + } + } +} diff --git a/libs/bech32/exampleplus_test.go b/libs/bech32/exampleplus_test.go new file mode 100644 index 000000000..4f8e021da --- /dev/null +++ b/libs/bech32/exampleplus_test.go @@ -0,0 +1,90 @@ +// TODO our copyright + +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32_test + +import ( + "encoding/hex" + "fmt" + "strings" + "testing" + + "github.com/tendermint/tendermint/libs/bech32" +) + +// This example demonstrates how to decode a bech32 encoded string. +func TestExampleDecode(t *testing.T) { + encoded := "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx" + hrp, decoded, err := bech32.Decode(encoded) + if err != nil { + fmt.Println("Error:", err) + } + + // Show the decoded data. + fmt.Println("Decoded human-readable part:", hrp) + fmt.Println("Decoded Data:", hex.EncodeToString(decoded)) + + // Output: + // Decoded human-readable part: bc + // Decoded Data: 010e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e160e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16 +} + +// This example demonstrates how to encode data into a bech32 string. +func TestExampleEncode(t *testing.T) { + data := []byte("Test data") + // Convert test data to base32: + conv, err := bech32.ConvertBits(data, 8, 5, true) + if err != nil { + fmt.Println("Error:", err) + } + fmt.Println(conv) + encoded, err := bech32.Encode("customHrp!11111q", conv) + if err != nil { + fmt.Println("Error:", err) + } + + // Show the encoded data. + fmt.Println("Encoded Data:", encoded) + + // Output: + // Encoded Data: customHrp!11111q123jhxapqv3shgcgumastr +} + +// This example demonstrates how to encode data into a bech32 string. +func TestExampleEncodeDecode(t *testing.T) { + // ------ encode -------- + data := []byte("Test data") + // Convert test data to base32: + conv, err := bech32.ConvertBits(data, 8, 5, true) + if err != nil { + fmt.Println("Error:", err) + } + fmt.Println(conv) + encoded, err := bech32.Encode("linkvalconspub", conv) + if err != nil { + fmt.Println("Error:", err) + } + + // Show the encoded data. + fmt.Println("Encoded Data:", encoded) + + // -------- decode --------------- + // kokeshi Error: string not all lowercase or all uppercase + encoded = strings.ToLower(encoded) + + // decode start + hrp, decoded, err := bech32.Decode(encoded) + if err != nil { + fmt.Println("Error:", err) + } + + // Show the decoded data. + fmt.Println("Decoded human-readable part:", hrp) + fmt.Println("Decoded Data:", hex.EncodeToString(decoded)) + + // Output: + // Encoded Data: customHrp!11111q123jhxapqv3shgcgumastr +} diff --git a/types/genesis_test.go b/types/genesis_test.go index 16961ea7b..c81faf83f 100644 --- a/types/genesis_test.go +++ b/types/genesis_test.go @@ -3,8 +3,11 @@ package types import ( "io/ioutil" "os" + "reflect" "testing" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -23,6 +26,28 @@ func TestGenesisBad(t *testing.T) { []byte( `{"validators":[{"pub_key":{"value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="},"power":"10","name":""}]}`, ), + // missing pub_key type with composite + []byte( + `{"validators":[{"pub_key":{"value": ` + + `{"sign": {"type": "tendermint/PubKeyBLS12",` + + `"value": "mD+qWAgrBtbtQIxnQZ32LbbGvNJfo87gvbp9bGrtjSRRCdleoG/YuZJtCmv5rd1p"},` + + `"vrf": {"type": "tendermint/PubKeyEd25519","value": "KKGRprl+IjD3fG7KZ9YF+3oVpD+LvE2cd5m5N54dnKM="}}},` + + `"power":"10","name":""}]}`, + ), + // missing pub_key type with composite sign + []byte( + `{"validators":[{"pub_key":{"type": "tendermint/PubKeyComposite","value": ` + + `{"sign": {"value": "mD+qWAgrBtbtQIxnQZ32LbbGvNJfo87gvbp9bGrtjSRRCdleoG/YuZJtCmv5rd1p"},` + + `"vrf": {"type": "tendermint/PubKeyEd25519","value": "KKGRprl+IjD3fG7KZ9YF+3oVpD+LvE2cd5m5N54dnKM="}}},` + + `"power":"10","name":""}]}`, + ), + // missing pub_key type with composite vrf + []byte( + `{"validators":[{"pub_key":{"type": "tendermint/PubKeyComposite","value": ` + + `{"sign": {"type": "tendermint/PubKeyBLS12",` + + `"value": "mD+qWAgrBtbtQIxnQZ32LbbGvNJfo87gvbp9bGrtjSRRCdleoG/YuZJtCmv5rd1p"},` + + `"vrf": {"value": "KKGRprl+IjD3fG7KZ9YF+3oVpD+LvE2cd5m5N54dnKM="}}},"power":"10","name":""}]}`, + ), // missing chain_id []byte( `{"validators":[` + @@ -120,6 +145,52 @@ func TestGenesisGood(t *testing.T) { } } +func TestGenesisGoodComposite(t *testing.T) { + // test a good one by raw json with composite + genDocBytes := []byte( + `{"genesis_time":"0001-01-01T00:00:00Z","chain_id":"test-chain-QDKdJr","consensus_params":null,"validators":[` + + `{"pub_key":{"type": "tendermint/PubKeyComposite","value": ` + + `{"sign": {"type": "tendermint/PubKeyBLS12",` + + `"value": "mD+qWAgrBtbtQIxnQZ32LbbGvNJfo87gvbp9bGrtjSRRCdleoG/YuZJtCmv5rd1p"},` + + `"vrf": {"type": "tendermint/PubKeyEd25519","value": "KKGRprl+IjD3fG7KZ9YF+3oVpD+LvE2cd5m5N54dnKM="}}},` + + `"power":"10","name":""}` + `],"voter_params":null, "app_hash":"","app_state":{"account_owner": "Bob"}}`) + _, err := GenesisDocFromJSON(genDocBytes) + assert.NoError(t, err, "expected no error for good genDoc json") + + pubkey := composite.GenPrivKey().PubKey() + // create a base gendoc from struct with composite + baseGenDoc := &GenesisDoc{ + ChainID: "abc", + Validators: []GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}}, + } + genDocBytes, err = cdc.MarshalJSON(baseGenDoc) + assert.NoError(t, err, "error marshalling genDoc") + + // test base gendoc and check consensus params were filled + genDoc, err := GenesisDocFromJSON(genDocBytes) + assert.NoError(t, err, "expected no error for valid genDoc json") + assert.NotNil(t, genDoc.ConsensusParams, "expected consensus params to be filled in") + + // check validator's address is filled + assert.NotNil(t, genDoc.Validators[0].Address, "expected validator's address to be filled in") + + // check validator's key type is composite + assert.EqualValues(t, reflect.TypeOf(composite.PubKeyComposite{}), reflect.TypeOf(genDoc.Validators[0].PubKey)) + + // create json with consensus params filled + genDocBytes, err = cdc.MarshalJSON(genDoc) + assert.NoError(t, err, "error marshalling genDoc") + genDoc, err = GenesisDocFromJSON(genDocBytes) + assert.NoError(t, err, "expected no error for valid genDoc json") + + // test with invalid consensus params + genDoc.ConsensusParams.Block.MaxBytes = 0 + genDocBytes, err = cdc.MarshalJSON(genDoc) + assert.NoError(t, err, "error marshalling genDoc") + _, err = GenesisDocFromJSON(genDocBytes) + assert.Error(t, err, "expected error for genDoc json with block size of 0") +} + func TestGenesisSaveAs(t *testing.T) { tmpfile, err := ioutil.TempFile("", "genesis") require.NoError(t, err) diff --git a/types/protobuf.go b/types/protobuf.go index 0b2d6f709..e4d5850c0 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -32,7 +32,7 @@ const ( // TODO: Make non-global by allowing for registration of more pubkey types var ABCIPubKeyTypesToAminoNames = map[string]string{ - ABCIPubKeyTypeComposite: composite.PubKeyCompositeAminoName, + ABCIPubKeyTypeComposite: composite.PubKeyAminoName, ABCIPubKeyTypeEd25519: ed25519.PubKeyAminoName, ABCIPubKeyTypeSr25519: sr25519.PubKeyAminoName, ABCIPubKeyTypeSecp256k1: secp256k1.PubKeyAminoName,