From 4242d3d93b9b221155f84e6a5456c33f204e975f Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Thu, 3 Sep 2020 17:52:12 +0900 Subject: [PATCH 01/36] added signature aggregation and verify to block --- crypto/bls/bls.go | 73 ++++++++++++- crypto/bls/bls_test.go | 233 ++++++++++++++++++++++++++++++++++++++++ proto/types/types.pb.go | 177 +++++++++++++++--------------- proto/types/types.proto | 5 +- types/block.go | 78 ++++++++++++-- types/block_test.go | 65 +++++++++++ types/vote.go | 2 +- types/vote_set.go | 19 +++- types/voter_set.go | 21 +++- 9 files changed, 573 insertions(+), 100 deletions(-) diff --git a/crypto/bls/bls.go b/crypto/bls/bls.go index 4744ef947..acb99bcd0 100644 --- a/crypto/bls/bls.go +++ b/crypto/bls/bls.go @@ -2,6 +2,7 @@ package bls import ( "bytes" + "crypto/sha512" "crypto/subtle" "fmt" @@ -46,6 +47,72 @@ func init() { // PrivKeyBLS12 implements crypto.PrivKey. type PrivKeyBLS12 [PrivKeyBLS12Size]byte +// AddSignature adds a BLS signature to the init. When the init is nil, then a new aggregate signature is built +// from specified signature. +func AddSignature(init []byte, signature []byte) (aggrSign []byte, err error) { + if init == nil { + blsSign := bls.Sign{} + init = blsSign.Serialize() + } else if len(init) != SignatureSize { + err = fmt.Errorf("invalid BLS signature: aggregated signature size %d is not valid size %d", + len(init), SignatureSize) + return + } + if len(signature) != SignatureSize { + err = fmt.Errorf("invalid BLS signature: signature size %d is not valid size %d", + len(signature), SignatureSize) + return + } + blsSign := bls.Sign{} + err = blsSign.Deserialize(signature) + if err != nil { + return + } + aggrBLSSign := bls.Sign{} + err = aggrBLSSign.Deserialize(init) + if err != nil { + return + } + aggrBLSSign.Add(&blsSign) + aggrSign = aggrBLSSign.Serialize() + return +} + +func VerifyAggregatedSignature(aggregatedSignature []byte, pubKeys []PubKeyBLS12, msgs [][]byte) error { + if len(pubKeys) != len(msgs) { + return fmt.Errorf("the number of public keys %d doesn't match the one of messages %d", + len(pubKeys), len(msgs)) + } + if aggregatedSignature == nil { + if len(pubKeys) == 0 { + return nil + } else { + return fmt.Errorf( + "the aggregate signature was omitted, even though %d public keys were specified", len(pubKeys)) + } + } + aggrSign := bls.Sign{} + err := aggrSign.Deserialize(aggregatedSignature) + if err != nil { + return err + } + blsPubKeys := make([]bls.PublicKey, len(pubKeys)) + hashes := make([][]byte, len(msgs)) + for i := 0; i < len(pubKeys); i++ { + blsPubKeys[i] = bls.PublicKey{} + err = blsPubKeys[i].Deserialize(pubKeys[i][:]) + if err != nil { + return err + } + hash := sha512.Sum512_256(msgs[i]) + hashes[i] = hash[:] + } + if !aggrSign.VerifyAggregateHashes(blsPubKeys, hashes) { + return fmt.Errorf("failed to verify the aggregated hashes by %d public keys", len(blsPubKeys)) + } + return nil +} + // GenPrivKey generates a new BLS12-381 private key. func GenPrivKey() PrivKeyBLS12 { sigKey := bls.SecretKey{} @@ -74,7 +141,8 @@ func (privKey PrivKeyBLS12) Sign(msg []byte) ([]byte, error) { if err != nil { panic(fmt.Sprintf("Failed to copy the private key: %s", err)) } - sign := blsKey.SignByte(msg) + hash := sha512.Sum512_256(msg) + sign := blsKey.SignHash(hash[:]) return sign.Serialize(), nil } @@ -143,7 +211,8 @@ func (pubKey PubKeyBLS12) VerifyBytes(msg []byte, sig []byte) bool { if err != nil { return false } - return blsSign.VerifyByte(&blsPubKey, msg) + hash := sha512.Sum512_256(msg) + return blsSign.VerifyHash(&blsPubKey, hash[:]) } // VRFVerify is not supported in BLS12. diff --git a/crypto/bls/bls_test.go b/crypto/bls/bls_test.go index f00df1da6..2070f32e9 100644 --- a/crypto/bls/bls_test.go +++ b/crypto/bls/bls_test.go @@ -2,8 +2,11 @@ package bls_test import ( "bytes" + "crypto/sha256" "fmt" + "math/rand" "testing" + "time" b "github.com/herumi/bls-eth-go-binary/bls" "github.com/stretchr/testify/assert" @@ -234,6 +237,236 @@ func TestBasicSignatureFunctions(t *testing.T) { } } +func TestSignatureAggregationAndVerify(t *testing.T) { + privKeys := make([]b.SecretKey, 25) + pubKeys := make([]b.PublicKey, len(privKeys)) + msgs := make([][]byte, len(privKeys)) + hash32s := make([][32]byte, len(privKeys)) + signatures := make([]b.Sign, len(privKeys)) + for i, privKey := range privKeys { + privKey.SetByCSPRNG() + pubKeys[i] = *privKey.GetPublicKey() + msgs[i] = []byte(fmt.Sprintf("hello, world #%d", i)) + hash32s[i] = sha256.Sum256(msgs[i]) + signatures[i] = *privKey.SignHash(hash32s[i][:]) + + // normal single-hash case + if !signatures[i].VerifyHash(&pubKeys[i], hash32s[i][:]) { + t.Fail() + } + + // in case where 1-bit of hash was garbled + garbledHash := make([]byte, len(msgs[i])) + copy(garbledHash, msgs[i]) + garbledHash[0] ^= 1 << 0 + if garbledHash[0] == msgs[i][0] || signatures[i].VerifyByte(&pubKeys[i], garbledHash) { + t.Fail() + } + + // Verification using an invalid public key + } + + // aggregation + multiSig := b.Sign{} + multiSig.Aggregate(signatures) + + // normal case + hashes := make([][]byte, len(privKeys)) + for i := 0; i < len(hashes); i++ { + hashes[i] = hash32s[i][:] + } + if !multiSig.VerifyAggregateHashes(pubKeys, hashes) { + t.Fatalf("failed to validate the aggregate signature of the hashed message") + } + + // in case where 1-bit of signature was garbled + multiSigBytes := multiSig.Serialize() + for i := range multiSigBytes { + for j := 0; j < 8; j++ { + garbledMultiSigBytes := make([]byte, len(multiSigBytes)) + copy(garbledMultiSigBytes, multiSigBytes) + garbledMultiSigBytes[i] ^= 1 << j + if garbledMultiSigBytes[i] == multiSigBytes[i] { + t.Fail() + } + garbledMultiSig := b.Sign{} + err := garbledMultiSig.Deserialize(garbledMultiSigBytes) + if err == nil { + // Note that in some cases Deserialize() fails + if garbledMultiSig.VerifyAggregateHashes(pubKeys, hashes) { + t.Errorf("successfully verified the redacted signature") + } + } + } + } + + // in case a public key used for verification is replaced + invalidPrivKey := b.SecretKey{} + invalidPrivKey.SetByCSPRNG() + invalidPubKeys := make([]b.PublicKey, len(pubKeys)) + copy(invalidPubKeys, pubKeys) + invalidPubKeys[len(invalidPubKeys)-1] = *invalidPrivKey.GetPublicKey() + if multiSig.VerifyAggregateHashes(invalidPubKeys, hashes) { + t.Fatalf("successfully verified that it contains a public key that was not involved in the signing") + } + + // in case a hash used for verification is replaced + invalidHashes := make([][]byte, len(hashes)) + copy(invalidHashes, hashes) + invalidHash := sha256.Sum256([]byte("hello, world #99")) + invalidHashes[len(invalidHashes)-1] = invalidHash[:] + if multiSig.VerifyAggregateHashes(pubKeys, invalidHashes) { + t.Fatalf("successfully verified that it contains a hash that was not involved in the signing") + } +} + +func generatePubKeysAndSigns(t *testing.T, size int) ([]bls.PubKeyBLS12, [][]byte, [][]byte) { + pubKeys := make([]bls.PubKeyBLS12, size) + msgs := make([][]byte, len(pubKeys)) + sigs := make([][]byte, len(pubKeys)) + for i := 0; i < len(pubKeys); i++ { + var err error + privKey := bls.GenPrivKey() + pubKeys[i] = blsPublicKey(t, privKey.PubKey()) + msgs[i] = []byte(fmt.Sprintf("hello, workd #%d", i)) + sigs[i], err = privKey.Sign(msgs[i]) + if err != nil { + t.Fatal(fmt.Sprintf("fail to sign: %s", err)) + } + if !pubKeys[i].VerifyBytes(msgs[i], sigs[i]) { + t.Fatal("fail to verify signature") + } + } + return pubKeys, msgs, sigs +} + +func blsPublicKey(t *testing.T, pubKey crypto.PubKey) bls.PubKeyBLS12 { + blsPubKey, ok := pubKey.(bls.PubKeyBLS12) + if !ok { + t.Fatal(fmt.Sprintf("specified public key is not for BLS")) + } + return blsPubKey +} +func aggregateSignatures(init []byte, signatures [][]byte) (aggrSig []byte, err error) { + aggrSig = init + for _, sign := range signatures { + aggrSig, err = bls.AddSignature(aggrSig, sign) + if err != nil { + return + } + } + return +} + +func TestAggregatedSignature(t *testing.T) { + + // generate BLS signatures and public keys + pubKeys, msgs, sigs := generatePubKeysAndSigns(t, 25) + + // aggregate signatures + aggrSig, err := aggregateSignatures(nil, sigs) + if err != nil { + t.Error(fmt.Sprintf("fail to aggregate BLS signatures: %s", err)) + } + if len(aggrSig) != bls.SignatureSize { + t.Error(fmt.Sprintf("inconpatible signature size: %d != %d", len(aggrSig), bls.SignatureSize)) + } + + // validate the aggregated signature + if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, msgs); err != nil { + t.Errorf("fail to verify aggregated signature: %s", err) + } + + // validate with the public keys and messages pair in random order + t.Run("Doesn't Depend on the Order of PublicKey-Message Pairs", func(t *testing.T) { + shuffledPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + shuffledMsgs := make([][]byte, len(msgs)) + copy(shuffledPubKeys, pubKeys) + copy(shuffledMsgs, msgs) + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(shuffledPubKeys), func(i, j int) { + shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] + shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] + }) + if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, shuffledMsgs); err != nil { + t.Errorf("fail to verify the aggregated signature with random order: %s", err) + } + }) + + // validate with the public keys in random order + t.Run("Incorrect Public Key Order", func(t *testing.T) { + shuffledPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + copy(shuffledPubKeys, pubKeys) + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(shuffledPubKeys), func(i, j int) { shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] }) + if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, msgs); err == nil { + t.Error("successfully validated with public keys of different order") + } + }) + + // validate with the messages in random order + t.Run("Incorrect Message Order", func(t *testing.T) { + shuffledMsgs := make([][]byte, len(msgs)) + copy(shuffledMsgs, msgs) + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(shuffledMsgs), func(i, j int) { shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] }) + if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, shuffledMsgs); err == nil { + t.Error("successfully validated with messages of different order") + } + }) + + // replace one public key with another and detect + t.Run("Replace One Public Key", func(t *testing.T) { + pubKey, _ := bls.GenPrivKey().PubKey().(bls.PubKeyBLS12) + replacedPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + copy(replacedPubKeys, pubKeys) + replacedPubKeys[0] = pubKey + if err := bls.VerifyAggregatedSignature(aggrSig, replacedPubKeys, msgs); err == nil { + t.Error("verification with an invalid key was successful") + } + }) + + // replace one message with another and detect + t.Run("Replace One Message", func(t *testing.T) { + msg := []byte(fmt.Sprintf("hello, world #%d replaced", len(msgs))) + replacedMsgs := make([][]byte, len(msgs)) + copy(replacedMsgs, msgs) + replacedMsgs[0] = msg + if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, replacedMsgs); err == nil { + t.Error("verification with an invalid message was successful") + } + }) + + // add new signature to existing aggregated signature and verify + t.Run("Incremental Update", func(t *testing.T) { + msg := []byte(fmt.Sprintf("hello, world #%d", len(msgs))) + privKey := bls.GenPrivKey() + pubKey := privKey.PubKey() + sig, err := privKey.Sign(msg) + assert.Nilf(t, err, "%s", err) + newAggrSig, _ := aggregateSignatures(aggrSig, [][]byte{sig}) + newPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + copy(newPubKeys, pubKeys) + newPubKeys = append(newPubKeys, blsPublicKey(t, pubKey)) + newMsgs := make([][]byte, len(msgs)) + copy(newMsgs, msgs) + newMsgs = append(newMsgs, msg) + if err := bls.VerifyAggregatedSignature(newAggrSig, newPubKeys, newMsgs); err != nil { + t.Errorf("fail to verify the aggregate signature with the new signature: %s", err) + } + }) + + // nil is returned for nil and empty signature + nilSig, _ := aggregateSignatures(nil, [][]byte{}) + assert.Nil(t, nilSig) + + // a non-BLS signature contained + func() { + _, err = aggregateSignatures(nil, [][]byte{make([]byte, 0)}) + assert.NotNil(t, err) + }() +} + func TestSignatureAggregation(t *testing.T) { publicKeys := make([]b.PublicKey, 25) aggregatedSignature := b.Sign{} diff --git a/proto/types/types.pb.go b/proto/types/types.pb.go index cbc58d4ad..6b7c0c6e9 100644 --- a/proto/types/types.pb.go +++ b/proto/types/types.pb.go @@ -540,8 +540,9 @@ type Commit struct { Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` BlockID BlockID `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id"` Signatures []CommitSig `protobuf:"bytes,4,rep,name=signatures,proto3" json:"signatures"` - Hash []byte `protobuf:"bytes,5,opt,name=hash,proto3" json:"hash,omitempty"` - BitArray *bits.BitArray `protobuf:"bytes,6,opt,name=bit_array,json=bitArray,proto3" json:"bit_array,omitempty"` + AggregatedSignature []byte `protobuf:"bytes,5,opt,name=aggregated_signature,json=aggregatedSignature,proto3" json:"aggregated_signature,omitempty"` + Hash []byte `protobuf:"bytes,6,opt,name=hash,proto3" json:"hash,omitempty"` + BitArray *bits.BitArray `protobuf:"bytes,7,opt,name=bit_array,json=bitArray,proto3" json:"bit_array,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -599,6 +600,13 @@ func (m *Commit) GetSignatures() []CommitSig { return nil } +func (m *Commit) GetAggregatedSignature() []byte { + if m != nil { + return m.AggregatedSignature + } + return nil +} + func (m *Commit) GetHash() []byte { if m != nil { return m.Hash @@ -889,86 +897,87 @@ func init() { func init() { proto.RegisterFile("proto/types/types.proto", fileDescriptor_ff06f8095857fb18) } var fileDescriptor_ff06f8095857fb18 = []byte{ - // 1286 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6e, 0xdb, 0xc6, - 0x13, 0x37, 0x25, 0xca, 0x92, 0x86, 0x92, 0x2d, 0xf3, 0xef, 0x7f, 0xa2, 0xca, 0xad, 0xa5, 0xc8, - 0x4d, 0xea, 0x7c, 0x80, 0x2a, 0x5c, 0xa0, 0x68, 0x80, 0x5e, 0x24, 0xdb, 0x71, 0x84, 0xd8, 0xb2, - 0x40, 0xa9, 0xe9, 0xc7, 0x85, 0x58, 0x89, 0x1b, 0x8a, 0x08, 0x45, 0x12, 0xdc, 0x95, 0x61, 0xa7, - 0x40, 0xcf, 0x85, 0x4f, 0x7d, 0x01, 0x5f, 0x9a, 0x16, 0xe8, 0x5b, 0xb4, 0xc7, 0x9e, 0xfa, 0x08, - 0x29, 0x90, 0xbe, 0x42, 0x1f, 0xa0, 0xd8, 0x0f, 0x52, 0x52, 0x64, 0xb7, 0x41, 0x93, 0x5e, 0x12, - 0xee, 0xcc, 0x6f, 0x66, 0x77, 0x7e, 0xf3, 0x9b, 0x5d, 0x19, 0xae, 0x87, 0x51, 0x40, 0x83, 0x06, - 0x3d, 0x0b, 0x31, 0x11, 0xff, 0x1a, 0xdc, 0xa2, 0x5f, 0xa3, 0xd8, 0xb7, 0x71, 0x34, 0x76, 0x7d, - 0x2a, 0x2c, 0x06, 0xf7, 0x56, 0x6e, 0xd1, 0x91, 0x1b, 0xd9, 0x56, 0x88, 0x22, 0x7a, 0xd6, 0x10, - 0xc1, 0x4e, 0xe0, 0x04, 0xd3, 0x2f, 0x81, 0xae, 0x54, 0x9d, 0x20, 0x70, 0x3c, 0x2c, 0x20, 0x83, - 0xc9, 0x93, 0x06, 0x75, 0xc7, 0x98, 0x50, 0x34, 0x0e, 0x25, 0x60, 0x43, 0x84, 0x78, 0xee, 0x80, - 0x34, 0x06, 0x2e, 0x9d, 0xdb, 0xbd, 0x52, 0x15, 0xce, 0x61, 0x74, 0x16, 0xd2, 0xa0, 0x31, 0xc6, - 0xd1, 0x53, 0x0f, 0xcf, 0x01, 0x64, 0xf4, 0x09, 0x8e, 0x88, 0x1b, 0xf8, 0xf1, 0xff, 0xc2, 0x59, - 0xbf, 0x0f, 0xc5, 0x2e, 0x8a, 0x68, 0x0f, 0xd3, 0x87, 0x18, 0xd9, 0x38, 0xd2, 0xd7, 0x21, 0x43, - 0x03, 0x8a, 0xbc, 0xb2, 0x52, 0x53, 0xb6, 0xd3, 0xa6, 0x58, 0xe8, 0x3a, 0xa8, 0x23, 0x44, 0x46, - 0xe5, 0x54, 0x4d, 0xd9, 0x2e, 0x98, 0xfc, 0xbb, 0xfe, 0x35, 0xa8, 0x2c, 0x94, 0x45, 0xb8, 0xbe, - 0x8d, 0x4f, 0x79, 0x44, 0xd1, 0x14, 0x0b, 0x66, 0x1d, 0x9c, 0x51, 0x4c, 0x64, 0x88, 0x58, 0xe8, - 0x07, 0x90, 0x09, 0xa3, 0x20, 0x78, 0x52, 0x4e, 0xd7, 0x94, 0x6d, 0x6d, 0xe7, 0xae, 0xb1, 0x40, - 0x9d, 0xa8, 0xc3, 0x10, 0x75, 0x18, 0x3d, 0x77, 0x1c, 0x7a, 0xb8, 0xcb, 0x42, 0x5a, 0xea, 0xaf, - 0x2f, 0xaa, 0x4b, 0xa6, 0x88, 0xaf, 0x8f, 0x21, 0xdb, 0xf2, 0x82, 0xe1, 0xd3, 0xf6, 0x5e, 0x72, - 0x36, 0x65, 0x7a, 0x36, 0xbd, 0x03, 0x05, 0x46, 0x3b, 0xb1, 0x46, 0xbc, 0x2a, 0x7e, 0x08, 0x6d, - 0xe7, 0xa6, 0x71, 0x79, 0xa7, 0x8c, 0x39, 0x0a, 0xe4, 0x46, 0x1a, 0x4f, 0x20, 0x4c, 0xf5, 0xef, - 0x33, 0xb0, 0x2c, 0x09, 0xda, 0x85, 0xac, 0xa4, 0x90, 0xef, 0xa8, 0xed, 0x6c, 0x2d, 0x66, 0x8d, - 0x39, 0xde, 0x0d, 0x7c, 0x82, 0x7d, 0x32, 0x21, 0x32, 0x67, 0x1c, 0xa9, 0xdf, 0x82, 0xdc, 0x70, - 0x84, 0x5c, 0xdf, 0x72, 0x6d, 0x7e, 0xb6, 0x7c, 0x4b, 0x7b, 0xf9, 0xa2, 0x9a, 0xdd, 0x65, 0xb6, - 0xf6, 0x9e, 0x99, 0xe5, 0xce, 0xb6, 0xad, 0x5f, 0x83, 0xe5, 0x11, 0x76, 0x9d, 0x11, 0xe5, 0x84, - 0xa5, 0x4d, 0xb9, 0xd2, 0x3f, 0x01, 0x95, 0x89, 0xa4, 0xac, 0xf2, 0x13, 0x54, 0x0c, 0xa1, 0x20, - 0x23, 0x56, 0x90, 0xd1, 0x8f, 0x15, 0xd4, 0xca, 0xb1, 0x8d, 0xbf, 0xfb, 0xbd, 0xaa, 0x98, 0x3c, - 0x42, 0xff, 0x02, 0x8a, 0x1e, 0x22, 0xd4, 0x1a, 0x30, 0xf6, 0xd8, 0xf6, 0x19, 0x9e, 0xa2, 0x7a, - 0x15, 0x35, 0x92, 0xe5, 0xd6, 0xff, 0x58, 0x9e, 0x97, 0x2f, 0xaa, 0xda, 0x21, 0x22, 0x54, 0x1a, - 0x4d, 0xcd, 0x4b, 0x16, 0xb6, 0xbe, 0x0d, 0x25, 0x9e, 0x79, 0x18, 0x8c, 0xc7, 0x2e, 0xb5, 0x78, - 0x4f, 0x96, 0x79, 0x4f, 0x56, 0x98, 0x7d, 0x97, 0x9b, 0x1f, 0xb2, 0xee, 0x6c, 0x40, 0xde, 0x46, - 0x14, 0x09, 0x48, 0x96, 0x43, 0x72, 0xcc, 0xc0, 0x9d, 0x55, 0xd0, 0x4e, 0x02, 0x8a, 0x23, 0x22, - 0xdc, 0x39, 0xee, 0x06, 0x61, 0xe2, 0x80, 0x0f, 0x60, 0xf5, 0x04, 0x79, 0xae, 0x8d, 0x68, 0x10, - 0x83, 0xf2, 0x62, 0x9b, 0xa9, 0x99, 0x03, 0x3f, 0x84, 0x75, 0x1f, 0x9f, 0x52, 0xeb, 0x55, 0x34, - 0x70, 0xb4, 0xce, 0x7c, 0x8f, 0xe7, 0x23, 0x6e, 0xc2, 0xca, 0x30, 0x6e, 0x99, 0xc0, 0x6a, 0x1c, - 0x5b, 0x4c, 0xac, 0x1c, 0xf6, 0x0e, 0xe4, 0x50, 0x18, 0x0a, 0x40, 0x81, 0x03, 0xb2, 0x28, 0x0c, - 0xb9, 0xeb, 0x0e, 0xac, 0x71, 0x12, 0x22, 0x4c, 0x26, 0x1e, 0x95, 0x49, 0x8a, 0x1c, 0xb3, 0xca, - 0x1c, 0xa6, 0xb0, 0x73, 0xec, 0x16, 0x14, 0xf1, 0x89, 0x6b, 0x63, 0x7f, 0x88, 0x05, 0x6e, 0x85, - 0xe3, 0x0a, 0xb1, 0x91, 0x83, 0x6e, 0x43, 0x29, 0x8c, 0x82, 0x30, 0x20, 0x38, 0xb2, 0x90, 0x6d, - 0x47, 0x98, 0x90, 0xf2, 0xaa, 0xc8, 0x17, 0xdb, 0x9b, 0xc2, 0x5c, 0xbf, 0x07, 0xea, 0x1e, 0xa2, - 0x48, 0x2f, 0x41, 0x9a, 0x9e, 0x92, 0xb2, 0x52, 0x4b, 0x6f, 0x17, 0x4c, 0xf6, 0x79, 0xe9, 0xf8, - 0xfe, 0x99, 0x02, 0xf5, 0x71, 0x40, 0xb1, 0x7e, 0x1f, 0x54, 0xd6, 0x6a, 0xae, 0xe6, 0x95, 0xab, - 0x67, 0xa4, 0xe7, 0x3a, 0x3e, 0xb6, 0x8f, 0x88, 0xd3, 0x3f, 0x0b, 0xb1, 0xc9, 0x43, 0x66, 0xe4, - 0x99, 0x9a, 0x93, 0xe7, 0x3a, 0x64, 0xa2, 0x60, 0xe2, 0xdb, 0x52, 0xb5, 0x62, 0xa1, 0x3f, 0x82, - 0x5c, 0xa2, 0x3a, 0xf5, 0xf5, 0x54, 0xb7, 0x2a, 0x55, 0x17, 0x0f, 0xbb, 0x99, 0x1d, 0x48, 0xb5, - 0xb5, 0x20, 0x9f, 0x5c, 0x93, 0x52, 0xc3, 0xaf, 0x37, 0x06, 0xd3, 0x30, 0xfd, 0x2e, 0xac, 0x25, - 0xda, 0x48, 0xc8, 0x15, 0x92, 0x2d, 0x25, 0x0e, 0xc9, 0xee, 0x9c, 0xec, 0x2c, 0x71, 0xe1, 0x65, - 0x79, 0x75, 0x53, 0xd9, 0xb5, 0xf9, 0xcd, 0xf7, 0x2e, 0xe4, 0x89, 0xeb, 0xf8, 0x88, 0x4e, 0x22, - 0x2c, 0xe5, 0x3b, 0x35, 0xd4, 0x9f, 0xa7, 0x60, 0x59, 0x8c, 0xc2, 0x0c, 0x7b, 0xca, 0xe5, 0xec, - 0x31, 0x52, 0x33, 0x97, 0xb1, 0x97, 0x7e, 0x53, 0xf6, 0x0e, 0x00, 0x92, 0x23, 0x91, 0xb2, 0x5a, - 0x4b, 0x6f, 0x6b, 0x3b, 0x37, 0xae, 0x4a, 0x27, 0x8e, 0xdb, 0x73, 0x1d, 0x79, 0x8b, 0xcd, 0x84, - 0x26, 0xca, 0xca, 0xcc, 0x5c, 0xbe, 0x4d, 0xc8, 0x0f, 0x5c, 0x6a, 0xa1, 0x28, 0x42, 0x67, 0x9c, - 0x4e, 0x6d, 0xe7, 0xfd, 0xc5, 0xdc, 0xec, 0x35, 0x33, 0xd8, 0x6b, 0x66, 0xb4, 0x5c, 0xda, 0x64, - 0x58, 0x33, 0x37, 0x90, 0x5f, 0xf5, 0x3f, 0x14, 0xc8, 0x27, 0xdb, 0xea, 0x07, 0x50, 0x8c, 0x4b, - 0xb7, 0x9e, 0x78, 0xc8, 0x91, 0x52, 0xdd, 0xfa, 0x87, 0xfa, 0x1f, 0x78, 0xc8, 0x31, 0x35, 0x59, - 0x32, 0x5b, 0x5c, 0xde, 0xf0, 0xd4, 0x15, 0x0d, 0x9f, 0x53, 0x58, 0xfa, 0xdf, 0x29, 0x6c, 0x4e, - 0x0b, 0xea, 0xab, 0x5a, 0xf8, 0x39, 0x05, 0xb9, 0x2e, 0x1f, 0x62, 0xe4, 0xfd, 0xe7, 0x63, 0x98, - 0x08, 0x69, 0x03, 0xf2, 0x61, 0xe0, 0x59, 0xc2, 0xa3, 0x72, 0x4f, 0x2e, 0x0c, 0x3c, 0x73, 0x41, - 0x65, 0x99, 0xb7, 0x3a, 0xa3, 0xcb, 0x6f, 0x81, 0xc1, 0xec, 0xab, 0x0c, 0x7e, 0x03, 0x05, 0x41, - 0x88, 0x7c, 0x9c, 0x3f, 0x66, 0x4c, 0xf0, 0x17, 0x5f, 0xbc, 0xcd, 0x9b, 0x57, 0x1d, 0x5e, 0xe0, - 0x4d, 0x89, 0x66, 0x71, 0xe2, 0xd9, 0x92, 0xbf, 0x14, 0x36, 0xff, 0x7e, 0x16, 0x4c, 0x89, 0xae, - 0xff, 0xa6, 0x40, 0x9e, 0x97, 0x7d, 0x84, 0x29, 0x9a, 0x23, 0x4f, 0x79, 0x53, 0xf2, 0xde, 0x03, - 0x10, 0xc9, 0x88, 0xfb, 0x0c, 0xcb, 0xc6, 0xe6, 0xb9, 0xa5, 0xe7, 0x3e, 0xc3, 0xfa, 0xa7, 0x49, - 0xa5, 0xe9, 0xd7, 0xa9, 0x54, 0x8e, 0x6e, 0x5c, 0xef, 0x75, 0xc8, 0xfa, 0x93, 0xb1, 0xc5, 0x9e, - 0x09, 0x55, 0x48, 0xc6, 0x9f, 0x8c, 0xfb, 0xa7, 0xe4, 0xce, 0x2f, 0x0a, 0x68, 0x33, 0xe3, 0xa3, - 0x57, 0xe0, 0x5a, 0xeb, 0xf0, 0x78, 0xf7, 0xd1, 0x9e, 0xd5, 0xde, 0xb3, 0x1e, 0x1c, 0x36, 0x0f, - 0xac, 0xcf, 0x3a, 0x8f, 0x3a, 0xc7, 0x9f, 0x77, 0x4a, 0x4b, 0x7a, 0x03, 0xd6, 0xb9, 0x2f, 0x71, - 0x35, 0x5b, 0xbd, 0xfd, 0x4e, 0xbf, 0xa4, 0x54, 0xfe, 0x7f, 0x7e, 0x51, 0x5b, 0x9b, 0x49, 0xd3, - 0x1c, 0x10, 0xec, 0xd3, 0xc5, 0x80, 0xdd, 0xe3, 0xa3, 0xa3, 0x76, 0xbf, 0x94, 0x5a, 0x08, 0x90, - 0x37, 0xe4, 0x6d, 0x58, 0x9b, 0x0f, 0xe8, 0xb4, 0x0f, 0x4b, 0xe9, 0x8a, 0x7e, 0x7e, 0x51, 0x5b, - 0x99, 0x41, 0x77, 0x5c, 0xaf, 0x92, 0xfb, 0xf6, 0xf9, 0xe6, 0xd2, 0x4f, 0x3f, 0x6c, 0x2e, 0xdd, - 0xf9, 0x51, 0x81, 0xe2, 0xdc, 0x94, 0xe8, 0x1b, 0x70, 0xbd, 0xd7, 0x3e, 0xe8, 0xec, 0xef, 0x59, - 0x47, 0xbd, 0x03, 0xab, 0xff, 0x65, 0x77, 0x7f, 0xa6, 0x8a, 0x1b, 0x50, 0xe8, 0x9a, 0xfb, 0x8f, - 0x8f, 0xfb, 0xfb, 0xdc, 0x53, 0x52, 0x2a, 0xab, 0xe7, 0x17, 0x35, 0xad, 0x1b, 0x61, 0xf6, 0x9b, - 0x83, 0xc7, 0xdf, 0x84, 0x95, 0xae, 0xb9, 0x2f, 0x0e, 0x2b, 0x40, 0xa9, 0xca, 0xda, 0xf9, 0x45, - 0xad, 0xd8, 0x8d, 0xb0, 0x10, 0x02, 0x87, 0x6d, 0x41, 0xb1, 0x6b, 0x1e, 0x77, 0x8f, 0x7b, 0xcd, - 0x43, 0x81, 0x4a, 0x57, 0x4a, 0xe7, 0x17, 0xb5, 0x42, 0x3c, 0xe2, 0x0c, 0x34, 0x3d, 0x67, 0xcb, - 0xf8, 0xea, 0x9e, 0xe3, 0xd2, 0xd1, 0x64, 0x60, 0x0c, 0x83, 0x71, 0x63, 0xda, 0xbd, 0xd9, 0xcf, - 0x99, 0x3f, 0x39, 0x06, 0xcb, 0x7c, 0xf1, 0xd1, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x10, - 0x1c, 0xa4, 0x88, 0x0c, 0x00, 0x00, + // 1308 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcb, 0x6e, 0xdb, 0x46, + 0x17, 0x36, 0x25, 0xea, 0x76, 0x28, 0xd9, 0x32, 0xe3, 0x3f, 0xd1, 0x2f, 0xb7, 0x96, 0x22, 0x37, + 0xa9, 0x73, 0x01, 0xd5, 0xba, 0x40, 0xd1, 0x00, 0xdd, 0x48, 0xb6, 0xe3, 0x08, 0xb1, 0x25, 0x81, + 0x52, 0xd3, 0xcb, 0x86, 0x18, 0x89, 0x13, 0x8a, 0x08, 0x45, 0x12, 0xe4, 0xc8, 0xb0, 0x52, 0xa0, + 0xeb, 0xc2, 0xab, 0xbe, 0x80, 0x37, 0xbd, 0x00, 0x7d, 0x8b, 0x76, 0xd9, 0x55, 0x97, 0x5d, 0xa6, + 0x40, 0xfa, 0x0a, 0x7d, 0x80, 0x62, 0x2e, 0xa4, 0xa4, 0xc8, 0x6e, 0x8d, 0x26, 0xdd, 0x24, 0x9c, + 0x73, 0xbe, 0x73, 0xe6, 0x9c, 0x6f, 0xbe, 0x33, 0x23, 0xc3, 0x0d, 0x3f, 0xf0, 0x88, 0x57, 0x27, + 0x53, 0x1f, 0x87, 0xfc, 0x5f, 0x8d, 0x59, 0xd4, 0xeb, 0x04, 0xbb, 0x26, 0x0e, 0xc6, 0xb6, 0x4b, + 0xb8, 0x45, 0x63, 0xde, 0xf2, 0x6d, 0x32, 0xb2, 0x03, 0xd3, 0xf0, 0x51, 0x40, 0xa6, 0x75, 0x1e, + 0x6c, 0x79, 0x96, 0x37, 0xfb, 0xe2, 0xe8, 0x72, 0xc5, 0xf2, 0x3c, 0xcb, 0xc1, 0x1c, 0x32, 0x98, + 0x3c, 0xad, 0x13, 0x7b, 0x8c, 0x43, 0x82, 0xc6, 0xbe, 0x00, 0x6c, 0xf2, 0x10, 0xc7, 0x1e, 0x84, + 0xf5, 0x81, 0x4d, 0x16, 0x76, 0x2f, 0x57, 0xb8, 0x73, 0x18, 0x4c, 0x7d, 0xe2, 0xd5, 0xc7, 0x38, + 0x78, 0xe6, 0xe0, 0x05, 0x80, 0x88, 0x3e, 0xc1, 0x41, 0x68, 0x7b, 0x6e, 0xf4, 0x3f, 0x77, 0xd6, + 0x1e, 0x40, 0xa1, 0x8b, 0x02, 0xd2, 0xc3, 0xe4, 0x11, 0x46, 0x26, 0x0e, 0xd4, 0x0d, 0x48, 0x11, + 0x8f, 0x20, 0xa7, 0x24, 0x55, 0xa5, 0x9d, 0xa4, 0xce, 0x17, 0xaa, 0x0a, 0xf2, 0x08, 0x85, 0xa3, + 0x52, 0xa2, 0x2a, 0xed, 0xe4, 0x75, 0xf6, 0x5d, 0xfb, 0x12, 0x64, 0x1a, 0x4a, 0x23, 0x6c, 0xd7, + 0xc4, 0xa7, 0x2c, 0xa2, 0xa0, 0xf3, 0x05, 0xb5, 0x0e, 0xa6, 0x04, 0x87, 0x22, 0x84, 0x2f, 0xd4, + 0x43, 0x48, 0xf9, 0x81, 0xe7, 0x3d, 0x2d, 0x25, 0xab, 0xd2, 0x8e, 0xb2, 0x7b, 0x4f, 0x5b, 0xa2, + 0x8e, 0xf7, 0xa1, 0xf1, 0x3e, 0xb4, 0x9e, 0x3d, 0xf6, 0x1d, 0xdc, 0xa5, 0x21, 0x4d, 0xf9, 0x97, + 0x17, 0x95, 0x15, 0x9d, 0xc7, 0xd7, 0xc6, 0x90, 0x69, 0x3a, 0xde, 0xf0, 0x59, 0x6b, 0x3f, 0xae, + 0x4d, 0x9a, 0xd5, 0xa6, 0xb6, 0x21, 0x4f, 0x69, 0x0f, 0x8d, 0x11, 0xeb, 0x8a, 0x15, 0xa1, 0xec, + 0xde, 0xd2, 0x2e, 0x3e, 0x29, 0x6d, 0x81, 0x02, 0xb1, 0x91, 0xc2, 0x12, 0x70, 0x53, 0xed, 0xdb, + 0x14, 0xa4, 0x05, 0x41, 0x7b, 0x90, 0x11, 0x14, 0xb2, 0x1d, 0x95, 0xdd, 0xed, 0xe5, 0xac, 0x11, + 0xc7, 0x7b, 0x9e, 0x1b, 0x62, 0x37, 0x9c, 0x84, 0x22, 0x67, 0x14, 0xa9, 0xde, 0x86, 0xec, 0x70, + 0x84, 0x6c, 0xd7, 0xb0, 0x4d, 0x56, 0x5b, 0xae, 0xa9, 0xbc, 0x7c, 0x51, 0xc9, 0xec, 0x51, 0x5b, + 0x6b, 0x5f, 0xcf, 0x30, 0x67, 0xcb, 0x54, 0xaf, 0x43, 0x7a, 0x84, 0x6d, 0x6b, 0x44, 0x18, 0x61, + 0x49, 0x5d, 0xac, 0xd4, 0x8f, 0x40, 0xa6, 0x22, 0x29, 0xc9, 0xac, 0x82, 0xb2, 0xc6, 0x15, 0xa4, + 0x45, 0x0a, 0xd2, 0xfa, 0x91, 0x82, 0x9a, 0x59, 0xba, 0xf1, 0x37, 0xbf, 0x57, 0x24, 0x9d, 0x45, + 0xa8, 0x9f, 0x41, 0xc1, 0x41, 0x21, 0x31, 0x06, 0x94, 0x3d, 0xba, 0x7d, 0x8a, 0xa5, 0xa8, 0x5c, + 0x46, 0x8d, 0x60, 0xb9, 0x79, 0x8d, 0xe6, 0x79, 0xf9, 0xa2, 0xa2, 0x1c, 0xa1, 0x90, 0x08, 0xa3, + 0xae, 0x38, 0xf1, 0xc2, 0x54, 0x77, 0xa0, 0xc8, 0x32, 0x0f, 0xbd, 0xf1, 0xd8, 0x26, 0x06, 0x3b, + 0x93, 0x34, 0x3b, 0x93, 0x55, 0x6a, 0xdf, 0x63, 0xe6, 0x47, 0xf4, 0x74, 0x36, 0x21, 0x67, 0x22, + 0x82, 0x38, 0x24, 0xc3, 0x20, 0x59, 0x6a, 0x60, 0xce, 0x0a, 0x28, 0x27, 0x1e, 0xc1, 0x41, 0xc8, + 0xdd, 0x59, 0xe6, 0x06, 0x6e, 0x62, 0x80, 0x77, 0x61, 0xed, 0x04, 0x39, 0xb6, 0x89, 0x88, 0x17, + 0x81, 0x72, 0x7c, 0x9b, 0x99, 0x99, 0x01, 0xdf, 0x83, 0x0d, 0x17, 0x9f, 0x12, 0xe3, 0x55, 0x34, + 0x30, 0xb4, 0x4a, 0x7d, 0x4f, 0x16, 0x23, 0x6e, 0xc1, 0xea, 0x30, 0x3a, 0x32, 0x8e, 0x55, 0x18, + 0xb6, 0x10, 0x5b, 0x19, 0xec, 0xff, 0x90, 0x45, 0xbe, 0xcf, 0x01, 0x79, 0x06, 0xc8, 0x20, 0xdf, + 0x67, 0xae, 0xbb, 0xb0, 0xce, 0x48, 0x08, 0x70, 0x38, 0x71, 0x88, 0x48, 0x52, 0x60, 0x98, 0x35, + 0xea, 0xd0, 0xb9, 0x9d, 0x61, 0xb7, 0xa1, 0x80, 0x4f, 0x6c, 0x13, 0xbb, 0x43, 0xcc, 0x71, 0xab, + 0x0c, 0x97, 0x8f, 0x8c, 0x0c, 0x74, 0x07, 0x8a, 0x7e, 0xe0, 0xf9, 0x5e, 0x88, 0x03, 0x03, 0x99, + 0x66, 0x80, 0xc3, 0xb0, 0xb4, 0xc6, 0xf3, 0x45, 0xf6, 0x06, 0x37, 0xd7, 0xee, 0x83, 0xbc, 0x8f, + 0x08, 0x52, 0x8b, 0x90, 0x24, 0xa7, 0x61, 0x49, 0xaa, 0x26, 0x77, 0xf2, 0x3a, 0xfd, 0xbc, 0x70, + 0x7c, 0xff, 0x4c, 0x80, 0xfc, 0xc4, 0x23, 0x58, 0x7d, 0x00, 0x32, 0x3d, 0x6a, 0xa6, 0xe6, 0xd5, + 0xcb, 0x67, 0xa4, 0x67, 0x5b, 0x2e, 0x36, 0x8f, 0x43, 0xab, 0x3f, 0xf5, 0xb1, 0xce, 0x42, 0xe6, + 0xe4, 0x99, 0x58, 0x90, 0xe7, 0x06, 0xa4, 0x02, 0x6f, 0xe2, 0x9a, 0x42, 0xb5, 0x7c, 0xa1, 0x3e, + 0x86, 0x6c, 0xac, 0x3a, 0xf9, 0x6a, 0xaa, 0x5b, 0x13, 0xaa, 0x8b, 0x86, 0x5d, 0xcf, 0x0c, 0x84, + 0xda, 0x9a, 0x90, 0x8b, 0xaf, 0x49, 0xa1, 0xe1, 0xab, 0x8d, 0xc1, 0x2c, 0x4c, 0xbd, 0x07, 0xeb, + 0xb1, 0x36, 0x62, 0x72, 0xb9, 0x64, 0x8b, 0xb1, 0x43, 0xb0, 0xbb, 0x20, 0x3b, 0x83, 0x5f, 0x78, + 0x19, 0xd6, 0xdd, 0x4c, 0x76, 0x2d, 0x76, 0xf3, 0xbd, 0x05, 0xb9, 0xd0, 0xb6, 0x5c, 0x44, 0x26, + 0x01, 0x16, 0xf2, 0x9d, 0x19, 0x6a, 0xbf, 0x25, 0x20, 0xcd, 0x47, 0x61, 0x8e, 0x3d, 0xe9, 0x62, + 0xf6, 0x28, 0xa9, 0xa9, 0x8b, 0xd8, 0x4b, 0xbe, 0x2e, 0x7b, 0x87, 0x00, 0x71, 0x49, 0x61, 0x49, + 0xae, 0x26, 0x77, 0x94, 0xdd, 0x9b, 0x97, 0xa5, 0xe3, 0xe5, 0xf6, 0x6c, 0x4b, 0xdc, 0x62, 0x73, + 0xa1, 0xea, 0xfb, 0xb0, 0x81, 0x2c, 0x2b, 0xc0, 0x16, 0x22, 0xd8, 0x34, 0x66, 0x7d, 0xa7, 0x58, + 0xdf, 0xd7, 0x66, 0xbe, 0x5e, 0xe4, 0x8a, 0xc5, 0x98, 0x9e, 0xbb, 0xaf, 0x1b, 0x90, 0x1b, 0xd8, + 0xc4, 0x40, 0x41, 0x80, 0xa6, 0x8c, 0x56, 0x65, 0xf7, 0x9d, 0xe5, 0x72, 0xe8, 0x03, 0xa8, 0xd1, + 0x07, 0x50, 0x6b, 0xda, 0xa4, 0x41, 0xb1, 0x7a, 0x76, 0x20, 0xbe, 0x6a, 0x7f, 0x48, 0x90, 0x8b, + 0x2b, 0x55, 0x0f, 0xa1, 0x10, 0xb1, 0x65, 0x3c, 0x75, 0x90, 0x25, 0xd4, 0xbd, 0xfd, 0x0f, 0x94, + 0x3d, 0x74, 0x90, 0xa5, 0x2b, 0x82, 0x25, 0xba, 0xb8, 0x58, 0x23, 0x89, 0x4b, 0x34, 0xb2, 0x20, + 0xca, 0xe4, 0xbf, 0x13, 0xe5, 0x82, 0x7c, 0xe4, 0x57, 0xe5, 0xf3, 0x53, 0x02, 0xb2, 0x5d, 0x36, + 0xf7, 0xc8, 0xf9, 0xcf, 0x27, 0x37, 0xd6, 0xde, 0x26, 0xe4, 0x7c, 0xcf, 0x31, 0xb8, 0x47, 0x66, + 0x9e, 0xac, 0xef, 0x39, 0xfa, 0x92, 0x30, 0x53, 0x6f, 0x74, 0xac, 0xd3, 0x6f, 0x80, 0xc1, 0xcc, + 0xab, 0x0c, 0x7e, 0x05, 0x79, 0x4e, 0x88, 0x78, 0xcf, 0x3f, 0xa4, 0x4c, 0xb0, 0x1f, 0x09, 0xfc, + 0x39, 0xdf, 0xba, 0xac, 0x78, 0x8e, 0xd7, 0x05, 0x9a, 0xc6, 0xf1, 0x97, 0x4e, 0xfc, 0xb8, 0xd8, + 0xfa, 0xfb, 0xf1, 0xd1, 0x05, 0xba, 0xf6, 0xab, 0x04, 0x39, 0xd6, 0xf6, 0x31, 0x26, 0x68, 0x81, + 0x3c, 0xe9, 0x75, 0xc9, 0x7b, 0x1b, 0x80, 0x27, 0x0b, 0xed, 0xe7, 0x58, 0x1c, 0x6c, 0x8e, 0x59, + 0x7a, 0xf6, 0x73, 0xac, 0x7e, 0x1c, 0x77, 0x9a, 0xbc, 0x4a, 0xa7, 0x62, 0xda, 0xa3, 0x7e, 0x6f, + 0x40, 0xc6, 0x9d, 0x8c, 0x0d, 0xfa, 0xb2, 0xc8, 0x5c, 0x32, 0xee, 0x64, 0xdc, 0x3f, 0x0d, 0xef, + 0xfe, 0x2c, 0x81, 0x32, 0x37, 0x3e, 0x6a, 0x19, 0xae, 0x37, 0x8f, 0x3a, 0x7b, 0x8f, 0xf7, 0x8d, + 0xd6, 0xbe, 0xf1, 0xf0, 0xa8, 0x71, 0x68, 0x7c, 0xd2, 0x7e, 0xdc, 0xee, 0x7c, 0xda, 0x2e, 0xae, + 0xa8, 0x75, 0xd8, 0x60, 0xbe, 0xd8, 0xd5, 0x68, 0xf6, 0x0e, 0xda, 0xfd, 0xa2, 0x54, 0xfe, 0xdf, + 0xd9, 0x79, 0x75, 0x7d, 0x2e, 0x4d, 0x63, 0x10, 0x62, 0x97, 0x2c, 0x07, 0xec, 0x75, 0x8e, 0x8f, + 0x5b, 0xfd, 0x62, 0x62, 0x29, 0x40, 0x5c, 0xaa, 0x77, 0x60, 0x7d, 0x31, 0xa0, 0xdd, 0x3a, 0x2a, + 0x26, 0xcb, 0xea, 0xd9, 0x79, 0x75, 0x75, 0x0e, 0xdd, 0xb6, 0x9d, 0x72, 0xf6, 0xeb, 0xef, 0xb6, + 0x56, 0x7e, 0xfc, 0x7e, 0x6b, 0xe5, 0xee, 0x0f, 0x12, 0x14, 0x16, 0xa6, 0x44, 0xdd, 0x84, 0x1b, + 0xbd, 0xd6, 0x61, 0xfb, 0x60, 0xdf, 0x38, 0xee, 0x1d, 0x1a, 0xfd, 0xcf, 0xbb, 0x07, 0x73, 0x5d, + 0xdc, 0x84, 0x7c, 0x57, 0x3f, 0x78, 0xd2, 0xe9, 0x1f, 0x30, 0x4f, 0x51, 0x2a, 0xaf, 0x9d, 0x9d, + 0x57, 0x95, 0x6e, 0x80, 0xe9, 0xcf, 0x14, 0x16, 0x7f, 0x0b, 0x56, 0xbb, 0xfa, 0x01, 0x2f, 0x96, + 0x83, 0x12, 0xe5, 0xf5, 0xb3, 0xf3, 0x6a, 0xa1, 0x1b, 0x60, 0x2e, 0x04, 0x06, 0xdb, 0x86, 0x42, + 0x57, 0xef, 0x74, 0x3b, 0xbd, 0xc6, 0x11, 0x47, 0x25, 0xcb, 0xc5, 0xb3, 0xf3, 0x6a, 0x3e, 0x1a, + 0x71, 0x0a, 0x9a, 0xd5, 0xd9, 0xd4, 0xbe, 0xb8, 0x6f, 0xd9, 0x64, 0x34, 0x19, 0x68, 0x43, 0x6f, + 0x5c, 0x9f, 0x9d, 0xde, 0xfc, 0xe7, 0xdc, 0x5f, 0x29, 0x83, 0x34, 0x5b, 0x7c, 0xf0, 0x57, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x16, 0x51, 0x97, 0x28, 0xbb, 0x0c, 0x00, 0x00, } diff --git a/proto/types/types.proto b/proto/types/types.proto index e4578e5c6..6fe888c5c 100644 --- a/proto/types/types.proto +++ b/proto/types/types.proto @@ -108,8 +108,9 @@ message Commit { int32 round = 2; BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; - bytes hash = 5; - tendermint.proto.libs.bits.BitArray bit_array = 6; + bytes aggregated_signature = 5; + bytes hash = 6; + tendermint.proto.libs.bits.BitArray bit_array = 7; } // CommitSig is a part of the Vote included in a Commit. diff --git a/types/block.go b/types/block.go index 96274dd10..67ae93c80 100644 --- a/types/block.go +++ b/types/block.go @@ -7,6 +7,9 @@ import ( "sync" "time" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/pkg/errors" "github.com/tendermint/tendermint/crypto" @@ -636,10 +639,8 @@ func (cs CommitSig) ValidateBasic() error { ) } // NOTE: Timestamp validation is subtle and handled elsewhere. - if len(cs.Signature) == 0 { - return errors.New("signature is missing") - } - if len(cs.Signature) > MaxSignatureSize { + // NOTE: Signature may be nil if it is aggregated and is handled elsewhere. + if cs.Signature != nil && len(cs.Signature) > MaxSignatureSize { return fmt.Errorf("signature is too big %d (max: %d)", len(cs.Signature), MaxSignatureSize) } } @@ -698,11 +699,38 @@ type Commit struct { // NewCommit returns a new Commit. func NewCommit(height int64, round int, blockID BlockID, commitSigs []CommitSig) *Commit { + return NewCommitWithAggregatedSignature(height, round, blockID, commitSigs, nil) +} + +// NewCommitWithAggregatedSignature returns a new Commit with . +// The aggrSig can be set to null. Then all commitSigs must have a valid signature. +func NewCommitWithAggregatedSignature(height int64, round int, blockID BlockID, commitSigs []CommitSig, aggrSig []byte) *Commit { + + // Make a copy of CommitSig to avoid side effects + newCommitSigs := make([]CommitSig, len(commitSigs)) + copy(newCommitSigs, commitSigs) + commitSigs = newCommitSigs + + // Create aggregate signature and reset per-CommitSig signature if possible + for i := range commitSigs { + if commitSigs[i].Absent() { + continue + } + curAggrSig, err := bls.AddSignature(aggrSig, commitSigs[i].Signature) + if err == nil { + aggrSig = curAggrSig + commitSigs[i].Signature = nil + } else { + // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. + } + } + return &Commit{ - Height: height, - Round: round, - BlockID: blockID, - Signatures: commitSigs, + Height: height, + Round: round, + BlockID: blockID, + Signatures: commitSigs, + AggregatedSignature: aggrSig, } } @@ -819,10 +847,26 @@ func (commit *Commit) ValidateBasic() error { if len(commit.Signatures) == 0 { return errors.New("no signatures in commit") } + omittedSignatures := 0 for i, commitSig := range commit.Signatures { if err := commitSig.ValidateBasic(); err != nil { return fmt.Errorf("wrong CommitSig #%d: %v", i, err) } + if !commitSig.Absent() && commitSig.Signature == nil { + omittedSignatures += 1 + } + } + if commit.AggregatedSignature == nil { + if omittedSignatures > 0 { + return fmt.Errorf("%d erased signatures are present, but no aggregate signature exist in commit", + omittedSignatures) + } + } else if omittedSignatures == 0 { + return fmt.Errorf("erased signatures are not present, but aggregated signature exist in commit: %x", + commit.AggregatedSignature) + } else if len(commit.AggregatedSignature) > MaxSignatureSize { + return fmt.Errorf("signature is too big %d (max: %d)", + len(commit.AggregatedSignature), MaxSignatureSize) } } @@ -884,6 +928,7 @@ func (commit *Commit) ToProto() *tmproto.Commit { c.Height = commit.Height c.Round = int32(commit.Round) c.BlockID = commit.BlockID.ToProto() + c.AggregatedSignature = commit.AggregatedSignature if commit.hash != nil { c.Hash = commit.hash } @@ -921,6 +966,7 @@ func CommitFromProto(cp *tmproto.Commit) (*Commit, error) { commit.Height = cp.Height commit.Round = int(cp.Round) commit.BlockID = *bi + commit.AggregatedSignature = cp.AggregatedSignature commit.hash = cp.Hash commit.bitArray = bitArray @@ -1196,3 +1242,19 @@ func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) { return blockID, blockID.ValidateBasic() } + +// GetBLSPubKey is a utility function for referencing a specified public key as a BLS key for signature. +// If the key is not BLS, return nil +func GetSignatureKey(pubKey crypto.PubKey) *bls.PubKeyBLS12 { + for { + if compPubKey, ok := pubKey.(composite.PubKeyComposite); ok { + pubKey = compPubKey.SignKey + } else { + break + } + } + if blsPubKey, ok := pubKey.(bls.PubKeyBLS12); ok { + return &blsPubKey + } + return nil +} diff --git a/types/block_test.go b/types/block_test.go index 58d409bf7..34c0d10ae 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -5,12 +5,17 @@ import ( // number generator here and we can run the tests a bit faster "crypto/rand" "encoding/hex" + "fmt" "math" "os" "reflect" "testing" "time" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -211,6 +216,66 @@ func TestNilDataHashDoesntCrash(t *testing.T) { assert.Equal(t, []byte(new(Data).Hash()), nilBytes) } +func TestNewCommit(t *testing.T) { + blockID := BlockID{ + Hash: []byte{}, + PartsHeader: PartSetHeader{ + Total: 0, + Hash: []byte{}, + }, + } + privKeys := [...]crypto.PrivKey{ + bls.GenPrivKey(), + composite.GenPrivKey(), + ed25519.GenPrivKey(), + bls.GenPrivKey(), + } + msgs := make([][]byte, len(privKeys)) + signs := make([][]byte, len(privKeys)) + pubKeys := make([]crypto.PubKey, len(privKeys)) + commitSigs := make([]CommitSig, len(privKeys)) + for i := 0; i < len(privKeys); i++ { + msgs[i] = []byte(fmt.Sprintf("hello, world %d", i)) + signs[i], _ = privKeys[i].Sign(msgs[i]) + pubKeys[i] = privKeys[i].PubKey() + commitSigs[i] = NewCommitSigForBlock(signs[i], pubKeys[i].Address(), time.Now()) + assert.Equal(t, signs[i], commitSigs[i].Signature) + } + commit := NewCommit(0, 1, blockID, commitSigs) + + assert.Equal(t, int64(0), commit.Height) + assert.Equal(t, 1, commit.Round) + assert.Equal(t, blockID, commit.BlockID) + assert.Equal(t, len(commitSigs), len(commit.Signatures)) + assert.NotNil(t, commit.AggregatedSignature) + assert.Nil(t, commit.Signatures[0].Signature) + assert.Nil(t, commit.Signatures[1].Signature) + assert.NotNil(t, commit.Signatures[2].Signature) + assert.Nil(t, commit.Signatures[3].Signature) + assert.True(t, pubKeys[2].VerifyBytes(msgs[2], commit.Signatures[2].Signature)) + + blsPubKeys := []bls.PubKeyBLS12{ + *GetSignatureKey(pubKeys[0]), + *GetSignatureKey(pubKeys[1]), + *GetSignatureKey(pubKeys[3]), + } + blsSigMsgs := [][]byte{msgs[0], msgs[1], msgs[3]} + func() { + aggrSig, err := bls.AddSignature(nil, signs[0]) + assert.Nil(t, err) + aggrSig, err = bls.AddSignature(aggrSig, signs[1]) + assert.Nil(t, err) + aggrSig, err = bls.AddSignature(aggrSig, signs[3]) + assert.Nil(t, err) + err = bls.VerifyAggregatedSignature(aggrSig, blsPubKeys, blsSigMsgs) + assert.Nil(t, err) + assert.Equal(t, aggrSig, commit.AggregatedSignature) + }() + + err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, blsSigMsgs) + assert.Nilf(t, err, "%s", err) +} + func TestCommit(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) diff --git a/types/vote.go b/types/vote.go index 0cd9b4151..0568396db 100644 --- a/types/vote.go +++ b/types/vote.go @@ -127,7 +127,7 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { return ErrVoteInvalidValidatorAddress } - if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { + if vote.Signature != nil && !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { return ErrVoteInvalidSignature } return nil diff --git a/types/vote_set.go b/types/vote_set.go index 5bcb885f5..2e08976a7 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -6,6 +6,8 @@ import ( "strings" "sync" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/pkg/errors" "github.com/tendermint/tendermint/libs/bits" @@ -64,6 +66,8 @@ type VoteSet struct { maj23 *BlockID // First 2/3 majority seen votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes peerMaj23s map[P2PID]BlockID // Maj23 for each peer + + aggregatedSignature []byte } // Constructs a new VoteSet struct used to accumulate votes for given height/round. @@ -83,6 +87,8 @@ func NewVoteSet(chainID string, height int64, round int, signedMsgType SignedMsg maj23: nil, votesByBlock: make(map[string]*blockVotes, voterSet.Size()), peerMaj23s: make(map[P2PID]BlockID), + + aggregatedSignature: nil, } } @@ -212,6 +218,16 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { if !added { panic("Expected to add non-conflicting vote") } + + // Aggregate signature in added vote if possible. + aggrSign, err := bls.AddSignature(voteSet.aggregatedSignature, vote.Signature) + if err == nil { + voteSet.aggregatedSignature = aggrSign + vote.Signature = nil + } else { + // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. + } + return added, nil } @@ -577,7 +593,8 @@ func (voteSet *VoteSet) MakeCommit() *Commit { commitSigs[i] = commitSig } - return NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs) + return NewCommitWithAggregatedSignature( + voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs, voteSet.aggregatedSignature) } //-------------------------------------------------------------------------------- diff --git a/types/voter_set.go b/types/voter_set.go index f0fe5e710..4834ec36a 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -8,6 +8,8 @@ import ( "sort" "strings" + "github.com/tendermint/tendermint/crypto/bls" + tmproto "github.com/tendermint/tendermint/proto/types" "github.com/datastream/probab/dst" @@ -147,6 +149,8 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, talliedVotingPower := int64(0) votingPowerNeeded := voters.TotalVotingPower() * 2 / 3 + blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) + messages := make([][]byte, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. @@ -158,8 +162,17 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // Validate signature. voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + if commitSig.Signature != nil { + if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + } else { + blsPubKey := GetSignatureKey(val.PubKey) + if blsPubKey == nil { + return fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx) + } + blsPubKeys = append(blsPubKeys, *blsPubKey) + messages = append(messages, voteSignBytes) } // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { @@ -171,6 +184,10 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // } } + if err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, messages); err != nil { + return fmt.Errorf("wrong aggregated signature: %X; %s", commit.AggregatedSignature, err) + } + if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} } From 8ff0ba2d050bb0a2edd41caa22e85a08456b3bec Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Fri, 4 Sep 2020 16:43:32 +0900 Subject: [PATCH 02/36] apply BLS signature validation for various commit verifications --- types/block.go | 32 ++++++++++++++++ types/validator_set_test.go | 14 ++++++- types/vote_test.go | 1 + types/voter_set.go | 75 +++++++++++++++---------------------- 4 files changed, 76 insertions(+), 46 deletions(-) diff --git a/types/block.go b/types/block.go index 67ae93c80..a7b64d759 100644 --- a/types/block.go +++ b/types/block.go @@ -936,6 +936,38 @@ func (commit *Commit) ToProto() *tmproto.Commit { return c } +func (commit *Commit) VerifySignatures(chainID string, vals []*Validator) error { + blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) + messages := make([][]byte, 0, len(commit.Signatures)) + for idx, commitSig := range commit.Signatures { + if commitSig.Absent() { + continue // OK, some signatures can be absent. + } + + // Validate signature. + if val := vals[idx]; val != nil { + voteSignBytes := commit.VoteSignBytes(chainID, idx) + if commitSig.Signature != nil { + if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + } else { + blsPubKey := GetSignatureKey(val.PubKey) + if blsPubKey == nil { + return fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx) + } + blsPubKeys = append(blsPubKeys, *blsPubKey) + messages = append(messages, voteSignBytes) + } + } + } + + if err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, messages); err != nil { + return fmt.Errorf("wrong aggregated signature: %X; %s", commit.AggregatedSignature, err) + } + return nil +} + // FromProto sets a protobuf Commit to the given pointer. // It returns an error if the commit is invalid. func CommitFromProto(cp *tmproto.Commit) (*Commit, error) { diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 3190d065f..2dc82cae7 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -717,8 +717,13 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign require.NoError(t, err) commit.Signatures[3] = vote.CommitSig() + // NOTE: Before BLS signature aggregation, in case that three of the four signatures are valid, the Commit itself + // is considered valid even if one of the remaining signatures has been tampered with or replaced, but after + // BLS signature aggregation, the verification fails unless all four signatures are valid, since all four + // signatures are verified at once. err = vset.VerifyCommitLight(chainID, blockID, h, commit) - assert.NoError(t, err) + assert.Error(t, err) + assert.Contains(t, err.Error(), "wrong signature (#3)") } func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) { @@ -738,8 +743,13 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin require.NoError(t, err) commit.Signatures[2] = vote.CommitSig() + // NOTE: Before BLS signature aggregation, in case that three of the four signatures are valid, the Commit itself + // is considered valid even if one of the remaining signatures has been tampered with or replaced, but after + // BLS signature aggregation, the verification fails unless all four signatures are valid, since all four + // signatures are verified at once. err = vset.VerifyCommitLightTrusting(chainID, blockID, h, commit, tmmath.Fraction{Numerator: 1, Denominator: 3}) - assert.NoError(t, err) + assert.Error(t, err) + assert.Contains(t, err.Error(), "wrong signature (#2)") } func TestEmptySet(t *testing.T) { diff --git a/types/vote_test.go b/types/vote_test.go index c3bd7a16f..ee1645ef3 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -199,6 +199,7 @@ func TestVoteVerify(t *testing.T) { vote := examplePrevote() vote.ValidatorAddress = pubkey.Address() + vote.Signature = []byte{} err = vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey()) if assert.Error(t, err) { diff --git a/types/voter_set.go b/types/voter_set.go index 4834ec36a..fa51d6505 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -8,8 +8,6 @@ import ( "sort" "strings" - "github.com/tendermint/tendermint/crypto/bls" - tmproto "github.com/tendermint/tendermint/proto/types" "github.com/datastream/probab/dst" @@ -149,8 +147,6 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, talliedVotingPower := int64(0) votingPowerNeeded := voters.TotalVotingPower() * 2 / 3 - blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) - messages := make([][]byte, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. @@ -160,20 +156,6 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // This means we don't need the validator address or to do any lookup. val := voters.Voters[idx] - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if commitSig.Signature != nil { - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - } else { - blsPubKey := GetSignatureKey(val.PubKey) - if blsPubKey == nil { - return fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx) - } - blsPubKeys = append(blsPubKeys, *blsPubKey) - messages = append(messages, voteSignBytes) - } // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { talliedVotingPower += val.VotingPower @@ -184,8 +166,9 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // } } - if err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, messages); err != nil { - return fmt.Errorf("wrong aggregated signature: %X; %s", commit.AggregatedSignature, err) + // Validate signature. + if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { + return err } if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { @@ -231,16 +214,14 @@ func (voters *VoterSet) VerifyCommitLight(chainID string, blockID BlockID, // This means we don't need the validator address or to do any lookup. val := voters.Voters[idx] - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - talliedVotingPower += val.VotingPower // return as soon as +2/3 of the signatures are verified if talliedVotingPower > votingPowerNeeded { + // Validate signature. + if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { + return err + } return nil } } @@ -291,23 +272,20 @@ func (voters *VoterSet) VerifyFutureCommit(newSet *VoterSet, chainID string, oldVotingPower := int64(0) seen := map[int]bool{} - for idx, commitSig := range commit.Signatures { + vals := make([]*Validator, 0, len(commit.Signatures)) + for _, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. } // See if this validator is in oldVals. oldIdx, val := oldVoters.GetByAddress(commitSig.ValidatorAddress) + vals = append(vals, val) if val == nil || seen[oldIdx] { continue // missing or double vote... } seen[oldIdx] = true - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return errors.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { oldVotingPower += val.VotingPower @@ -318,6 +296,11 @@ func (voters *VoterSet) VerifyFutureCommit(newSet *VoterSet, chainID string, // } } + // Validate signature. + if err := commit.VerifySignatures(chainID, vals); err != nil { + return err + } + if got, needed := oldVotingPower, oldVoters.TotalVotingPower()*2/3; got <= needed { return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} } @@ -351,6 +334,7 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, } votingPowerNeeded := totalVotingPowerMulByNumerator / trustLevel.Denominator + vals := make([]*Validator, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. @@ -359,6 +343,7 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, // We don't know the validators that committed this block, so we have to // check for each vote if its validator is already known. valIdx, val := voters.GetByAddress(commitSig.ValidatorAddress) + vals = append(vals, val) if firstIndex, ok := seenVals[valIdx]; ok { // double vote secondIndex := idx @@ -368,12 +353,6 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, if val != nil { seenVals[valIdx] = idx - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return errors.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { talliedVotingPower += val.VotingPower @@ -389,6 +368,11 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, } } + // Validate signature. + if err := commit.VerifySignatures(chainID, vals); err != nil { + return err + } + return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} } @@ -445,15 +429,18 @@ func (voters *VoterSet) VerifyCommitLightTrusting(chainID string, blockID BlockI } seenVals[valIdx] = idx - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return errors.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - talliedVotingPower += val.VotingPower if talliedVotingPower > votingPowerNeeded { + // Validate signature. + vals := make([]*Validator, 0, len(commit.Signatures)) + for _, cs := range commit.Signatures { + _, val := voters.GetByAddress(cs.ValidatorAddress) + vals = append(vals, val) + } + if err := commit.VerifySignatures(chainID, vals); err != nil { + return err + } return nil } } From 3b634572f53f9495519367ffe464babd090ec91d Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Fri, 4 Sep 2020 17:01:52 +0900 Subject: [PATCH 03/36] apply github-action-lint --- crypto/bls/bls.go | 5 ++--- crypto/bls/bls_test.go | 18 +++++++++++++++--- types/block.go | 5 +++-- types/vote_set.go | 5 +++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/crypto/bls/bls.go b/crypto/bls/bls.go index acb99bcd0..c3a788d0f 100644 --- a/crypto/bls/bls.go +++ b/crypto/bls/bls.go @@ -86,10 +86,9 @@ func VerifyAggregatedSignature(aggregatedSignature []byte, pubKeys []PubKeyBLS12 if aggregatedSignature == nil { if len(pubKeys) == 0 { return nil - } else { - return fmt.Errorf( - "the aggregate signature was omitted, even though %d public keys were specified", len(pubKeys)) } + return fmt.Errorf( + "the aggregate signature was omitted, even though %d public keys were specified", len(pubKeys)) } aggrSign := bls.Sign{} err := aggrSign.Deserialize(aggregatedSignature) diff --git a/crypto/bls/bls_test.go b/crypto/bls/bls_test.go index 2070f32e9..4058d2521 100644 --- a/crypto/bls/bls_test.go +++ b/crypto/bls/bls_test.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "fmt" "math/rand" + "reflect" "testing" "time" @@ -343,10 +344,17 @@ func generatePubKeysAndSigns(t *testing.T, size int) ([]bls.PubKeyBLS12, [][]byt func blsPublicKey(t *testing.T, pubKey crypto.PubKey) bls.PubKeyBLS12 { blsPubKey, ok := pubKey.(bls.PubKeyBLS12) if !ok { - t.Fatal(fmt.Sprintf("specified public key is not for BLS")) + var keyType string + if t := reflect.TypeOf(pubKey); t.Kind() == reflect.Ptr { + keyType = "*" + t.Elem().Name() + } else { + keyType = t.Name() + } + t.Fatal(fmt.Sprintf("specified public key is not for BLS: %s", keyType)) } return blsPubKey } + func aggregateSignatures(init []byte, signatures [][]byte) (aggrSig []byte, err error) { aggrSig = init for _, sign := range signatures { @@ -398,7 +406,9 @@ func TestAggregatedSignature(t *testing.T) { shuffledPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) copy(shuffledPubKeys, pubKeys) rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(shuffledPubKeys), func(i, j int) { shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] }) + rand.Shuffle(len(shuffledPubKeys), func(i, j int) { + shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] + }) if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, msgs); err == nil { t.Error("successfully validated with public keys of different order") } @@ -409,7 +419,9 @@ func TestAggregatedSignature(t *testing.T) { shuffledMsgs := make([][]byte, len(msgs)) copy(shuffledMsgs, msgs) rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(shuffledMsgs), func(i, j int) { shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] }) + rand.Shuffle(len(shuffledMsgs), func(i, j int) { + shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] + }) if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, shuffledMsgs); err == nil { t.Error("successfully validated with messages of different order") } diff --git a/types/block.go b/types/block.go index a7b64d759..3c3e95656 100644 --- a/types/block.go +++ b/types/block.go @@ -704,7 +704,8 @@ func NewCommit(height int64, round int, blockID BlockID, commitSigs []CommitSig) // NewCommitWithAggregatedSignature returns a new Commit with . // The aggrSig can be set to null. Then all commitSigs must have a valid signature. -func NewCommitWithAggregatedSignature(height int64, round int, blockID BlockID, commitSigs []CommitSig, aggrSig []byte) *Commit { +func NewCommitWithAggregatedSignature( + height int64, round int, blockID BlockID, commitSigs []CommitSig, aggrSig []byte) *Commit { // Make a copy of CommitSig to avoid side effects newCommitSigs := make([]CommitSig, len(commitSigs)) @@ -853,7 +854,7 @@ func (commit *Commit) ValidateBasic() error { return fmt.Errorf("wrong CommitSig #%d: %v", i, err) } if !commitSig.Absent() && commitSig.Signature == nil { - omittedSignatures += 1 + omittedSignatures++ } } if commit.AggregatedSignature == nil { diff --git a/types/vote_set.go b/types/vote_set.go index 2e08976a7..08157c219 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -224,9 +224,10 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { if err == nil { voteSet.aggregatedSignature = aggrSign vote.Signature = nil - } else { - // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. } + // else { + // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. + // } return added, nil } From 7a7d0558d740a82760ffafb30295a0082e3295ec Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Fri, 4 Sep 2020 18:03:16 +0900 Subject: [PATCH 04/36] set default value of Vote.Signature to zero-length bytes --- blockchain/v1/reactor_test.go | 1 + consensus/common_test.go | 1 + consensus/invalid_test.go | 1 + consensus/state.go | 1 + consensus/types/height_vote_set_test.go | 1 + lite/helpers.go | 1 + lite2/helpers_test.go | 1 + privval/file_test.go | 1 + privval/signer_client_test.go | 16 ++++++++-------- rpc/client/rpc_test.go | 10 ++++++++-- state/validation_test.go | 1 + .../tm-signer-harness/internal/test_harness.go | 1 + types/block_test.go | 1 + types/evidence_test.go | 1 + types/test_util.go | 2 ++ types/vote.go | 4 ++-- types/vote_set_test.go | 6 ++++++ types/vote_test.go | 18 ++++++++++-------- 18 files changed, 48 insertions(+), 20 deletions(-) diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index a402dfb2d..7ea194614 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -67,6 +67,7 @@ func makeVote( Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } _ = privVal.SignVote(header.ChainID, vote) diff --git a/consensus/common_test.go b/consensus/common_test.go index 05e488b56..8ee2f1f38 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -101,6 +101,7 @@ func (vs *validatorStub) signVote( Timestamp: tmtime.Now(), Type: voteType, BlockID: types.BlockID{Hash: hash, PartsHeader: header}, + Signature: []byte{}, } err = vs.PrivValidator.SignVote(config.ChainID(), vote) diff --git a/consensus/invalid_test.go b/consensus/invalid_test.go index 1ae0a69ec..627240b3e 100644 --- a/consensus/invalid_test.go +++ b/consensus/invalid_test.go @@ -80,6 +80,7 @@ func invalidDoPrevoteFunc(t *testing.T, height int64, round int, cs *State, sw * BlockID: types.BlockID{ Hash: blockHash, PartsHeader: types.PartSetHeader{Total: 1, Hash: tmrand.Bytes(32)}}, + Signature: []byte{}, } cs.privValidator.SignVote(cs.state.ChainID, precommit) cs.privValidator = nil // disable priv val so we don't do normal votes diff --git a/consensus/state.go b/consensus/state.go index bb049d2b5..bab97babf 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1941,6 +1941,7 @@ func (cs *State) signVote( Timestamp: cs.voteTime(), Type: msgType, BlockID: types.BlockID{Hash: hash, PartsHeader: header}, + Signature: []byte{}, } err = cs.privValidator.SignVote(cs.state.ChainID, vote) diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index 373e28cca..3953d0bf4 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -67,6 +67,7 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: types.BlockID{Hash: []byte("fakehash"), PartsHeader: types.PartSetHeader{}}, + Signature: []byte{}, } chainID := config.ChainID() err = privVal.SignVote(chainID, vote) diff --git a/lite/helpers.go b/lite/helpers.go index f56071ed0..cb4254878 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -104,6 +104,7 @@ func makeVote(header *types.Header, voterSet *types.VoterSet, key crypto.PrivKey Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } // Sign it signBytes := vote.SignBytes(header.ChainID) diff --git a/lite2/helpers_test.go b/lite2/helpers_test.go index 291b417a4..db167f981 100644 --- a/lite2/helpers_test.go +++ b/lite2/helpers_test.go @@ -146,6 +146,7 @@ func makeVote(header *types.Header, voterIdx int, key crypto.PrivKey, blockID ty Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } // Sign it signBytes := vote.SignBytes(header.ChainID) diff --git a/privval/file_test.go b/privval/file_test.go index a4b3a3c8c..ad57b83f0 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -330,6 +330,7 @@ func newVote(addr types.Address, idx int, height int64, round int, typ byte, blo Type: types.SignedMsgType(typ), Timestamp: tmtime.Now(), BlockID: blockID, + Signature: []byte{}, } } diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 1853192cc..7a5b785a0 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -127,8 +127,8 @@ func TestSignerGenerateVRFProof(t *testing.T) { func TestSignerVote(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} - have := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} + have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -143,8 +143,8 @@ func TestSignerVote(t *testing.T) { func TestSignerVoteResetDeadline(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} - have := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} + have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -169,8 +169,8 @@ func TestSignerVoteResetDeadline(t *testing.T) { func TestSignerVoteKeepAlive(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} - have := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} + have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -216,7 +216,7 @@ func TestSignerSignProposalErrors(t *testing.T) { func TestSignerSignVoteErrors(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - vote := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + vote := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} // Replace signer service privval with one that always fails tc.signerServer.privVal = types.NewErroringMockPV() @@ -271,7 +271,7 @@ func TestSignerUnexpectedResponse(t *testing.T) { defer tc.signerClient.Close() ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} e := tc.signerClient.SignVote(tc.chainID, want) assert.EqualError(t, e, "received unexpected response") diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 48c4f9cbc..0e43485dc 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -564,11 +564,16 @@ func deepcpVote(vote *types.Vote) (res *types.Vote) { Hash: make([]byte, len(vote.BlockID.Hash)), PartsHeader: vote.BlockID.PartsHeader, }, - Signature: make([]byte, len(vote.Signature)), + Signature: []byte{}, } copy(res.ValidatorAddress, vote.ValidatorAddress) copy(res.BlockID.Hash, vote.BlockID.Hash) - copy(res.Signature, vote.Signature) + if vote.Signature == nil { + res.Signature = nil + } else if len(vote.Signature) > 0 { + res.Signature = make([]byte, len(vote.Signature)) + copy(res.Signature, vote.Signature) + } return } @@ -606,6 +611,7 @@ func makeEvidences( Hash: tmhash.Sum([]byte("partset")), }, }, + Signature: []byte{}, } var err error diff --git a/state/validation_test.go b/state/validation_test.go index c70abc4c3..7fe12f733 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -188,6 +188,7 @@ func TestValidateBlockCommit(t *testing.T) { Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } err = badPrivVal.SignVote(chainID, goodVote) require.NoError(t, err, "height %d", height) diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go index f9d48fdcb..bfef830d4 100644 --- a/tools/tm-signer-harness/internal/test_harness.go +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -273,6 +273,7 @@ func (th *TestHarness) TestSignVote() error { ValidatorIndex: 0, ValidatorAddress: tmhash.SumTruncated([]byte("addr")), Timestamp: time.Now(), + Signature: []byte{}, } voteBytes := vote.SignBytes(th.chainID) // sign the vote diff --git a/types/block_test.go b/types/block_test.go index 34c0d10ae..440d1bd70 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -560,6 +560,7 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { Type: PrecommitType, BlockID: tc.blockIDs[n], Timestamp: tmtime.Now(), + Signature: []byte{}, } added, err := signAddVote(vals[vi], vote, voteSet) diff --git a/types/evidence_test.go b/types/evidence_test.go index 64d7178b1..e1ddfec9a 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -30,6 +30,7 @@ func makeVote( Round: round, Type: SignedMsgType(step), BlockID: blockID, + Signature: []byte{}, } err = val.SignVote(chainID, v) if err != nil { diff --git a/types/test_util.go b/types/test_util.go index 377c965a8..115d0575b 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -23,6 +23,7 @@ func MakeCommit(blockID BlockID, height int64, round int, Type: PrecommitType, BlockID: blockID, Timestamp: now, + Signature: []byte{}, } _, err = signAddVote(validators[i], vote, voteSet) @@ -64,6 +65,7 @@ func MakeVote( Timestamp: now, Type: PrecommitType, BlockID: blockID, + Signature: []byte{}, } if err := privVal.SignVote(chainID, vote); err != nil { return nil, err diff --git a/types/vote.go b/types/vote.go index 0568396db..5f0cf9782 100644 --- a/types/vote.go +++ b/types/vote.go @@ -164,10 +164,10 @@ func (vote *Vote) ValidateBasic() error { if vote.ValidatorIndex < 0 { return errors.New("negative ValidatorIndex") } - if len(vote.Signature) == 0 { + if vote.Signature != nil && len(vote.Signature) == 0 { return errors.New("signature is missing") } - if len(vote.Signature) > MaxSignatureSize { + if vote.Signature != nil && len(vote.Signature) > MaxSignatureSize { return fmt.Errorf("signature is too big %d (max: %d)", len(vote.Signature), MaxSignatureSize) } return nil diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 0153f38d1..c3985c455 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -97,6 +97,7 @@ func TestAddVote(t *testing.T) { Type: PrevoteType, Timestamp: tmtime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } _, err = signAddVote(val0, vote, voteSet) if err != nil { @@ -127,6 +128,7 @@ func Test2_3Majority(t *testing.T) { Type: PrevoteType, Timestamp: tmtime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } // 6 out of 10 voted for nil. for i := 0; i < 6; i++ { @@ -193,6 +195,7 @@ func Test2_3MajorityRedux(t *testing.T) { Timestamp: tmtime.Now(), Type: PrevoteType, BlockID: BlockID{blockHash, blockPartsHeader}, + Signature: []byte{}, } // 66 out of 100 voted for nil. @@ -306,6 +309,7 @@ func TestBadVotes(t *testing.T) { Timestamp: tmtime.Now(), Type: PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } // val0 votes for nil. @@ -383,6 +387,7 @@ func TestConflicts(t *testing.T) { Timestamp: tmtime.Now(), Type: PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } val0, err := privValidators[0].GetPubKey() @@ -525,6 +530,7 @@ func TestMakeCommit(t *testing.T) { Timestamp: tmtime.Now(), Type: PrecommitType, BlockID: BlockID{blockHash, blockPartsHeader}, + Signature: []byte{}, } // 6 out of 10 voted for some block. diff --git a/types/vote_test.go b/types/vote_test.go index ee1645ef3..e15ecaf51 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -41,6 +41,7 @@ func exampleVote(t byte) *Vote { }, ValidatorAddress: crypto.AddressHash([]byte("validator_address")), ValidatorIndex: 56789, + Signature: []byte{}, } } @@ -62,13 +63,13 @@ func TestVoteSignBytesTestVectors(t *testing.T) { want []byte }{ 0: { - "", &Vote{}, + "", &Vote{Signature: []byte{}}, // NOTE: Height and Round are skipped here. This case needs to be considered while parsing. []byte{0xd, 0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, // with proper (fixed size) height and round (PreCommit): 1: { - "", &Vote{Height: 1, Round: 1, Type: PrecommitType}, + "", &Vote{Height: 1, Round: 1, Type: PrecommitType, Signature: []byte{}}, []byte{ 0x21, // length 0x8, // (field_number << 3) | wire_type @@ -83,7 +84,7 @@ func TestVoteSignBytesTestVectors(t *testing.T) { }, // with proper (fixed size) height and round (PreVote): 2: { - "", &Vote{Height: 1, Round: 1, Type: PrevoteType}, + "", &Vote{Height: 1, Round: 1, Type: PrevoteType, Signature: []byte{}}, []byte{ 0x21, // length 0x8, // (field_number << 3) | wire_type @@ -97,7 +98,7 @@ func TestVoteSignBytesTestVectors(t *testing.T) { 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, 3: { - "", &Vote{Height: 1, Round: 1}, + "", &Vote{Height: 1, Round: 1, Signature: []byte{}}, []byte{ 0x1f, // length 0x11, // (field_number << 3) | wire_type @@ -110,7 +111,7 @@ func TestVoteSignBytesTestVectors(t *testing.T) { }, // containing non-empty chain_id: 4: { - "test_chain_id", &Vote{Height: 1, Round: 1}, + "test_chain_id", &Vote{Height: 1, Round: 1, Signature: []byte{}}, []byte{ 0x2e, // length 0x11, // (field_number << 3) | wire_type @@ -132,8 +133,8 @@ func TestVoteSignBytesTestVectors(t *testing.T) { } func TestVoteProposalNotEq(t *testing.T) { - cv := CanonicalizeVote("", &Vote{Height: 1, Round: 1}) - p := CanonicalizeProposal("", &Proposal{Height: 1, Round: 1}) + cv := CanonicalizeVote("", &Vote{Height: 1, Round: 1, Signature: []byte{}}) + p := CanonicalizeProposal("", &Proposal{Height: 1, Round: 1, Signature: []byte{}}) vb, err := cdc.MarshalBinaryLengthPrefixed(cv) require.NoError(t, err) pb, err := cdc.MarshalBinaryLengthPrefixed(p) @@ -231,6 +232,7 @@ func TestMaxVoteBytes(t *testing.T) { Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")), }, }, + Signature: []byte{}, } privVal := NewMockPV() @@ -300,7 +302,7 @@ func TestVoteProtobuf(t *testing.T) { expPass bool }{ {"success", vote, true}, - {"fail vote validate basic", &Vote{}, false}, + {"fail vote validate basic", &Vote{Signature: []byte{}}, false}, {"failure nil", nil, false}, } for _, tc := range testCases { From aadda7bbea09b0046868e9a01be8cf79e8dceceb Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Mon, 7 Sep 2020 16:56:55 +0900 Subject: [PATCH 05/36] fix: docker file; add library --- DOCKER/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index e3a918749..2bac91cb7 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -15,7 +15,7 @@ ENV TMHOME /tendermint # could execute bash commands. RUN apk update && \ apk upgrade && \ - apk --no-cache add curl jq bash && \ + apk --no-cache add git make gcc libc-dev build-base curl jq bash && \ addgroup tmuser && \ adduser -S -G tmuser tmuser -h "$TMHOME" From e38c311f0c013c694fe3dd1d327e71d8fb8634bc Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 8 Sep 2020 16:05:21 +0900 Subject: [PATCH 06/36] fixed to restore aggregate signature when restoring Commit to VoteSet --- types/block.go | 1 + types/block_test.go | 3 +++ types/vote_test.go | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/types/block.go b/types/block.go index 3c3e95656..873652d96 100644 --- a/types/block.go +++ b/types/block.go @@ -740,6 +740,7 @@ func NewCommitWithAggregatedSignature( // Inverse of VoteSet.MakeCommit(). func CommitToVoteSet(chainID string, commit *Commit, voters *VoterSet) *VoteSet { voteSet := NewVoteSet(chainID, commit.Height, commit.Round, PrecommitType, voters) + voteSet.aggregatedSignature = commit.AggregatedSignature for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some precommits can be missing. diff --git a/types/block_test.go b/types/block_test.go index 440d1bd70..cb17dd0af 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -512,6 +512,9 @@ func TestCommitToVoteSet(t *testing.T) { chainID := voteSet.ChainID() voteSet2 := CommitToVoteSet(chainID, commit, valSet) + assert.Equal(t, voteSet.aggregatedSignature, commit.AggregatedSignature) + assert.Equal(t, commit.AggregatedSignature, voteSet2.aggregatedSignature) + for i := 0; i < len(vals); i++ { vote1 := voteSet.GetByIndex(i) vote2 := voteSet2.GetByIndex(i) diff --git a/types/vote_test.go b/types/vote_test.go index e15ecaf51..c8a30df9e 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -275,7 +275,7 @@ func TestVoteValidateBasic(t *testing.T) { }, true}, {"Invalid Address", func(v *Vote) { v.ValidatorAddress = make([]byte, 1) }, true}, {"Invalid ValidatorIndex", func(v *Vote) { v.ValidatorIndex = -1 }, true}, - {"Invalid Signature", func(v *Vote) { v.Signature = nil }, true}, + {"Invalid Signature", func(v *Vote) { v.Signature = []byte{} }, true}, {"Too big Signature", func(v *Vote) { v.Signature = make([]byte, MaxSignatureSize+1) }, true}, } for _, tc := range testCases { From e13251098479b19bedd88d7e65a2c18177f6c9c2 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 8 Sep 2020 18:07:29 +0900 Subject: [PATCH 07/36] fixed conversion from int to string yields a string of one rune --- libs/cmap/cmap_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/cmap/cmap_test.go b/libs/cmap/cmap_test.go index b6ea0117d..b1ab85c0c 100644 --- a/libs/cmap/cmap_test.go +++ b/libs/cmap/cmap_test.go @@ -60,10 +60,10 @@ func TestContains(t *testing.T) { func BenchmarkCMapHas(b *testing.B) { m := NewCMap() for i := 0; i < 1000; i++ { - m.Set(string(i), i) + m.Set(fmt.Sprint(i), i) } b.ResetTimer() for i := 0; i < b.N; i++ { - m.Has(string(i)) + m.Has(fmt.Sprint(i)) } } From 80eb00b526663f9b75422516953ed929da797492 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Thu, 3 Sep 2020 17:52:12 +0900 Subject: [PATCH 08/36] added signature aggregation and verify to block --- crypto/bls/bls.go | 73 ++++++++++++- crypto/bls/bls_test.go | 233 ++++++++++++++++++++++++++++++++++++++++ proto/types/types.pb.go | 177 +++++++++++++++--------------- proto/types/types.proto | 5 +- types/block.go | 78 ++++++++++++-- types/block_test.go | 65 +++++++++++ types/vote.go | 2 +- types/vote_set.go | 19 +++- types/voter_set.go | 21 +++- 9 files changed, 573 insertions(+), 100 deletions(-) diff --git a/crypto/bls/bls.go b/crypto/bls/bls.go index 4744ef947..acb99bcd0 100644 --- a/crypto/bls/bls.go +++ b/crypto/bls/bls.go @@ -2,6 +2,7 @@ package bls import ( "bytes" + "crypto/sha512" "crypto/subtle" "fmt" @@ -46,6 +47,72 @@ func init() { // PrivKeyBLS12 implements crypto.PrivKey. type PrivKeyBLS12 [PrivKeyBLS12Size]byte +// AddSignature adds a BLS signature to the init. When the init is nil, then a new aggregate signature is built +// from specified signature. +func AddSignature(init []byte, signature []byte) (aggrSign []byte, err error) { + if init == nil { + blsSign := bls.Sign{} + init = blsSign.Serialize() + } else if len(init) != SignatureSize { + err = fmt.Errorf("invalid BLS signature: aggregated signature size %d is not valid size %d", + len(init), SignatureSize) + return + } + if len(signature) != SignatureSize { + err = fmt.Errorf("invalid BLS signature: signature size %d is not valid size %d", + len(signature), SignatureSize) + return + } + blsSign := bls.Sign{} + err = blsSign.Deserialize(signature) + if err != nil { + return + } + aggrBLSSign := bls.Sign{} + err = aggrBLSSign.Deserialize(init) + if err != nil { + return + } + aggrBLSSign.Add(&blsSign) + aggrSign = aggrBLSSign.Serialize() + return +} + +func VerifyAggregatedSignature(aggregatedSignature []byte, pubKeys []PubKeyBLS12, msgs [][]byte) error { + if len(pubKeys) != len(msgs) { + return fmt.Errorf("the number of public keys %d doesn't match the one of messages %d", + len(pubKeys), len(msgs)) + } + if aggregatedSignature == nil { + if len(pubKeys) == 0 { + return nil + } else { + return fmt.Errorf( + "the aggregate signature was omitted, even though %d public keys were specified", len(pubKeys)) + } + } + aggrSign := bls.Sign{} + err := aggrSign.Deserialize(aggregatedSignature) + if err != nil { + return err + } + blsPubKeys := make([]bls.PublicKey, len(pubKeys)) + hashes := make([][]byte, len(msgs)) + for i := 0; i < len(pubKeys); i++ { + blsPubKeys[i] = bls.PublicKey{} + err = blsPubKeys[i].Deserialize(pubKeys[i][:]) + if err != nil { + return err + } + hash := sha512.Sum512_256(msgs[i]) + hashes[i] = hash[:] + } + if !aggrSign.VerifyAggregateHashes(blsPubKeys, hashes) { + return fmt.Errorf("failed to verify the aggregated hashes by %d public keys", len(blsPubKeys)) + } + return nil +} + // GenPrivKey generates a new BLS12-381 private key. func GenPrivKey() PrivKeyBLS12 { sigKey := bls.SecretKey{} @@ -74,7 +141,8 @@ func (privKey PrivKeyBLS12) Sign(msg []byte) ([]byte, error) { if err != nil { panic(fmt.Sprintf("Failed to copy the private key: %s", err)) } - sign := blsKey.SignByte(msg) + hash := sha512.Sum512_256(msg) + sign := blsKey.SignHash(hash[:]) return sign.Serialize(), nil } @@ -143,7 +211,8 @@ func (pubKey PubKeyBLS12) VerifyBytes(msg []byte, sig []byte) bool { if err != nil { return false } - return blsSign.VerifyByte(&blsPubKey, msg) + hash := sha512.Sum512_256(msg) + return blsSign.VerifyHash(&blsPubKey, hash[:]) } // VRFVerify is not supported in BLS12. diff --git a/crypto/bls/bls_test.go b/crypto/bls/bls_test.go index f00df1da6..2070f32e9 100644 --- a/crypto/bls/bls_test.go +++ b/crypto/bls/bls_test.go @@ -2,8 +2,11 @@ package bls_test import ( "bytes" + "crypto/sha256" "fmt" + "math/rand" "testing" + "time" b "github.com/herumi/bls-eth-go-binary/bls" "github.com/stretchr/testify/assert" @@ -234,6 +237,236 @@ func TestBasicSignatureFunctions(t *testing.T) { } } +func TestSignatureAggregationAndVerify(t *testing.T) { + privKeys := make([]b.SecretKey, 25) + pubKeys := make([]b.PublicKey, len(privKeys)) + msgs := make([][]byte, len(privKeys)) + hash32s := make([][32]byte, len(privKeys)) + signatures := make([]b.Sign, len(privKeys)) + for i, privKey := range privKeys { + privKey.SetByCSPRNG() + pubKeys[i] = *privKey.GetPublicKey() + msgs[i] = []byte(fmt.Sprintf("hello, world #%d", i)) + hash32s[i] = sha256.Sum256(msgs[i]) + signatures[i] = *privKey.SignHash(hash32s[i][:]) + + // normal single-hash case + if !signatures[i].VerifyHash(&pubKeys[i], hash32s[i][:]) { + t.Fail() + } + + // in case where 1-bit of hash was garbled + garbledHash := make([]byte, len(msgs[i])) + copy(garbledHash, msgs[i]) + garbledHash[0] ^= 1 << 0 + if garbledHash[0] == msgs[i][0] || signatures[i].VerifyByte(&pubKeys[i], garbledHash) { + t.Fail() + } + + // Verification using an invalid public key + } + + // aggregation + multiSig := b.Sign{} + multiSig.Aggregate(signatures) + + // normal case + hashes := make([][]byte, len(privKeys)) + for i := 0; i < len(hashes); i++ { + hashes[i] = hash32s[i][:] + } + if !multiSig.VerifyAggregateHashes(pubKeys, hashes) { + t.Fatalf("failed to validate the aggregate signature of the hashed message") + } + + // in case where 1-bit of signature was garbled + multiSigBytes := multiSig.Serialize() + for i := range multiSigBytes { + for j := 0; j < 8; j++ { + garbledMultiSigBytes := make([]byte, len(multiSigBytes)) + copy(garbledMultiSigBytes, multiSigBytes) + garbledMultiSigBytes[i] ^= 1 << j + if garbledMultiSigBytes[i] == multiSigBytes[i] { + t.Fail() + } + garbledMultiSig := b.Sign{} + err := garbledMultiSig.Deserialize(garbledMultiSigBytes) + if err == nil { + // Note that in some cases Deserialize() fails + if garbledMultiSig.VerifyAggregateHashes(pubKeys, hashes) { + t.Errorf("successfully verified the redacted signature") + } + } + } + } + + // in case a public key used for verification is replaced + invalidPrivKey := b.SecretKey{} + invalidPrivKey.SetByCSPRNG() + invalidPubKeys := make([]b.PublicKey, len(pubKeys)) + copy(invalidPubKeys, pubKeys) + invalidPubKeys[len(invalidPubKeys)-1] = *invalidPrivKey.GetPublicKey() + if multiSig.VerifyAggregateHashes(invalidPubKeys, hashes) { + t.Fatalf("successfully verified that it contains a public key that was not involved in the signing") + } + + // in case a hash used for verification is replaced + invalidHashes := make([][]byte, len(hashes)) + copy(invalidHashes, hashes) + invalidHash := sha256.Sum256([]byte("hello, world #99")) + invalidHashes[len(invalidHashes)-1] = invalidHash[:] + if multiSig.VerifyAggregateHashes(pubKeys, invalidHashes) { + t.Fatalf("successfully verified that it contains a hash that was not involved in the signing") + } +} + +func generatePubKeysAndSigns(t *testing.T, size int) ([]bls.PubKeyBLS12, [][]byte, [][]byte) { + pubKeys := make([]bls.PubKeyBLS12, size) + msgs := make([][]byte, len(pubKeys)) + sigs := make([][]byte, len(pubKeys)) + for i := 0; i < len(pubKeys); i++ { + var err error + privKey := bls.GenPrivKey() + pubKeys[i] = blsPublicKey(t, privKey.PubKey()) + msgs[i] = []byte(fmt.Sprintf("hello, workd #%d", i)) + sigs[i], err = privKey.Sign(msgs[i]) + if err != nil { + t.Fatal(fmt.Sprintf("fail to sign: %s", err)) + } + if !pubKeys[i].VerifyBytes(msgs[i], sigs[i]) { + t.Fatal("fail to verify signature") + } + } + return pubKeys, msgs, sigs +} + +func blsPublicKey(t *testing.T, pubKey crypto.PubKey) bls.PubKeyBLS12 { + blsPubKey, ok := pubKey.(bls.PubKeyBLS12) + if !ok { + t.Fatal(fmt.Sprintf("specified public key is not for BLS")) + } + return blsPubKey +} +func aggregateSignatures(init []byte, signatures [][]byte) (aggrSig []byte, err error) { + aggrSig = init + for _, sign := range signatures { + aggrSig, err = bls.AddSignature(aggrSig, sign) + if err != nil { + return + } + } + return +} + +func TestAggregatedSignature(t *testing.T) { + + // generate BLS signatures and public keys + pubKeys, msgs, sigs := generatePubKeysAndSigns(t, 25) + + // aggregate signatures + aggrSig, err := aggregateSignatures(nil, sigs) + if err != nil { + t.Error(fmt.Sprintf("fail to aggregate BLS signatures: %s", err)) + } + if len(aggrSig) != bls.SignatureSize { + t.Error(fmt.Sprintf("inconpatible signature size: %d != %d", len(aggrSig), bls.SignatureSize)) + } + + // validate the aggregated signature + if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, msgs); err != nil { + t.Errorf("fail to verify aggregated signature: %s", err) + } + + // validate with the public keys and messages pair in random order + t.Run("Doesn't Depend on the Order of PublicKey-Message Pairs", func(t *testing.T) { + shuffledPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + shuffledMsgs := make([][]byte, len(msgs)) + copy(shuffledPubKeys, pubKeys) + copy(shuffledMsgs, msgs) + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(shuffledPubKeys), func(i, j int) { + shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] + shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] + }) + if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, shuffledMsgs); err != nil { + t.Errorf("fail to verify the aggregated signature with random order: %s", err) + } + }) + + // validate with the public keys in random order + t.Run("Incorrect Public Key Order", func(t *testing.T) { + shuffledPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + copy(shuffledPubKeys, pubKeys) + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(shuffledPubKeys), func(i, j int) { shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] }) + if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, msgs); err == nil { + t.Error("successfully validated with public keys of different order") + } + }) + + // validate with the messages in random order + t.Run("Incorrect Message Order", func(t *testing.T) { + shuffledMsgs := make([][]byte, len(msgs)) + copy(shuffledMsgs, msgs) + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(shuffledMsgs), func(i, j int) { shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] }) + if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, shuffledMsgs); err == nil { + t.Error("successfully validated with messages of different order") + } + }) + + // replace one public key with another and detect + t.Run("Replace One Public Key", func(t *testing.T) { + pubKey, _ := bls.GenPrivKey().PubKey().(bls.PubKeyBLS12) + replacedPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + copy(replacedPubKeys, pubKeys) + replacedPubKeys[0] = pubKey + if err := bls.VerifyAggregatedSignature(aggrSig, replacedPubKeys, msgs); err == nil { + t.Error("verification with an invalid key was successful") + } + }) + + // replace one message with another and detect + t.Run("Replace One Message", func(t *testing.T) { + msg := []byte(fmt.Sprintf("hello, world #%d replaced", len(msgs))) + replacedMsgs := make([][]byte, len(msgs)) + copy(replacedMsgs, msgs) + replacedMsgs[0] = msg + if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, replacedMsgs); err == nil { + t.Error("verification with an invalid message was successful") + } + }) + + // add new signature to existing aggregated signature and verify + t.Run("Incremental Update", func(t *testing.T) { + msg := []byte(fmt.Sprintf("hello, world #%d", len(msgs))) + privKey := bls.GenPrivKey() + pubKey := privKey.PubKey() + sig, err := privKey.Sign(msg) + assert.Nilf(t, err, "%s", err) + newAggrSig, _ := aggregateSignatures(aggrSig, [][]byte{sig}) + newPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) + copy(newPubKeys, pubKeys) + newPubKeys = append(newPubKeys, blsPublicKey(t, pubKey)) + newMsgs := make([][]byte, len(msgs)) + copy(newMsgs, msgs) + newMsgs = append(newMsgs, msg) + if err := bls.VerifyAggregatedSignature(newAggrSig, newPubKeys, newMsgs); err != nil { + t.Errorf("fail to verify the aggregate signature with the new signature: %s", err) + } + }) + + // nil is returned for nil and empty signature + nilSig, _ := aggregateSignatures(nil, [][]byte{}) + assert.Nil(t, nilSig) + + // a non-BLS signature contained + func() { + _, err = aggregateSignatures(nil, [][]byte{make([]byte, 0)}) + assert.NotNil(t, err) + }() +} + func TestSignatureAggregation(t *testing.T) { publicKeys := make([]b.PublicKey, 25) aggregatedSignature := b.Sign{} diff --git a/proto/types/types.pb.go b/proto/types/types.pb.go index cbc58d4ad..6b7c0c6e9 100644 --- a/proto/types/types.pb.go +++ b/proto/types/types.pb.go @@ -540,8 +540,9 @@ type Commit struct { Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` BlockID BlockID `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id"` Signatures []CommitSig `protobuf:"bytes,4,rep,name=signatures,proto3" json:"signatures"` - Hash []byte `protobuf:"bytes,5,opt,name=hash,proto3" json:"hash,omitempty"` - BitArray *bits.BitArray `protobuf:"bytes,6,opt,name=bit_array,json=bitArray,proto3" json:"bit_array,omitempty"` + AggregatedSignature []byte `protobuf:"bytes,5,opt,name=aggregated_signature,json=aggregatedSignature,proto3" json:"aggregated_signature,omitempty"` + Hash []byte `protobuf:"bytes,6,opt,name=hash,proto3" json:"hash,omitempty"` + BitArray *bits.BitArray `protobuf:"bytes,7,opt,name=bit_array,json=bitArray,proto3" json:"bit_array,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -599,6 +600,13 @@ func (m *Commit) GetSignatures() []CommitSig { return nil } +func (m *Commit) GetAggregatedSignature() []byte { + if m != nil { + return m.AggregatedSignature + } + return nil +} + func (m *Commit) GetHash() []byte { if m != nil { return m.Hash @@ -889,86 +897,87 @@ func init() { func init() { proto.RegisterFile("proto/types/types.proto", fileDescriptor_ff06f8095857fb18) } var fileDescriptor_ff06f8095857fb18 = []byte{ - // 1286 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6e, 0xdb, 0xc6, - 0x13, 0x37, 0x25, 0xca, 0x92, 0x86, 0x92, 0x2d, 0xf3, 0xef, 0x7f, 0xa2, 0xca, 0xad, 0xa5, 0xc8, - 0x4d, 0xea, 0x7c, 0x80, 0x2a, 0x5c, 0xa0, 0x68, 0x80, 0x5e, 0x24, 0xdb, 0x71, 0x84, 0xd8, 0xb2, - 0x40, 0xa9, 0xe9, 0xc7, 0x85, 0x58, 0x89, 0x1b, 0x8a, 0x08, 0x45, 0x12, 0xdc, 0x95, 0x61, 0xa7, - 0x40, 0xcf, 0x85, 0x4f, 0x7d, 0x01, 0x5f, 0x9a, 0x16, 0xe8, 0x5b, 0xb4, 0xc7, 0x9e, 0xfa, 0x08, - 0x29, 0x90, 0xbe, 0x42, 0x1f, 0xa0, 0xd8, 0x0f, 0x52, 0x52, 0x64, 0xb7, 0x41, 0x93, 0x5e, 0x12, - 0xee, 0xcc, 0x6f, 0x66, 0x77, 0x7e, 0xf3, 0x9b, 0x5d, 0x19, 0xae, 0x87, 0x51, 0x40, 0x83, 0x06, - 0x3d, 0x0b, 0x31, 0x11, 0xff, 0x1a, 0xdc, 0xa2, 0x5f, 0xa3, 0xd8, 0xb7, 0x71, 0x34, 0x76, 0x7d, - 0x2a, 0x2c, 0x06, 0xf7, 0x56, 0x6e, 0xd1, 0x91, 0x1b, 0xd9, 0x56, 0x88, 0x22, 0x7a, 0xd6, 0x10, - 0xc1, 0x4e, 0xe0, 0x04, 0xd3, 0x2f, 0x81, 0xae, 0x54, 0x9d, 0x20, 0x70, 0x3c, 0x2c, 0x20, 0x83, - 0xc9, 0x93, 0x06, 0x75, 0xc7, 0x98, 0x50, 0x34, 0x0e, 0x25, 0x60, 0x43, 0x84, 0x78, 0xee, 0x80, - 0x34, 0x06, 0x2e, 0x9d, 0xdb, 0xbd, 0x52, 0x15, 0xce, 0x61, 0x74, 0x16, 0xd2, 0xa0, 0x31, 0xc6, - 0xd1, 0x53, 0x0f, 0xcf, 0x01, 0x64, 0xf4, 0x09, 0x8e, 0x88, 0x1b, 0xf8, 0xf1, 0xff, 0xc2, 0x59, - 0xbf, 0x0f, 0xc5, 0x2e, 0x8a, 0x68, 0x0f, 0xd3, 0x87, 0x18, 0xd9, 0x38, 0xd2, 0xd7, 0x21, 0x43, - 0x03, 0x8a, 0xbc, 0xb2, 0x52, 0x53, 0xb6, 0xd3, 0xa6, 0x58, 0xe8, 0x3a, 0xa8, 0x23, 0x44, 0x46, - 0xe5, 0x54, 0x4d, 0xd9, 0x2e, 0x98, 0xfc, 0xbb, 0xfe, 0x35, 0xa8, 0x2c, 0x94, 0x45, 0xb8, 0xbe, - 0x8d, 0x4f, 0x79, 0x44, 0xd1, 0x14, 0x0b, 0x66, 0x1d, 0x9c, 0x51, 0x4c, 0x64, 0x88, 0x58, 0xe8, - 0x07, 0x90, 0x09, 0xa3, 0x20, 0x78, 0x52, 0x4e, 0xd7, 0x94, 0x6d, 0x6d, 0xe7, 0xae, 0xb1, 0x40, - 0x9d, 0xa8, 0xc3, 0x10, 0x75, 0x18, 0x3d, 0x77, 0x1c, 0x7a, 0xb8, 0xcb, 0x42, 0x5a, 0xea, 0xaf, - 0x2f, 0xaa, 0x4b, 0xa6, 0x88, 0xaf, 0x8f, 0x21, 0xdb, 0xf2, 0x82, 0xe1, 0xd3, 0xf6, 0x5e, 0x72, - 0x36, 0x65, 0x7a, 0x36, 0xbd, 0x03, 0x05, 0x46, 0x3b, 0xb1, 0x46, 0xbc, 0x2a, 0x7e, 0x08, 0x6d, - 0xe7, 0xa6, 0x71, 0x79, 0xa7, 0x8c, 0x39, 0x0a, 0xe4, 0x46, 0x1a, 0x4f, 0x20, 0x4c, 0xf5, 0xef, - 0x33, 0xb0, 0x2c, 0x09, 0xda, 0x85, 0xac, 0xa4, 0x90, 0xef, 0xa8, 0xed, 0x6c, 0x2d, 0x66, 0x8d, - 0x39, 0xde, 0x0d, 0x7c, 0x82, 0x7d, 0x32, 0x21, 0x32, 0x67, 0x1c, 0xa9, 0xdf, 0x82, 0xdc, 0x70, - 0x84, 0x5c, 0xdf, 0x72, 0x6d, 0x7e, 0xb6, 0x7c, 0x4b, 0x7b, 0xf9, 0xa2, 0x9a, 0xdd, 0x65, 0xb6, - 0xf6, 0x9e, 0x99, 0xe5, 0xce, 0xb6, 0xad, 0x5f, 0x83, 0xe5, 0x11, 0x76, 0x9d, 0x11, 0xe5, 0x84, - 0xa5, 0x4d, 0xb9, 0xd2, 0x3f, 0x01, 0x95, 0x89, 0xa4, 0xac, 0xf2, 0x13, 0x54, 0x0c, 0xa1, 0x20, - 0x23, 0x56, 0x90, 0xd1, 0x8f, 0x15, 0xd4, 0xca, 0xb1, 0x8d, 0xbf, 0xfb, 0xbd, 0xaa, 0x98, 0x3c, - 0x42, 0xff, 0x02, 0x8a, 0x1e, 0x22, 0xd4, 0x1a, 0x30, 0xf6, 0xd8, 0xf6, 0x19, 0x9e, 0xa2, 0x7a, - 0x15, 0x35, 0x92, 0xe5, 0xd6, 0xff, 0x58, 0x9e, 0x97, 0x2f, 0xaa, 0xda, 0x21, 0x22, 0x54, 0x1a, - 0x4d, 0xcd, 0x4b, 0x16, 0xb6, 0xbe, 0x0d, 0x25, 0x9e, 0x79, 0x18, 0x8c, 0xc7, 0x2e, 0xb5, 0x78, - 0x4f, 0x96, 0x79, 0x4f, 0x56, 0x98, 0x7d, 0x97, 0x9b, 0x1f, 0xb2, 0xee, 0x6c, 0x40, 0xde, 0x46, - 0x14, 0x09, 0x48, 0x96, 0x43, 0x72, 0xcc, 0xc0, 0x9d, 0x55, 0xd0, 0x4e, 0x02, 0x8a, 0x23, 0x22, - 0xdc, 0x39, 0xee, 0x06, 0x61, 0xe2, 0x80, 0x0f, 0x60, 0xf5, 0x04, 0x79, 0xae, 0x8d, 0x68, 0x10, - 0x83, 0xf2, 0x62, 0x9b, 0xa9, 0x99, 0x03, 0x3f, 0x84, 0x75, 0x1f, 0x9f, 0x52, 0xeb, 0x55, 0x34, - 0x70, 0xb4, 0xce, 0x7c, 0x8f, 0xe7, 0x23, 0x6e, 0xc2, 0xca, 0x30, 0x6e, 0x99, 0xc0, 0x6a, 0x1c, - 0x5b, 0x4c, 0xac, 0x1c, 0xf6, 0x0e, 0xe4, 0x50, 0x18, 0x0a, 0x40, 0x81, 0x03, 0xb2, 0x28, 0x0c, - 0xb9, 0xeb, 0x0e, 0xac, 0x71, 0x12, 0x22, 0x4c, 0x26, 0x1e, 0x95, 0x49, 0x8a, 0x1c, 0xb3, 0xca, - 0x1c, 0xa6, 0xb0, 0x73, 0xec, 0x16, 0x14, 0xf1, 0x89, 0x6b, 0x63, 0x7f, 0x88, 0x05, 0x6e, 0x85, - 0xe3, 0x0a, 0xb1, 0x91, 0x83, 0x6e, 0x43, 0x29, 0x8c, 0x82, 0x30, 0x20, 0x38, 0xb2, 0x90, 0x6d, - 0x47, 0x98, 0x90, 0xf2, 0xaa, 0xc8, 0x17, 0xdb, 0x9b, 0xc2, 0x5c, 0xbf, 0x07, 0xea, 0x1e, 0xa2, - 0x48, 0x2f, 0x41, 0x9a, 0x9e, 0x92, 0xb2, 0x52, 0x4b, 0x6f, 0x17, 0x4c, 0xf6, 0x79, 0xe9, 0xf8, - 0xfe, 0x99, 0x02, 0xf5, 0x71, 0x40, 0xb1, 0x7e, 0x1f, 0x54, 0xd6, 0x6a, 0xae, 0xe6, 0x95, 0xab, - 0x67, 0xa4, 0xe7, 0x3a, 0x3e, 0xb6, 0x8f, 0x88, 0xd3, 0x3f, 0x0b, 0xb1, 0xc9, 0x43, 0x66, 0xe4, - 0x99, 0x9a, 0x93, 0xe7, 0x3a, 0x64, 0xa2, 0x60, 0xe2, 0xdb, 0x52, 0xb5, 0x62, 0xa1, 0x3f, 0x82, - 0x5c, 0xa2, 0x3a, 0xf5, 0xf5, 0x54, 0xb7, 0x2a, 0x55, 0x17, 0x0f, 0xbb, 0x99, 0x1d, 0x48, 0xb5, - 0xb5, 0x20, 0x9f, 0x5c, 0x93, 0x52, 0xc3, 0xaf, 0x37, 0x06, 0xd3, 0x30, 0xfd, 0x2e, 0xac, 0x25, - 0xda, 0x48, 0xc8, 0x15, 0x92, 0x2d, 0x25, 0x0e, 0xc9, 0xee, 0x9c, 0xec, 0x2c, 0x71, 0xe1, 0x65, - 0x79, 0x75, 0x53, 0xd9, 0xb5, 0xf9, 0xcd, 0xf7, 0x2e, 0xe4, 0x89, 0xeb, 0xf8, 0x88, 0x4e, 0x22, - 0x2c, 0xe5, 0x3b, 0x35, 0xd4, 0x9f, 0xa7, 0x60, 0x59, 0x8c, 0xc2, 0x0c, 0x7b, 0xca, 0xe5, 0xec, - 0x31, 0x52, 0x33, 0x97, 0xb1, 0x97, 0x7e, 0x53, 0xf6, 0x0e, 0x00, 0x92, 0x23, 0x91, 0xb2, 0x5a, - 0x4b, 0x6f, 0x6b, 0x3b, 0x37, 0xae, 0x4a, 0x27, 0x8e, 0xdb, 0x73, 0x1d, 0x79, 0x8b, 0xcd, 0x84, - 0x26, 0xca, 0xca, 0xcc, 0x5c, 0xbe, 0x4d, 0xc8, 0x0f, 0x5c, 0x6a, 0xa1, 0x28, 0x42, 0x67, 0x9c, - 0x4e, 0x6d, 0xe7, 0xfd, 0xc5, 0xdc, 0xec, 0x35, 0x33, 0xd8, 0x6b, 0x66, 0xb4, 0x5c, 0xda, 0x64, - 0x58, 0x33, 0x37, 0x90, 0x5f, 0xf5, 0x3f, 0x14, 0xc8, 0x27, 0xdb, 0xea, 0x07, 0x50, 0x8c, 0x4b, - 0xb7, 0x9e, 0x78, 0xc8, 0x91, 0x52, 0xdd, 0xfa, 0x87, 0xfa, 0x1f, 0x78, 0xc8, 0x31, 0x35, 0x59, - 0x32, 0x5b, 0x5c, 0xde, 0xf0, 0xd4, 0x15, 0x0d, 0x9f, 0x53, 0x58, 0xfa, 0xdf, 0x29, 0x6c, 0x4e, - 0x0b, 0xea, 0xab, 0x5a, 0xf8, 0x39, 0x05, 0xb9, 0x2e, 0x1f, 0x62, 0xe4, 0xfd, 0xe7, 0x63, 0x98, - 0x08, 0x69, 0x03, 0xf2, 0x61, 0xe0, 0x59, 0xc2, 0xa3, 0x72, 0x4f, 0x2e, 0x0c, 0x3c, 0x73, 0x41, - 0x65, 0x99, 0xb7, 0x3a, 0xa3, 0xcb, 0x6f, 0x81, 0xc1, 0xec, 0xab, 0x0c, 0x7e, 0x03, 0x05, 0x41, - 0x88, 0x7c, 0x9c, 0x3f, 0x66, 0x4c, 0xf0, 0x17, 0x5f, 0xbc, 0xcd, 0x9b, 0x57, 0x1d, 0x5e, 0xe0, - 0x4d, 0x89, 0x66, 0x71, 0xe2, 0xd9, 0x92, 0xbf, 0x14, 0x36, 0xff, 0x7e, 0x16, 0x4c, 0x89, 0xae, - 0xff, 0xa6, 0x40, 0x9e, 0x97, 0x7d, 0x84, 0x29, 0x9a, 0x23, 0x4f, 0x79, 0x53, 0xf2, 0xde, 0x03, - 0x10, 0xc9, 0x88, 0xfb, 0x0c, 0xcb, 0xc6, 0xe6, 0xb9, 0xa5, 0xe7, 0x3e, 0xc3, 0xfa, 0xa7, 0x49, - 0xa5, 0xe9, 0xd7, 0xa9, 0x54, 0x8e, 0x6e, 0x5c, 0xef, 0x75, 0xc8, 0xfa, 0x93, 0xb1, 0xc5, 0x9e, - 0x09, 0x55, 0x48, 0xc6, 0x9f, 0x8c, 0xfb, 0xa7, 0xe4, 0xce, 0x2f, 0x0a, 0x68, 0x33, 0xe3, 0xa3, - 0x57, 0xe0, 0x5a, 0xeb, 0xf0, 0x78, 0xf7, 0xd1, 0x9e, 0xd5, 0xde, 0xb3, 0x1e, 0x1c, 0x36, 0x0f, - 0xac, 0xcf, 0x3a, 0x8f, 0x3a, 0xc7, 0x9f, 0x77, 0x4a, 0x4b, 0x7a, 0x03, 0xd6, 0xb9, 0x2f, 0x71, - 0x35, 0x5b, 0xbd, 0xfd, 0x4e, 0xbf, 0xa4, 0x54, 0xfe, 0x7f, 0x7e, 0x51, 0x5b, 0x9b, 0x49, 0xd3, - 0x1c, 0x10, 0xec, 0xd3, 0xc5, 0x80, 0xdd, 0xe3, 0xa3, 0xa3, 0x76, 0xbf, 0x94, 0x5a, 0x08, 0x90, - 0x37, 0xe4, 0x6d, 0x58, 0x9b, 0x0f, 0xe8, 0xb4, 0x0f, 0x4b, 0xe9, 0x8a, 0x7e, 0x7e, 0x51, 0x5b, - 0x99, 0x41, 0x77, 0x5c, 0xaf, 0x92, 0xfb, 0xf6, 0xf9, 0xe6, 0xd2, 0x4f, 0x3f, 0x6c, 0x2e, 0xdd, - 0xf9, 0x51, 0x81, 0xe2, 0xdc, 0x94, 0xe8, 0x1b, 0x70, 0xbd, 0xd7, 0x3e, 0xe8, 0xec, 0xef, 0x59, - 0x47, 0xbd, 0x03, 0xab, 0xff, 0x65, 0x77, 0x7f, 0xa6, 0x8a, 0x1b, 0x50, 0xe8, 0x9a, 0xfb, 0x8f, - 0x8f, 0xfb, 0xfb, 0xdc, 0x53, 0x52, 0x2a, 0xab, 0xe7, 0x17, 0x35, 0xad, 0x1b, 0x61, 0xf6, 0x9b, - 0x83, 0xc7, 0xdf, 0x84, 0x95, 0xae, 0xb9, 0x2f, 0x0e, 0x2b, 0x40, 0xa9, 0xca, 0xda, 0xf9, 0x45, - 0xad, 0xd8, 0x8d, 0xb0, 0x10, 0x02, 0x87, 0x6d, 0x41, 0xb1, 0x6b, 0x1e, 0x77, 0x8f, 0x7b, 0xcd, - 0x43, 0x81, 0x4a, 0x57, 0x4a, 0xe7, 0x17, 0xb5, 0x42, 0x3c, 0xe2, 0x0c, 0x34, 0x3d, 0x67, 0xcb, - 0xf8, 0xea, 0x9e, 0xe3, 0xd2, 0xd1, 0x64, 0x60, 0x0c, 0x83, 0x71, 0x63, 0xda, 0xbd, 0xd9, 0xcf, - 0x99, 0x3f, 0x39, 0x06, 0xcb, 0x7c, 0xf1, 0xd1, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x10, - 0x1c, 0xa4, 0x88, 0x0c, 0x00, 0x00, + // 1308 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcb, 0x6e, 0xdb, 0x46, + 0x17, 0x36, 0x25, 0xea, 0x76, 0x28, 0xd9, 0x32, 0xe3, 0x3f, 0xd1, 0x2f, 0xb7, 0x96, 0x22, 0x37, + 0xa9, 0x73, 0x01, 0xd5, 0xba, 0x40, 0xd1, 0x00, 0xdd, 0x48, 0xb6, 0xe3, 0x08, 0xb1, 0x25, 0x81, + 0x52, 0xd3, 0xcb, 0x86, 0x18, 0x89, 0x13, 0x8a, 0x08, 0x45, 0x12, 0xe4, 0xc8, 0xb0, 0x52, 0xa0, + 0xeb, 0xc2, 0xab, 0xbe, 0x80, 0x37, 0xbd, 0x00, 0x7d, 0x8b, 0x76, 0xd9, 0x55, 0x97, 0x5d, 0xa6, + 0x40, 0xfa, 0x0a, 0x7d, 0x80, 0x62, 0x2e, 0xa4, 0xa4, 0xc8, 0x6e, 0x8d, 0x26, 0xdd, 0x24, 0x9c, + 0x73, 0xbe, 0x73, 0xe6, 0x9c, 0x6f, 0xbe, 0x33, 0x23, 0xc3, 0x0d, 0x3f, 0xf0, 0x88, 0x57, 0x27, + 0x53, 0x1f, 0x87, 0xfc, 0x5f, 0x8d, 0x59, 0xd4, 0xeb, 0x04, 0xbb, 0x26, 0x0e, 0xc6, 0xb6, 0x4b, + 0xb8, 0x45, 0x63, 0xde, 0xf2, 0x6d, 0x32, 0xb2, 0x03, 0xd3, 0xf0, 0x51, 0x40, 0xa6, 0x75, 0x1e, + 0x6c, 0x79, 0x96, 0x37, 0xfb, 0xe2, 0xe8, 0x72, 0xc5, 0xf2, 0x3c, 0xcb, 0xc1, 0x1c, 0x32, 0x98, + 0x3c, 0xad, 0x13, 0x7b, 0x8c, 0x43, 0x82, 0xc6, 0xbe, 0x00, 0x6c, 0xf2, 0x10, 0xc7, 0x1e, 0x84, + 0xf5, 0x81, 0x4d, 0x16, 0x76, 0x2f, 0x57, 0xb8, 0x73, 0x18, 0x4c, 0x7d, 0xe2, 0xd5, 0xc7, 0x38, + 0x78, 0xe6, 0xe0, 0x05, 0x80, 0x88, 0x3e, 0xc1, 0x41, 0x68, 0x7b, 0x6e, 0xf4, 0x3f, 0x77, 0xd6, + 0x1e, 0x40, 0xa1, 0x8b, 0x02, 0xd2, 0xc3, 0xe4, 0x11, 0x46, 0x26, 0x0e, 0xd4, 0x0d, 0x48, 0x11, + 0x8f, 0x20, 0xa7, 0x24, 0x55, 0xa5, 0x9d, 0xa4, 0xce, 0x17, 0xaa, 0x0a, 0xf2, 0x08, 0x85, 0xa3, + 0x52, 0xa2, 0x2a, 0xed, 0xe4, 0x75, 0xf6, 0x5d, 0xfb, 0x12, 0x64, 0x1a, 0x4a, 0x23, 0x6c, 0xd7, + 0xc4, 0xa7, 0x2c, 0xa2, 0xa0, 0xf3, 0x05, 0xb5, 0x0e, 0xa6, 0x04, 0x87, 0x22, 0x84, 0x2f, 0xd4, + 0x43, 0x48, 0xf9, 0x81, 0xe7, 0x3d, 0x2d, 0x25, 0xab, 0xd2, 0x8e, 0xb2, 0x7b, 0x4f, 0x5b, 0xa2, + 0x8e, 0xf7, 0xa1, 0xf1, 0x3e, 0xb4, 0x9e, 0x3d, 0xf6, 0x1d, 0xdc, 0xa5, 0x21, 0x4d, 0xf9, 0x97, + 0x17, 0x95, 0x15, 0x9d, 0xc7, 0xd7, 0xc6, 0x90, 0x69, 0x3a, 0xde, 0xf0, 0x59, 0x6b, 0x3f, 0xae, + 0x4d, 0x9a, 0xd5, 0xa6, 0xb6, 0x21, 0x4f, 0x69, 0x0f, 0x8d, 0x11, 0xeb, 0x8a, 0x15, 0xa1, 0xec, + 0xde, 0xd2, 0x2e, 0x3e, 0x29, 0x6d, 0x81, 0x02, 0xb1, 0x91, 0xc2, 0x12, 0x70, 0x53, 0xed, 0xdb, + 0x14, 0xa4, 0x05, 0x41, 0x7b, 0x90, 0x11, 0x14, 0xb2, 0x1d, 0x95, 0xdd, 0xed, 0xe5, 0xac, 0x11, + 0xc7, 0x7b, 0x9e, 0x1b, 0x62, 0x37, 0x9c, 0x84, 0x22, 0x67, 0x14, 0xa9, 0xde, 0x86, 0xec, 0x70, + 0x84, 0x6c, 0xd7, 0xb0, 0x4d, 0x56, 0x5b, 0xae, 0xa9, 0xbc, 0x7c, 0x51, 0xc9, 0xec, 0x51, 0x5b, + 0x6b, 0x5f, 0xcf, 0x30, 0x67, 0xcb, 0x54, 0xaf, 0x43, 0x7a, 0x84, 0x6d, 0x6b, 0x44, 0x18, 0x61, + 0x49, 0x5d, 0xac, 0xd4, 0x8f, 0x40, 0xa6, 0x22, 0x29, 0xc9, 0xac, 0x82, 0xb2, 0xc6, 0x15, 0xa4, + 0x45, 0x0a, 0xd2, 0xfa, 0x91, 0x82, 0x9a, 0x59, 0xba, 0xf1, 0x37, 0xbf, 0x57, 0x24, 0x9d, 0x45, + 0xa8, 0x9f, 0x41, 0xc1, 0x41, 0x21, 0x31, 0x06, 0x94, 0x3d, 0xba, 0x7d, 0x8a, 0xa5, 0xa8, 0x5c, + 0x46, 0x8d, 0x60, 0xb9, 0x79, 0x8d, 0xe6, 0x79, 0xf9, 0xa2, 0xa2, 0x1c, 0xa1, 0x90, 0x08, 0xa3, + 0xae, 0x38, 0xf1, 0xc2, 0x54, 0x77, 0xa0, 0xc8, 0x32, 0x0f, 0xbd, 0xf1, 0xd8, 0x26, 0x06, 0x3b, + 0x93, 0x34, 0x3b, 0x93, 0x55, 0x6a, 0xdf, 0x63, 0xe6, 0x47, 0xf4, 0x74, 0x36, 0x21, 0x67, 0x22, + 0x82, 0x38, 0x24, 0xc3, 0x20, 0x59, 0x6a, 0x60, 0xce, 0x0a, 0x28, 0x27, 0x1e, 0xc1, 0x41, 0xc8, + 0xdd, 0x59, 0xe6, 0x06, 0x6e, 0x62, 0x80, 0x77, 0x61, 0xed, 0x04, 0x39, 0xb6, 0x89, 0x88, 0x17, + 0x81, 0x72, 0x7c, 0x9b, 0x99, 0x99, 0x01, 0xdf, 0x83, 0x0d, 0x17, 0x9f, 0x12, 0xe3, 0x55, 0x34, + 0x30, 0xb4, 0x4a, 0x7d, 0x4f, 0x16, 0x23, 0x6e, 0xc1, 0xea, 0x30, 0x3a, 0x32, 0x8e, 0x55, 0x18, + 0xb6, 0x10, 0x5b, 0x19, 0xec, 0xff, 0x90, 0x45, 0xbe, 0xcf, 0x01, 0x79, 0x06, 0xc8, 0x20, 0xdf, + 0x67, 0xae, 0xbb, 0xb0, 0xce, 0x48, 0x08, 0x70, 0x38, 0x71, 0x88, 0x48, 0x52, 0x60, 0x98, 0x35, + 0xea, 0xd0, 0xb9, 0x9d, 0x61, 0xb7, 0xa1, 0x80, 0x4f, 0x6c, 0x13, 0xbb, 0x43, 0xcc, 0x71, 0xab, + 0x0c, 0x97, 0x8f, 0x8c, 0x0c, 0x74, 0x07, 0x8a, 0x7e, 0xe0, 0xf9, 0x5e, 0x88, 0x03, 0x03, 0x99, + 0x66, 0x80, 0xc3, 0xb0, 0xb4, 0xc6, 0xf3, 0x45, 0xf6, 0x06, 0x37, 0xd7, 0xee, 0x83, 0xbc, 0x8f, + 0x08, 0x52, 0x8b, 0x90, 0x24, 0xa7, 0x61, 0x49, 0xaa, 0x26, 0x77, 0xf2, 0x3a, 0xfd, 0xbc, 0x70, + 0x7c, 0xff, 0x4c, 0x80, 0xfc, 0xc4, 0x23, 0x58, 0x7d, 0x00, 0x32, 0x3d, 0x6a, 0xa6, 0xe6, 0xd5, + 0xcb, 0x67, 0xa4, 0x67, 0x5b, 0x2e, 0x36, 0x8f, 0x43, 0xab, 0x3f, 0xf5, 0xb1, 0xce, 0x42, 0xe6, + 0xe4, 0x99, 0x58, 0x90, 0xe7, 0x06, 0xa4, 0x02, 0x6f, 0xe2, 0x9a, 0x42, 0xb5, 0x7c, 0xa1, 0x3e, + 0x86, 0x6c, 0xac, 0x3a, 0xf9, 0x6a, 0xaa, 0x5b, 0x13, 0xaa, 0x8b, 0x86, 0x5d, 0xcf, 0x0c, 0x84, + 0xda, 0x9a, 0x90, 0x8b, 0xaf, 0x49, 0xa1, 0xe1, 0xab, 0x8d, 0xc1, 0x2c, 0x4c, 0xbd, 0x07, 0xeb, + 0xb1, 0x36, 0x62, 0x72, 0xb9, 0x64, 0x8b, 0xb1, 0x43, 0xb0, 0xbb, 0x20, 0x3b, 0x83, 0x5f, 0x78, + 0x19, 0xd6, 0xdd, 0x4c, 0x76, 0x2d, 0x76, 0xf3, 0xbd, 0x05, 0xb9, 0xd0, 0xb6, 0x5c, 0x44, 0x26, + 0x01, 0x16, 0xf2, 0x9d, 0x19, 0x6a, 0xbf, 0x25, 0x20, 0xcd, 0x47, 0x61, 0x8e, 0x3d, 0xe9, 0x62, + 0xf6, 0x28, 0xa9, 0xa9, 0x8b, 0xd8, 0x4b, 0xbe, 0x2e, 0x7b, 0x87, 0x00, 0x71, 0x49, 0x61, 0x49, + 0xae, 0x26, 0x77, 0x94, 0xdd, 0x9b, 0x97, 0xa5, 0xe3, 0xe5, 0xf6, 0x6c, 0x4b, 0xdc, 0x62, 0x73, + 0xa1, 0xea, 0xfb, 0xb0, 0x81, 0x2c, 0x2b, 0xc0, 0x16, 0x22, 0xd8, 0x34, 0x66, 0x7d, 0xa7, 0x58, + 0xdf, 0xd7, 0x66, 0xbe, 0x5e, 0xe4, 0x8a, 0xc5, 0x98, 0x9e, 0xbb, 0xaf, 0x1b, 0x90, 0x1b, 0xd8, + 0xc4, 0x40, 0x41, 0x80, 0xa6, 0x8c, 0x56, 0x65, 0xf7, 0x9d, 0xe5, 0x72, 0xe8, 0x03, 0xa8, 0xd1, + 0x07, 0x50, 0x6b, 0xda, 0xa4, 0x41, 0xb1, 0x7a, 0x76, 0x20, 0xbe, 0x6a, 0x7f, 0x48, 0x90, 0x8b, + 0x2b, 0x55, 0x0f, 0xa1, 0x10, 0xb1, 0x65, 0x3c, 0x75, 0x90, 0x25, 0xd4, 0xbd, 0xfd, 0x0f, 0x94, + 0x3d, 0x74, 0x90, 0xa5, 0x2b, 0x82, 0x25, 0xba, 0xb8, 0x58, 0x23, 0x89, 0x4b, 0x34, 0xb2, 0x20, + 0xca, 0xe4, 0xbf, 0x13, 0xe5, 0x82, 0x7c, 0xe4, 0x57, 0xe5, 0xf3, 0x53, 0x02, 0xb2, 0x5d, 0x36, + 0xf7, 0xc8, 0xf9, 0xcf, 0x27, 0x37, 0xd6, 0xde, 0x26, 0xe4, 0x7c, 0xcf, 0x31, 0xb8, 0x47, 0x66, + 0x9e, 0xac, 0xef, 0x39, 0xfa, 0x92, 0x30, 0x53, 0x6f, 0x74, 0xac, 0xd3, 0x6f, 0x80, 0xc1, 0xcc, + 0xab, 0x0c, 0x7e, 0x05, 0x79, 0x4e, 0x88, 0x78, 0xcf, 0x3f, 0xa4, 0x4c, 0xb0, 0x1f, 0x09, 0xfc, + 0x39, 0xdf, 0xba, 0xac, 0x78, 0x8e, 0xd7, 0x05, 0x9a, 0xc6, 0xf1, 0x97, 0x4e, 0xfc, 0xb8, 0xd8, + 0xfa, 0xfb, 0xf1, 0xd1, 0x05, 0xba, 0xf6, 0xab, 0x04, 0x39, 0xd6, 0xf6, 0x31, 0x26, 0x68, 0x81, + 0x3c, 0xe9, 0x75, 0xc9, 0x7b, 0x1b, 0x80, 0x27, 0x0b, 0xed, 0xe7, 0x58, 0x1c, 0x6c, 0x8e, 0x59, + 0x7a, 0xf6, 0x73, 0xac, 0x7e, 0x1c, 0x77, 0x9a, 0xbc, 0x4a, 0xa7, 0x62, 0xda, 0xa3, 0x7e, 0x6f, + 0x40, 0xc6, 0x9d, 0x8c, 0x0d, 0xfa, 0xb2, 0xc8, 0x5c, 0x32, 0xee, 0x64, 0xdc, 0x3f, 0x0d, 0xef, + 0xfe, 0x2c, 0x81, 0x32, 0x37, 0x3e, 0x6a, 0x19, 0xae, 0x37, 0x8f, 0x3a, 0x7b, 0x8f, 0xf7, 0x8d, + 0xd6, 0xbe, 0xf1, 0xf0, 0xa8, 0x71, 0x68, 0x7c, 0xd2, 0x7e, 0xdc, 0xee, 0x7c, 0xda, 0x2e, 0xae, + 0xa8, 0x75, 0xd8, 0x60, 0xbe, 0xd8, 0xd5, 0x68, 0xf6, 0x0e, 0xda, 0xfd, 0xa2, 0x54, 0xfe, 0xdf, + 0xd9, 0x79, 0x75, 0x7d, 0x2e, 0x4d, 0x63, 0x10, 0x62, 0x97, 0x2c, 0x07, 0xec, 0x75, 0x8e, 0x8f, + 0x5b, 0xfd, 0x62, 0x62, 0x29, 0x40, 0x5c, 0xaa, 0x77, 0x60, 0x7d, 0x31, 0xa0, 0xdd, 0x3a, 0x2a, + 0x26, 0xcb, 0xea, 0xd9, 0x79, 0x75, 0x75, 0x0e, 0xdd, 0xb6, 0x9d, 0x72, 0xf6, 0xeb, 0xef, 0xb6, + 0x56, 0x7e, 0xfc, 0x7e, 0x6b, 0xe5, 0xee, 0x0f, 0x12, 0x14, 0x16, 0xa6, 0x44, 0xdd, 0x84, 0x1b, + 0xbd, 0xd6, 0x61, 0xfb, 0x60, 0xdf, 0x38, 0xee, 0x1d, 0x1a, 0xfd, 0xcf, 0xbb, 0x07, 0x73, 0x5d, + 0xdc, 0x84, 0x7c, 0x57, 0x3f, 0x78, 0xd2, 0xe9, 0x1f, 0x30, 0x4f, 0x51, 0x2a, 0xaf, 0x9d, 0x9d, + 0x57, 0x95, 0x6e, 0x80, 0xe9, 0xcf, 0x14, 0x16, 0x7f, 0x0b, 0x56, 0xbb, 0xfa, 0x01, 0x2f, 0x96, + 0x83, 0x12, 0xe5, 0xf5, 0xb3, 0xf3, 0x6a, 0xa1, 0x1b, 0x60, 0x2e, 0x04, 0x06, 0xdb, 0x86, 0x42, + 0x57, 0xef, 0x74, 0x3b, 0xbd, 0xc6, 0x11, 0x47, 0x25, 0xcb, 0xc5, 0xb3, 0xf3, 0x6a, 0x3e, 0x1a, + 0x71, 0x0a, 0x9a, 0xd5, 0xd9, 0xd4, 0xbe, 0xb8, 0x6f, 0xd9, 0x64, 0x34, 0x19, 0x68, 0x43, 0x6f, + 0x5c, 0x9f, 0x9d, 0xde, 0xfc, 0xe7, 0xdc, 0x5f, 0x29, 0x83, 0x34, 0x5b, 0x7c, 0xf0, 0x57, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x16, 0x51, 0x97, 0x28, 0xbb, 0x0c, 0x00, 0x00, } diff --git a/proto/types/types.proto b/proto/types/types.proto index e4578e5c6..6fe888c5c 100644 --- a/proto/types/types.proto +++ b/proto/types/types.proto @@ -108,8 +108,9 @@ message Commit { int32 round = 2; BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; - bytes hash = 5; - tendermint.proto.libs.bits.BitArray bit_array = 6; + bytes aggregated_signature = 5; + bytes hash = 6; + tendermint.proto.libs.bits.BitArray bit_array = 7; } // CommitSig is a part of the Vote included in a Commit. diff --git a/types/block.go b/types/block.go index 96274dd10..67ae93c80 100644 --- a/types/block.go +++ b/types/block.go @@ -7,6 +7,9 @@ import ( "sync" "time" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/pkg/errors" "github.com/tendermint/tendermint/crypto" @@ -636,10 +639,8 @@ func (cs CommitSig) ValidateBasic() error { ) } // NOTE: Timestamp validation is subtle and handled elsewhere. - if len(cs.Signature) == 0 { - return errors.New("signature is missing") - } - if len(cs.Signature) > MaxSignatureSize { + // NOTE: Signature may be nil if it is aggregated and is handled elsewhere. + if cs.Signature != nil && len(cs.Signature) > MaxSignatureSize { return fmt.Errorf("signature is too big %d (max: %d)", len(cs.Signature), MaxSignatureSize) } } @@ -698,11 +699,38 @@ type Commit struct { // NewCommit returns a new Commit. func NewCommit(height int64, round int, blockID BlockID, commitSigs []CommitSig) *Commit { + return NewCommitWithAggregatedSignature(height, round, blockID, commitSigs, nil) +} + +// NewCommitWithAggregatedSignature returns a new Commit with . +// The aggrSig can be set to null. Then all commitSigs must have a valid signature. +func NewCommitWithAggregatedSignature(height int64, round int, blockID BlockID, commitSigs []CommitSig, aggrSig []byte) *Commit { + + // Make a copy of CommitSig to avoid side effects + newCommitSigs := make([]CommitSig, len(commitSigs)) + copy(newCommitSigs, commitSigs) + commitSigs = newCommitSigs + + // Create aggregate signature and reset per-CommitSig signature if possible + for i := range commitSigs { + if commitSigs[i].Absent() { + continue + } + curAggrSig, err := bls.AddSignature(aggrSig, commitSigs[i].Signature) + if err == nil { + aggrSig = curAggrSig + commitSigs[i].Signature = nil + } else { + // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. + } + } + return &Commit{ - Height: height, - Round: round, - BlockID: blockID, - Signatures: commitSigs, + Height: height, + Round: round, + BlockID: blockID, + Signatures: commitSigs, + AggregatedSignature: aggrSig, } } @@ -819,10 +847,26 @@ func (commit *Commit) ValidateBasic() error { if len(commit.Signatures) == 0 { return errors.New("no signatures in commit") } + omittedSignatures := 0 for i, commitSig := range commit.Signatures { if err := commitSig.ValidateBasic(); err != nil { return fmt.Errorf("wrong CommitSig #%d: %v", i, err) } + if !commitSig.Absent() && commitSig.Signature == nil { + omittedSignatures += 1 + } + } + if commit.AggregatedSignature == nil { + if omittedSignatures > 0 { + return fmt.Errorf("%d erased signatures are present, but no aggregate signature exist in commit", + omittedSignatures) + } + } else if omittedSignatures == 0 { + return fmt.Errorf("erased signatures are not present, but aggregated signature exist in commit: %x", + commit.AggregatedSignature) + } else if len(commit.AggregatedSignature) > MaxSignatureSize { + return fmt.Errorf("signature is too big %d (max: %d)", + len(commit.AggregatedSignature), MaxSignatureSize) } } @@ -884,6 +928,7 @@ func (commit *Commit) ToProto() *tmproto.Commit { c.Height = commit.Height c.Round = int32(commit.Round) c.BlockID = commit.BlockID.ToProto() + c.AggregatedSignature = commit.AggregatedSignature if commit.hash != nil { c.Hash = commit.hash } @@ -921,6 +966,7 @@ func CommitFromProto(cp *tmproto.Commit) (*Commit, error) { commit.Height = cp.Height commit.Round = int(cp.Round) commit.BlockID = *bi + commit.AggregatedSignature = cp.AggregatedSignature commit.hash = cp.Hash commit.bitArray = bitArray @@ -1196,3 +1242,19 @@ func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) { return blockID, blockID.ValidateBasic() } + +// GetBLSPubKey is a utility function for referencing a specified public key as a BLS key for signature. +// If the key is not BLS, return nil +func GetSignatureKey(pubKey crypto.PubKey) *bls.PubKeyBLS12 { + for { + if compPubKey, ok := pubKey.(composite.PubKeyComposite); ok { + pubKey = compPubKey.SignKey + } else { + break + } + } + if blsPubKey, ok := pubKey.(bls.PubKeyBLS12); ok { + return &blsPubKey + } + return nil +} diff --git a/types/block_test.go b/types/block_test.go index 58d409bf7..34c0d10ae 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -5,12 +5,17 @@ import ( // number generator here and we can run the tests a bit faster "crypto/rand" "encoding/hex" + "fmt" "math" "os" "reflect" "testing" "time" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -211,6 +216,66 @@ func TestNilDataHashDoesntCrash(t *testing.T) { assert.Equal(t, []byte(new(Data).Hash()), nilBytes) } +func TestNewCommit(t *testing.T) { + blockID := BlockID{ + Hash: []byte{}, + PartsHeader: PartSetHeader{ + Total: 0, + Hash: []byte{}, + }, + } + privKeys := [...]crypto.PrivKey{ + bls.GenPrivKey(), + composite.GenPrivKey(), + ed25519.GenPrivKey(), + bls.GenPrivKey(), + } + msgs := make([][]byte, len(privKeys)) + signs := make([][]byte, len(privKeys)) + pubKeys := make([]crypto.PubKey, len(privKeys)) + commitSigs := make([]CommitSig, len(privKeys)) + for i := 0; i < len(privKeys); i++ { + msgs[i] = []byte(fmt.Sprintf("hello, world %d", i)) + signs[i], _ = privKeys[i].Sign(msgs[i]) + pubKeys[i] = privKeys[i].PubKey() + commitSigs[i] = NewCommitSigForBlock(signs[i], pubKeys[i].Address(), time.Now()) + assert.Equal(t, signs[i], commitSigs[i].Signature) + } + commit := NewCommit(0, 1, blockID, commitSigs) + + assert.Equal(t, int64(0), commit.Height) + assert.Equal(t, 1, commit.Round) + assert.Equal(t, blockID, commit.BlockID) + assert.Equal(t, len(commitSigs), len(commit.Signatures)) + assert.NotNil(t, commit.AggregatedSignature) + assert.Nil(t, commit.Signatures[0].Signature) + assert.Nil(t, commit.Signatures[1].Signature) + assert.NotNil(t, commit.Signatures[2].Signature) + assert.Nil(t, commit.Signatures[3].Signature) + assert.True(t, pubKeys[2].VerifyBytes(msgs[2], commit.Signatures[2].Signature)) + + blsPubKeys := []bls.PubKeyBLS12{ + *GetSignatureKey(pubKeys[0]), + *GetSignatureKey(pubKeys[1]), + *GetSignatureKey(pubKeys[3]), + } + blsSigMsgs := [][]byte{msgs[0], msgs[1], msgs[3]} + func() { + aggrSig, err := bls.AddSignature(nil, signs[0]) + assert.Nil(t, err) + aggrSig, err = bls.AddSignature(aggrSig, signs[1]) + assert.Nil(t, err) + aggrSig, err = bls.AddSignature(aggrSig, signs[3]) + assert.Nil(t, err) + err = bls.VerifyAggregatedSignature(aggrSig, blsPubKeys, blsSigMsgs) + assert.Nil(t, err) + assert.Equal(t, aggrSig, commit.AggregatedSignature) + }() + + err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, blsSigMsgs) + assert.Nilf(t, err, "%s", err) +} + func TestCommit(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) diff --git a/types/vote.go b/types/vote.go index 0cd9b4151..0568396db 100644 --- a/types/vote.go +++ b/types/vote.go @@ -127,7 +127,7 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { return ErrVoteInvalidValidatorAddress } - if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { + if vote.Signature != nil && !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) { return ErrVoteInvalidSignature } return nil diff --git a/types/vote_set.go b/types/vote_set.go index 5bcb885f5..2e08976a7 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -6,6 +6,8 @@ import ( "strings" "sync" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/pkg/errors" "github.com/tendermint/tendermint/libs/bits" @@ -64,6 +66,8 @@ type VoteSet struct { maj23 *BlockID // First 2/3 majority seen votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes peerMaj23s map[P2PID]BlockID // Maj23 for each peer + + aggregatedSignature []byte } // Constructs a new VoteSet struct used to accumulate votes for given height/round. @@ -83,6 +87,8 @@ func NewVoteSet(chainID string, height int64, round int, signedMsgType SignedMsg maj23: nil, votesByBlock: make(map[string]*blockVotes, voterSet.Size()), peerMaj23s: make(map[P2PID]BlockID), + + aggregatedSignature: nil, } } @@ -212,6 +218,16 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { if !added { panic("Expected to add non-conflicting vote") } + + // Aggregate signature in added vote if possible. + aggrSign, err := bls.AddSignature(voteSet.aggregatedSignature, vote.Signature) + if err == nil { + voteSet.aggregatedSignature = aggrSign + vote.Signature = nil + } else { + // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. + } + return added, nil } @@ -577,7 +593,8 @@ func (voteSet *VoteSet) MakeCommit() *Commit { commitSigs[i] = commitSig } - return NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs) + return NewCommitWithAggregatedSignature( + voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs, voteSet.aggregatedSignature) } //-------------------------------------------------------------------------------- diff --git a/types/voter_set.go b/types/voter_set.go index f0fe5e710..4834ec36a 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -8,6 +8,8 @@ import ( "sort" "strings" + "github.com/tendermint/tendermint/crypto/bls" + tmproto "github.com/tendermint/tendermint/proto/types" "github.com/datastream/probab/dst" @@ -147,6 +149,8 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, talliedVotingPower := int64(0) votingPowerNeeded := voters.TotalVotingPower() * 2 / 3 + blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) + messages := make([][]byte, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. @@ -158,8 +162,17 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // Validate signature. voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + if commitSig.Signature != nil { + if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + } else { + blsPubKey := GetSignatureKey(val.PubKey) + if blsPubKey == nil { + return fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx) + } + blsPubKeys = append(blsPubKeys, *blsPubKey) + messages = append(messages, voteSignBytes) } // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { @@ -171,6 +184,10 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // } } + if err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, messages); err != nil { + return fmt.Errorf("wrong aggregated signature: %X; %s", commit.AggregatedSignature, err) + } + if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} } From 08dc7db504486ea2af3b1d032f180638922223d1 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Fri, 4 Sep 2020 16:43:32 +0900 Subject: [PATCH 09/36] apply BLS signature validation for various commit verifications --- types/block.go | 32 ++++++++++++++++ types/validator_set_test.go | 14 ++++++- types/vote_test.go | 1 + types/voter_set.go | 75 +++++++++++++++---------------------- 4 files changed, 76 insertions(+), 46 deletions(-) diff --git a/types/block.go b/types/block.go index 67ae93c80..a7b64d759 100644 --- a/types/block.go +++ b/types/block.go @@ -936,6 +936,38 @@ func (commit *Commit) ToProto() *tmproto.Commit { return c } +func (commit *Commit) VerifySignatures(chainID string, vals []*Validator) error { + blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) + messages := make([][]byte, 0, len(commit.Signatures)) + for idx, commitSig := range commit.Signatures { + if commitSig.Absent() { + continue // OK, some signatures can be absent. + } + + // Validate signature. + if val := vals[idx]; val != nil { + voteSignBytes := commit.VoteSignBytes(chainID, idx) + if commitSig.Signature != nil { + if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + } else { + blsPubKey := GetSignatureKey(val.PubKey) + if blsPubKey == nil { + return fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx) + } + blsPubKeys = append(blsPubKeys, *blsPubKey) + messages = append(messages, voteSignBytes) + } + } + } + + if err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, messages); err != nil { + return fmt.Errorf("wrong aggregated signature: %X; %s", commit.AggregatedSignature, err) + } + return nil +} + // FromProto sets a protobuf Commit to the given pointer. // It returns an error if the commit is invalid. func CommitFromProto(cp *tmproto.Commit) (*Commit, error) { diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 3190d065f..2dc82cae7 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -717,8 +717,13 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign require.NoError(t, err) commit.Signatures[3] = vote.CommitSig() + // NOTE: Before BLS signature aggregation, in case that three of the four signatures are valid, the Commit itself + // is considered valid even if one of the remaining signatures has been tampered with or replaced, but after + // BLS signature aggregation, the verification fails unless all four signatures are valid, since all four + // signatures are verified at once. err = vset.VerifyCommitLight(chainID, blockID, h, commit) - assert.NoError(t, err) + assert.Error(t, err) + assert.Contains(t, err.Error(), "wrong signature (#3)") } func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) { @@ -738,8 +743,13 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin require.NoError(t, err) commit.Signatures[2] = vote.CommitSig() + // NOTE: Before BLS signature aggregation, in case that three of the four signatures are valid, the Commit itself + // is considered valid even if one of the remaining signatures has been tampered with or replaced, but after + // BLS signature aggregation, the verification fails unless all four signatures are valid, since all four + // signatures are verified at once. err = vset.VerifyCommitLightTrusting(chainID, blockID, h, commit, tmmath.Fraction{Numerator: 1, Denominator: 3}) - assert.NoError(t, err) + assert.Error(t, err) + assert.Contains(t, err.Error(), "wrong signature (#2)") } func TestEmptySet(t *testing.T) { diff --git a/types/vote_test.go b/types/vote_test.go index c3bd7a16f..ee1645ef3 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -199,6 +199,7 @@ func TestVoteVerify(t *testing.T) { vote := examplePrevote() vote.ValidatorAddress = pubkey.Address() + vote.Signature = []byte{} err = vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey()) if assert.Error(t, err) { diff --git a/types/voter_set.go b/types/voter_set.go index 4834ec36a..fa51d6505 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -8,8 +8,6 @@ import ( "sort" "strings" - "github.com/tendermint/tendermint/crypto/bls" - tmproto "github.com/tendermint/tendermint/proto/types" "github.com/datastream/probab/dst" @@ -149,8 +147,6 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, talliedVotingPower := int64(0) votingPowerNeeded := voters.TotalVotingPower() * 2 / 3 - blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) - messages := make([][]byte, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. @@ -160,20 +156,6 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // This means we don't need the validator address or to do any lookup. val := voters.Voters[idx] - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if commitSig.Signature != nil { - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - } else { - blsPubKey := GetSignatureKey(val.PubKey) - if blsPubKey == nil { - return fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx) - } - blsPubKeys = append(blsPubKeys, *blsPubKey) - messages = append(messages, voteSignBytes) - } // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { talliedVotingPower += val.VotingPower @@ -184,8 +166,9 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, // } } - if err := bls.VerifyAggregatedSignature(commit.AggregatedSignature, blsPubKeys, messages); err != nil { - return fmt.Errorf("wrong aggregated signature: %X; %s", commit.AggregatedSignature, err) + // Validate signature. + if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { + return err } if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { @@ -231,16 +214,14 @@ func (voters *VoterSet) VerifyCommitLight(chainID string, blockID BlockID, // This means we don't need the validator address or to do any lookup. val := voters.Voters[idx] - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - talliedVotingPower += val.VotingPower // return as soon as +2/3 of the signatures are verified if talliedVotingPower > votingPowerNeeded { + // Validate signature. + if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { + return err + } return nil } } @@ -291,23 +272,20 @@ func (voters *VoterSet) VerifyFutureCommit(newSet *VoterSet, chainID string, oldVotingPower := int64(0) seen := map[int]bool{} - for idx, commitSig := range commit.Signatures { + vals := make([]*Validator, 0, len(commit.Signatures)) + for _, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. } // See if this validator is in oldVals. oldIdx, val := oldVoters.GetByAddress(commitSig.ValidatorAddress) + vals = append(vals, val) if val == nil || seen[oldIdx] { continue // missing or double vote... } seen[oldIdx] = true - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return errors.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { oldVotingPower += val.VotingPower @@ -318,6 +296,11 @@ func (voters *VoterSet) VerifyFutureCommit(newSet *VoterSet, chainID string, // } } + // Validate signature. + if err := commit.VerifySignatures(chainID, vals); err != nil { + return err + } + if got, needed := oldVotingPower, oldVoters.TotalVotingPower()*2/3; got <= needed { return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} } @@ -351,6 +334,7 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, } votingPowerNeeded := totalVotingPowerMulByNumerator / trustLevel.Denominator + vals := make([]*Validator, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. @@ -359,6 +343,7 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, // We don't know the validators that committed this block, so we have to // check for each vote if its validator is already known. valIdx, val := voters.GetByAddress(commitSig.ValidatorAddress) + vals = append(vals, val) if firstIndex, ok := seenVals[valIdx]; ok { // double vote secondIndex := idx @@ -368,12 +353,6 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, if val != nil { seenVals[valIdx] = idx - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return errors.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - // Good! if blockID.Equals(commitSig.BlockID(commit.BlockID)) { talliedVotingPower += val.VotingPower @@ -389,6 +368,11 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, } } + // Validate signature. + if err := commit.VerifySignatures(chainID, vals); err != nil { + return err + } + return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} } @@ -445,15 +429,18 @@ func (voters *VoterSet) VerifyCommitLightTrusting(chainID string, blockID BlockI } seenVals[valIdx] = idx - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, idx) - if !val.PubKey.VerifyBytes(voteSignBytes, commitSig.Signature) { - return errors.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - talliedVotingPower += val.VotingPower if talliedVotingPower > votingPowerNeeded { + // Validate signature. + vals := make([]*Validator, 0, len(commit.Signatures)) + for _, cs := range commit.Signatures { + _, val := voters.GetByAddress(cs.ValidatorAddress) + vals = append(vals, val) + } + if err := commit.VerifySignatures(chainID, vals); err != nil { + return err + } return nil } } From da2c07eac0033599505a401409a78174512032df Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Fri, 4 Sep 2020 17:01:52 +0900 Subject: [PATCH 10/36] apply github-action-lint --- crypto/bls/bls.go | 5 ++--- crypto/bls/bls_test.go | 18 +++++++++++++++--- types/block.go | 5 +++-- types/vote_set.go | 5 +++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/crypto/bls/bls.go b/crypto/bls/bls.go index acb99bcd0..c3a788d0f 100644 --- a/crypto/bls/bls.go +++ b/crypto/bls/bls.go @@ -86,10 +86,9 @@ func VerifyAggregatedSignature(aggregatedSignature []byte, pubKeys []PubKeyBLS12 if aggregatedSignature == nil { if len(pubKeys) == 0 { return nil - } else { - return fmt.Errorf( - "the aggregate signature was omitted, even though %d public keys were specified", len(pubKeys)) } + return fmt.Errorf( + "the aggregate signature was omitted, even though %d public keys were specified", len(pubKeys)) } aggrSign := bls.Sign{} err := aggrSign.Deserialize(aggregatedSignature) diff --git a/crypto/bls/bls_test.go b/crypto/bls/bls_test.go index 2070f32e9..4058d2521 100644 --- a/crypto/bls/bls_test.go +++ b/crypto/bls/bls_test.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "fmt" "math/rand" + "reflect" "testing" "time" @@ -343,10 +344,17 @@ func generatePubKeysAndSigns(t *testing.T, size int) ([]bls.PubKeyBLS12, [][]byt func blsPublicKey(t *testing.T, pubKey crypto.PubKey) bls.PubKeyBLS12 { blsPubKey, ok := pubKey.(bls.PubKeyBLS12) if !ok { - t.Fatal(fmt.Sprintf("specified public key is not for BLS")) + var keyType string + if t := reflect.TypeOf(pubKey); t.Kind() == reflect.Ptr { + keyType = "*" + t.Elem().Name() + } else { + keyType = t.Name() + } + t.Fatal(fmt.Sprintf("specified public key is not for BLS: %s", keyType)) } return blsPubKey } + func aggregateSignatures(init []byte, signatures [][]byte) (aggrSig []byte, err error) { aggrSig = init for _, sign := range signatures { @@ -398,7 +406,9 @@ func TestAggregatedSignature(t *testing.T) { shuffledPubKeys := make([]bls.PubKeyBLS12, len(pubKeys)) copy(shuffledPubKeys, pubKeys) rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(shuffledPubKeys), func(i, j int) { shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] }) + rand.Shuffle(len(shuffledPubKeys), func(i, j int) { + shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i] + }) if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, msgs); err == nil { t.Error("successfully validated with public keys of different order") } @@ -409,7 +419,9 @@ func TestAggregatedSignature(t *testing.T) { shuffledMsgs := make([][]byte, len(msgs)) copy(shuffledMsgs, msgs) rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(shuffledMsgs), func(i, j int) { shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] }) + rand.Shuffle(len(shuffledMsgs), func(i, j int) { + shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i] + }) if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, shuffledMsgs); err == nil { t.Error("successfully validated with messages of different order") } diff --git a/types/block.go b/types/block.go index a7b64d759..3c3e95656 100644 --- a/types/block.go +++ b/types/block.go @@ -704,7 +704,8 @@ func NewCommit(height int64, round int, blockID BlockID, commitSigs []CommitSig) // NewCommitWithAggregatedSignature returns a new Commit with . // The aggrSig can be set to null. Then all commitSigs must have a valid signature. -func NewCommitWithAggregatedSignature(height int64, round int, blockID BlockID, commitSigs []CommitSig, aggrSig []byte) *Commit { +func NewCommitWithAggregatedSignature( + height int64, round int, blockID BlockID, commitSigs []CommitSig, aggrSig []byte) *Commit { // Make a copy of CommitSig to avoid side effects newCommitSigs := make([]CommitSig, len(commitSigs)) @@ -853,7 +854,7 @@ func (commit *Commit) ValidateBasic() error { return fmt.Errorf("wrong CommitSig #%d: %v", i, err) } if !commitSig.Absent() && commitSig.Signature == nil { - omittedSignatures += 1 + omittedSignatures++ } } if commit.AggregatedSignature == nil { diff --git a/types/vote_set.go b/types/vote_set.go index 2e08976a7..08157c219 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -224,9 +224,10 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { if err == nil { voteSet.aggregatedSignature = aggrSign vote.Signature = nil - } else { - // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. } + // else { + // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. + // } return added, nil } From e8d7c822170e6e3ab66d827a5565db42ebf17e43 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Fri, 4 Sep 2020 18:03:16 +0900 Subject: [PATCH 11/36] set default value of Vote.Signature to zero-length bytes --- blockchain/v1/reactor_test.go | 1 + consensus/common_test.go | 1 + consensus/invalid_test.go | 1 + consensus/state.go | 1 + consensus/types/height_vote_set_test.go | 1 + lite/helpers.go | 1 + lite2/helpers_test.go | 1 + privval/file_test.go | 1 + privval/signer_client_test.go | 16 ++++++++-------- rpc/client/rpc_test.go | 10 ++++++++-- state/validation_test.go | 1 + .../tm-signer-harness/internal/test_harness.go | 1 + types/block_test.go | 1 + types/evidence_test.go | 1 + types/test_util.go | 2 ++ types/vote.go | 4 ++-- types/vote_set_test.go | 6 ++++++ types/vote_test.go | 18 ++++++++++-------- 18 files changed, 48 insertions(+), 20 deletions(-) diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index a402dfb2d..7ea194614 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -67,6 +67,7 @@ func makeVote( Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } _ = privVal.SignVote(header.ChainID, vote) diff --git a/consensus/common_test.go b/consensus/common_test.go index 05e488b56..8ee2f1f38 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -101,6 +101,7 @@ func (vs *validatorStub) signVote( Timestamp: tmtime.Now(), Type: voteType, BlockID: types.BlockID{Hash: hash, PartsHeader: header}, + Signature: []byte{}, } err = vs.PrivValidator.SignVote(config.ChainID(), vote) diff --git a/consensus/invalid_test.go b/consensus/invalid_test.go index 1ae0a69ec..627240b3e 100644 --- a/consensus/invalid_test.go +++ b/consensus/invalid_test.go @@ -80,6 +80,7 @@ func invalidDoPrevoteFunc(t *testing.T, height int64, round int, cs *State, sw * BlockID: types.BlockID{ Hash: blockHash, PartsHeader: types.PartSetHeader{Total: 1, Hash: tmrand.Bytes(32)}}, + Signature: []byte{}, } cs.privValidator.SignVote(cs.state.ChainID, precommit) cs.privValidator = nil // disable priv val so we don't do normal votes diff --git a/consensus/state.go b/consensus/state.go index bb049d2b5..bab97babf 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1941,6 +1941,7 @@ func (cs *State) signVote( Timestamp: cs.voteTime(), Type: msgType, BlockID: types.BlockID{Hash: hash, PartsHeader: header}, + Signature: []byte{}, } err = cs.privValidator.SignVote(cs.state.ChainID, vote) diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index 373e28cca..3953d0bf4 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -67,6 +67,7 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: types.BlockID{Hash: []byte("fakehash"), PartsHeader: types.PartSetHeader{}}, + Signature: []byte{}, } chainID := config.ChainID() err = privVal.SignVote(chainID, vote) diff --git a/lite/helpers.go b/lite/helpers.go index f56071ed0..cb4254878 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -104,6 +104,7 @@ func makeVote(header *types.Header, voterSet *types.VoterSet, key crypto.PrivKey Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } // Sign it signBytes := vote.SignBytes(header.ChainID) diff --git a/lite2/helpers_test.go b/lite2/helpers_test.go index 291b417a4..db167f981 100644 --- a/lite2/helpers_test.go +++ b/lite2/helpers_test.go @@ -146,6 +146,7 @@ func makeVote(header *types.Header, voterIdx int, key crypto.PrivKey, blockID ty Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } // Sign it signBytes := vote.SignBytes(header.ChainID) diff --git a/privval/file_test.go b/privval/file_test.go index a4b3a3c8c..ad57b83f0 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -330,6 +330,7 @@ func newVote(addr types.Address, idx int, height int64, round int, typ byte, blo Type: types.SignedMsgType(typ), Timestamp: tmtime.Now(), BlockID: blockID, + Signature: []byte{}, } } diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 1853192cc..7a5b785a0 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -127,8 +127,8 @@ func TestSignerGenerateVRFProof(t *testing.T) { func TestSignerVote(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} - have := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} + have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -143,8 +143,8 @@ func TestSignerVote(t *testing.T) { func TestSignerVoteResetDeadline(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} - have := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} + have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -169,8 +169,8 @@ func TestSignerVoteResetDeadline(t *testing.T) { func TestSignerVoteKeepAlive(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} - have := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} + have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -216,7 +216,7 @@ func TestSignerSignProposalErrors(t *testing.T) { func TestSignerSignVoteErrors(t *testing.T) { for _, tc := range getSignerTestCases(t) { ts := time.Now() - vote := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + vote := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} // Replace signer service privval with one that always fails tc.signerServer.privVal = types.NewErroringMockPV() @@ -271,7 +271,7 @@ func TestSignerUnexpectedResponse(t *testing.T) { defer tc.signerClient.Close() ts := time.Now() - want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} + want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} e := tc.signerClient.SignVote(tc.chainID, want) assert.EqualError(t, e, "received unexpected response") diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 48c4f9cbc..0e43485dc 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -564,11 +564,16 @@ func deepcpVote(vote *types.Vote) (res *types.Vote) { Hash: make([]byte, len(vote.BlockID.Hash)), PartsHeader: vote.BlockID.PartsHeader, }, - Signature: make([]byte, len(vote.Signature)), + Signature: []byte{}, } copy(res.ValidatorAddress, vote.ValidatorAddress) copy(res.BlockID.Hash, vote.BlockID.Hash) - copy(res.Signature, vote.Signature) + if vote.Signature == nil { + res.Signature = nil + } else if len(vote.Signature) > 0 { + res.Signature = make([]byte, len(vote.Signature)) + copy(res.Signature, vote.Signature) + } return } @@ -606,6 +611,7 @@ func makeEvidences( Hash: tmhash.Sum([]byte("partset")), }, }, + Signature: []byte{}, } var err error diff --git a/state/validation_test.go b/state/validation_test.go index c70abc4c3..7fe12f733 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -188,6 +188,7 @@ func TestValidateBlockCommit(t *testing.T) { Timestamp: tmtime.Now(), Type: types.PrecommitType, BlockID: blockID, + Signature: []byte{}, } err = badPrivVal.SignVote(chainID, goodVote) require.NoError(t, err, "height %d", height) diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go index f9d48fdcb..bfef830d4 100644 --- a/tools/tm-signer-harness/internal/test_harness.go +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -273,6 +273,7 @@ func (th *TestHarness) TestSignVote() error { ValidatorIndex: 0, ValidatorAddress: tmhash.SumTruncated([]byte("addr")), Timestamp: time.Now(), + Signature: []byte{}, } voteBytes := vote.SignBytes(th.chainID) // sign the vote diff --git a/types/block_test.go b/types/block_test.go index 34c0d10ae..440d1bd70 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -560,6 +560,7 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { Type: PrecommitType, BlockID: tc.blockIDs[n], Timestamp: tmtime.Now(), + Signature: []byte{}, } added, err := signAddVote(vals[vi], vote, voteSet) diff --git a/types/evidence_test.go b/types/evidence_test.go index 64d7178b1..e1ddfec9a 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -30,6 +30,7 @@ func makeVote( Round: round, Type: SignedMsgType(step), BlockID: blockID, + Signature: []byte{}, } err = val.SignVote(chainID, v) if err != nil { diff --git a/types/test_util.go b/types/test_util.go index 377c965a8..115d0575b 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -23,6 +23,7 @@ func MakeCommit(blockID BlockID, height int64, round int, Type: PrecommitType, BlockID: blockID, Timestamp: now, + Signature: []byte{}, } _, err = signAddVote(validators[i], vote, voteSet) @@ -64,6 +65,7 @@ func MakeVote( Timestamp: now, Type: PrecommitType, BlockID: blockID, + Signature: []byte{}, } if err := privVal.SignVote(chainID, vote); err != nil { return nil, err diff --git a/types/vote.go b/types/vote.go index 0568396db..5f0cf9782 100644 --- a/types/vote.go +++ b/types/vote.go @@ -164,10 +164,10 @@ func (vote *Vote) ValidateBasic() error { if vote.ValidatorIndex < 0 { return errors.New("negative ValidatorIndex") } - if len(vote.Signature) == 0 { + if vote.Signature != nil && len(vote.Signature) == 0 { return errors.New("signature is missing") } - if len(vote.Signature) > MaxSignatureSize { + if vote.Signature != nil && len(vote.Signature) > MaxSignatureSize { return fmt.Errorf("signature is too big %d (max: %d)", len(vote.Signature), MaxSignatureSize) } return nil diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 0153f38d1..c3985c455 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -97,6 +97,7 @@ func TestAddVote(t *testing.T) { Type: PrevoteType, Timestamp: tmtime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } _, err = signAddVote(val0, vote, voteSet) if err != nil { @@ -127,6 +128,7 @@ func Test2_3Majority(t *testing.T) { Type: PrevoteType, Timestamp: tmtime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } // 6 out of 10 voted for nil. for i := 0; i < 6; i++ { @@ -193,6 +195,7 @@ func Test2_3MajorityRedux(t *testing.T) { Timestamp: tmtime.Now(), Type: PrevoteType, BlockID: BlockID{blockHash, blockPartsHeader}, + Signature: []byte{}, } // 66 out of 100 voted for nil. @@ -306,6 +309,7 @@ func TestBadVotes(t *testing.T) { Timestamp: tmtime.Now(), Type: PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } // val0 votes for nil. @@ -383,6 +387,7 @@ func TestConflicts(t *testing.T) { Timestamp: tmtime.Now(), Type: PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, + Signature: []byte{}, } val0, err := privValidators[0].GetPubKey() @@ -525,6 +530,7 @@ func TestMakeCommit(t *testing.T) { Timestamp: tmtime.Now(), Type: PrecommitType, BlockID: BlockID{blockHash, blockPartsHeader}, + Signature: []byte{}, } // 6 out of 10 voted for some block. diff --git a/types/vote_test.go b/types/vote_test.go index ee1645ef3..e15ecaf51 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -41,6 +41,7 @@ func exampleVote(t byte) *Vote { }, ValidatorAddress: crypto.AddressHash([]byte("validator_address")), ValidatorIndex: 56789, + Signature: []byte{}, } } @@ -62,13 +63,13 @@ func TestVoteSignBytesTestVectors(t *testing.T) { want []byte }{ 0: { - "", &Vote{}, + "", &Vote{Signature: []byte{}}, // NOTE: Height and Round are skipped here. This case needs to be considered while parsing. []byte{0xd, 0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, // with proper (fixed size) height and round (PreCommit): 1: { - "", &Vote{Height: 1, Round: 1, Type: PrecommitType}, + "", &Vote{Height: 1, Round: 1, Type: PrecommitType, Signature: []byte{}}, []byte{ 0x21, // length 0x8, // (field_number << 3) | wire_type @@ -83,7 +84,7 @@ func TestVoteSignBytesTestVectors(t *testing.T) { }, // with proper (fixed size) height and round (PreVote): 2: { - "", &Vote{Height: 1, Round: 1, Type: PrevoteType}, + "", &Vote{Height: 1, Round: 1, Type: PrevoteType, Signature: []byte{}}, []byte{ 0x21, // length 0x8, // (field_number << 3) | wire_type @@ -97,7 +98,7 @@ func TestVoteSignBytesTestVectors(t *testing.T) { 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, 3: { - "", &Vote{Height: 1, Round: 1}, + "", &Vote{Height: 1, Round: 1, Signature: []byte{}}, []byte{ 0x1f, // length 0x11, // (field_number << 3) | wire_type @@ -110,7 +111,7 @@ func TestVoteSignBytesTestVectors(t *testing.T) { }, // containing non-empty chain_id: 4: { - "test_chain_id", &Vote{Height: 1, Round: 1}, + "test_chain_id", &Vote{Height: 1, Round: 1, Signature: []byte{}}, []byte{ 0x2e, // length 0x11, // (field_number << 3) | wire_type @@ -132,8 +133,8 @@ func TestVoteSignBytesTestVectors(t *testing.T) { } func TestVoteProposalNotEq(t *testing.T) { - cv := CanonicalizeVote("", &Vote{Height: 1, Round: 1}) - p := CanonicalizeProposal("", &Proposal{Height: 1, Round: 1}) + cv := CanonicalizeVote("", &Vote{Height: 1, Round: 1, Signature: []byte{}}) + p := CanonicalizeProposal("", &Proposal{Height: 1, Round: 1, Signature: []byte{}}) vb, err := cdc.MarshalBinaryLengthPrefixed(cv) require.NoError(t, err) pb, err := cdc.MarshalBinaryLengthPrefixed(p) @@ -231,6 +232,7 @@ func TestMaxVoteBytes(t *testing.T) { Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")), }, }, + Signature: []byte{}, } privVal := NewMockPV() @@ -300,7 +302,7 @@ func TestVoteProtobuf(t *testing.T) { expPass bool }{ {"success", vote, true}, - {"fail vote validate basic", &Vote{}, false}, + {"fail vote validate basic", &Vote{Signature: []byte{}}, false}, {"failure nil", nil, false}, } for _, tc := range testCases { From b171a9235c55f4d56f263ddc9ae3645943957ad4 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 8 Sep 2020 16:05:21 +0900 Subject: [PATCH 12/36] fixed to restore aggregate signature when restoring Commit to VoteSet --- types/block.go | 1 + types/block_test.go | 3 +++ types/vote_test.go | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/types/block.go b/types/block.go index 3c3e95656..873652d96 100644 --- a/types/block.go +++ b/types/block.go @@ -740,6 +740,7 @@ func NewCommitWithAggregatedSignature( // Inverse of VoteSet.MakeCommit(). func CommitToVoteSet(chainID string, commit *Commit, voters *VoterSet) *VoteSet { voteSet := NewVoteSet(chainID, commit.Height, commit.Round, PrecommitType, voters) + voteSet.aggregatedSignature = commit.AggregatedSignature for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some precommits can be missing. diff --git a/types/block_test.go b/types/block_test.go index 440d1bd70..cb17dd0af 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -512,6 +512,9 @@ func TestCommitToVoteSet(t *testing.T) { chainID := voteSet.ChainID() voteSet2 := CommitToVoteSet(chainID, commit, valSet) + assert.Equal(t, voteSet.aggregatedSignature, commit.AggregatedSignature) + assert.Equal(t, commit.AggregatedSignature, voteSet2.aggregatedSignature) + for i := 0; i < len(vals); i++ { vote1 := voteSet.GetByIndex(i) vote2 := voteSet2.GetByIndex(i) diff --git a/types/vote_test.go b/types/vote_test.go index e15ecaf51..c8a30df9e 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -275,7 +275,7 @@ func TestVoteValidateBasic(t *testing.T) { }, true}, {"Invalid Address", func(v *Vote) { v.ValidatorAddress = make([]byte, 1) }, true}, {"Invalid ValidatorIndex", func(v *Vote) { v.ValidatorIndex = -1 }, true}, - {"Invalid Signature", func(v *Vote) { v.Signature = nil }, true}, + {"Invalid Signature", func(v *Vote) { v.Signature = []byte{} }, true}, {"Too big Signature", func(v *Vote) { v.Signature = make([]byte, MaxSignatureSize+1) }, true}, } for _, tc := range testCases { From d1ab88f6913870586ae7ab787dbea2aeb0e0a5c8 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Mon, 7 Sep 2020 16:56:55 +0900 Subject: [PATCH 13/36] fix: docker file; add library --- DOCKER/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index e3a918749..2bac91cb7 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -15,7 +15,7 @@ ENV TMHOME /tendermint # could execute bash commands. RUN apk update && \ apk upgrade && \ - apk --no-cache add curl jq bash && \ + apk --no-cache add git make gcc libc-dev build-base curl jq bash && \ addgroup tmuser && \ adduser -S -G tmuser tmuser -h "$TMHOME" From 5b012e86a21c0b602ac50013d89a094d94e41d13 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 8 Sep 2020 18:07:29 +0900 Subject: [PATCH 14/36] fixed conversion from int to string yields a string of one rune --- libs/cmap/cmap_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/cmap/cmap_test.go b/libs/cmap/cmap_test.go index b6ea0117d..b1ab85c0c 100644 --- a/libs/cmap/cmap_test.go +++ b/libs/cmap/cmap_test.go @@ -60,10 +60,10 @@ func TestContains(t *testing.T) { func BenchmarkCMapHas(b *testing.B) { m := NewCMap() for i := 0; i < 1000; i++ { - m.Set(string(i), i) + m.Set(fmt.Sprint(i), i) } b.ResetTimer() for i := 0; i < b.N; i++ { - m.Has(string(i)) + m.Has(fmt.Sprint(i)) } } From 3fbb8b11d80fe31a28beffaec599d747c4b31083 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Wed, 23 Sep 2020 12:32:20 +0900 Subject: [PATCH 15/36] added reproduction test-case for #121 --- crypto/composite/composite_test.go | 123 +++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index c85327b03..4a7b5f67e 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -2,8 +2,15 @@ package composite_test import ( "bytes" + "encoding/base64" + "encoding/hex" + "fmt" "testing" + "github.com/tendermint/go-amino" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls" "github.com/tendermint/tendermint/crypto/composite" "github.com/tendermint/tendermint/crypto/ed25519" @@ -170,3 +177,119 @@ func TestPubKeyComposite_VRFVerify(t *testing.T) { t.Errorf("Output is different from the VRF key.") } } + +func TestEnvironmentalCompatibility(t *testing.T) { + var cdc = amino.NewCodec() + cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) + cdc.RegisterInterface((*crypto.PubKey)(nil), nil) + cdc.RegisterConcrete(bls.PubKeyBLS12{}, bls.PubKeyAminoName, nil) + 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) + + t.Run("MarshalCompositeKey", func(t *testing.T) { + privKey := composite.GenPrivKey() + privKeyBytes, err := cdc.MarshalBinaryBare(privKey) + if err != nil { + t.Fatal(err) + } + fmt.Printf("PrivKeyComposite: %s\n", hex.EncodeToString(privKeyBytes)) + }) + + t.Run("The same signature is generated from the same key binary for any runtime env", func(t *testing.T) { + privKeyBytes, err := hex.DecodeString("9f3ee8f00a25eaecf03f203761de9e836bed524dd0b2737c56b69f69672c2b3e40" + + "31b74817474c62b204221245a328891040c69e39ac62519a09895babbb84d6a3a8ea4b69a61760e0c0b04052e252c272ef478fb" + + "da7337ad81ed4202d2bb3ad8240e17248aae2dc169b2e12b45b366d09be") + if err != nil { + t.Fatal(err) + } + compositePrivKey := composite.PrivKeyComposite{} + if err := cdc.UnmarshalBinaryBare(privKeyBytes, &compositePrivKey); err != nil { + t.Fatal(err) + } + msg := []byte("hello, world") + actual, err := compositePrivKey.Sign(msg) + if err != nil { + t.Fatal(err) + } + expected, err := hex.DecodeString("894d184a975aed6b442991e2ce60f7b39d6e810bde5ccb5d5d45dd6e461028339aa43" + + "2d027462f8ce7a5cf7db71ddabb0446cbb90ac817c26cc65614536cf99a9b739592dce467cc3d26bff63f5509157c2daa3b485" + + "afa3d2efab804c16f271d") + //expected, err := hex.DecodeString("b0896e20be15f757686cec647bf86c97bc73f6607b730abb80828df110226498ba1c6b" + + // "a9ccc645a7ee4a08b7db07e6d103699b40369475df592343b77d7e798f659b1f85eb9e237f870a1c98fa5e70e685e82ccf6dec4" + + // "355e61450b5fa4ddb9b") + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(expected, actual) { + t.Logf("Expected Signature: %s (%d bytes)", hex.EncodeToString(expected), len(expected)) + t.Logf("Actual Signature: %s (%d bytes)", hex.EncodeToString(actual), len(actual)) + t.Errorf("Signatures generated from the same key and message are different than expected") + } + }) + + // https://github.com/line/tendermint/issues/121 + t.Run("A reproduction test of issue #121", func(t *testing.T) { + + // restore BLS private key from base64 string in priv_validator_key.json + blsPrivKey := bls.PrivKeyBLS12{} + blsPrivKeyBytes, err := base64.StdEncoding.DecodeString("BpqODFajV6NnQhBfT8ERyvwyqPoZS664e1v35sfr76g=") + if err != nil { + t.Fatal(err) + } else if len(blsPrivKeyBytes) != bls.PrivKeyBLS12Size { + t.Fatalf("fixed private key size: %d != %d", bls.PrivKeyBLS12Size, len(blsPrivKeyBytes)) + } + copy(blsPrivKey[:], blsPrivKeyBytes) + + // restore Ed25519 private key from base64 string in priv_validator_key.json + ed25519PrivKey := ed25519.PrivKeyEd25519{} + ed25519PrivKeyBytes, err := base64.StdEncoding.DecodeString("TGb5K4TbD1XdNd0HGEt7I6quhTJ2aSckgPLLBKs8hDUC" + + "4Wh8kfEmnRUeMYtR8V0UfNwQyTYqGupZeyIhJcV1TA==") + if err != nil { + t.Fatal(err) + } else if len(ed25519PrivKeyBytes) != len(ed25519PrivKey) { + t.Fatalf("fixed private key size: %d != %d", len(ed25519PrivKey), len(ed25519PrivKeyBytes)) + } + copy(ed25519PrivKey[:], ed25519PrivKeyBytes) + + // compare addresses to assumed value + compositePrivKey := composite.NewPrivKeyComposite(blsPrivKey, ed25519PrivKey) + compositePubKey := compositePrivKey.PubKey() + address, err := hex.DecodeString("7A68265205CB115AE35A13515C423F1721E87BB4") + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(address, compositePubKey.Address()) { + t.Fatalf("addresses didn't match: %s", hex.EncodeToString(compositePubKey.Address())) + } + + // compare generated signature to assumed value + message, err := hex.DecodeString("44080111010000000000000022300A147A68265205CB115AE35A13515C423F1721E87BB" + + "412180A147A68265205CB115AE35A13515C423F1721E87BB410013205636861696E") + if err != nil { + t.Fatal(err) + } + signature, err := compositePrivKey.Sign(message) + if err != nil { + t.Fatal(err) + } + //expectedSig, err := hex.DecodeString("9427431821a7db58542bf52289cfa1ffb087673541df3558f5a5d69633d27" + + // "bb7337846befc581536d48b9a805309c9b811eecb92336b290e52ee5be83e081f788ee4c66a154c8a954f5330744058c44aaa95" + + // "d8103fcddab0dfdda3b5a8551944") + expectedSig, err := hex.DecodeString("A97BA8640BC3A1E05A2F4979D7FCF21DAA2A5D4AC64E16F76C6B1CBD646D6BF6EE6" + + "DF18B212CAE18D100A7CA0BC3EF8A17B3E7B82ED4105BEABA9F3B857B067E73D9251962B5BC4CF39B10547287DF8ADB9ED24AF7" + + "AA834114AE20B0BB6DD98D") + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(expectedSig, signature) { + t.Logf("Address: %s", hex.EncodeToString(address)) + t.Logf("Message: %s (%d bytes)", hex.EncodeToString(message), len(message)) + t.Logf("Expected Signature: %s (%d bytes)", hex.EncodeToString(expectedSig), len(expectedSig)) + t.Logf("Actual Signature: %s (%d bytes)", hex.EncodeToString(signature), len(signature)) + t.Errorf("Different signatures are made for the same message with the same private key.") + } + }) +} From bdca2911b24c4c989a95cce6b9d0d3066c48e8d3 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Wed, 23 Sep 2020 13:52:38 +0900 Subject: [PATCH 16/36] fixed a number conversion miss in Tendermint's CMAP benchmark --- Makefile | 2 +- crypto/composite/composite_test.go | 6 ------ libs/pubsub/pubsub_test.go | 3 ++- p2p/upnp/upnp.go | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 6cab37f78..d679594b1 100644 --- a/Makefile +++ b/Makefile @@ -231,7 +231,7 @@ DOCKER_CMD = docker run --rm \ -v `pwd`:$(DOCKER_HOME) \ -w $(DOCKER_HOME) DOCKER_IMG = golang:1.14.6-alpine3.12 -BUILD_CMD = apk add --update --no-cache git make gcc libc-dev build-base curl jq file gmp-dev clang \ +BUILD_CMD = apk add --update --no-cache git make gcc libc-dev build-base curl jq file gmp-dev clang nasm \ && cd crypto/bls/internal/bls-eth-go-binary \ && make CXX=clang++ \ && cd $(DOCKER_HOME) \ diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 4a7b5f67e..795d3d528 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -217,9 +217,6 @@ func TestEnvironmentalCompatibility(t *testing.T) { expected, err := hex.DecodeString("894d184a975aed6b442991e2ce60f7b39d6e810bde5ccb5d5d45dd6e461028339aa43" + "2d027462f8ce7a5cf7db71ddabb0446cbb90ac817c26cc65614536cf99a9b739592dce467cc3d26bff63f5509157c2daa3b485" + "afa3d2efab804c16f271d") - //expected, err := hex.DecodeString("b0896e20be15f757686cec647bf86c97bc73f6607b730abb80828df110226498ba1c6b" + - // "a9ccc645a7ee4a08b7db07e6d103699b40369475df592343b77d7e798f659b1f85eb9e237f870a1c98fa5e70e685e82ccf6dec4" + - // "355e61450b5fa4ddb9b") if err != nil { t.Fatal(err) } @@ -275,9 +272,6 @@ func TestEnvironmentalCompatibility(t *testing.T) { if err != nil { t.Fatal(err) } - //expectedSig, err := hex.DecodeString("9427431821a7db58542bf52289cfa1ffb087673541df3558f5a5d69633d27" + - // "bb7337846befc581536d48b9a805309c9b811eecb92336b290e52ee5be83e081f788ee4c66a154c8a954f5330744058c44aaa95" + - // "d8103fcddab0dfdda3b5a8551944") expectedSig, err := hex.DecodeString("A97BA8640BC3A1E05A2F4979D7FCF21DAA2A5D4AC64E16F76C6B1CBD646D6BF6EE6" + "DF18B212CAE18D100A7CA0BC3EF8A17B3E7B82ED4105BEABA9F3B857B067E73D9251962B5BC4CF39B10547287DF8ADB9ED24AF7" + "AA834114AE20B0BB6DD98D") diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index f9dd592d1..85076f31e 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "runtime/debug" + "strconv" "testing" "time" @@ -382,7 +383,7 @@ func benchmarkNClients(n int, b *testing.B) { s.PublishWithEvents( ctx, "Gamora", - map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {string(i)}}, + map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {strconv.Itoa(i)}}, ) } } diff --git a/p2p/upnp/upnp.go b/p2p/upnp/upnp.go index 6b006dbf1..a5771dd84 100644 --- a/p2p/upnp/upnp.go +++ b/p2p/upnp/upnp.go @@ -209,7 +209,7 @@ func getServiceURL(rootURL string) (url, urnDomain string, err error) { defer r.Body.Close() // nolint: errcheck if r.StatusCode >= 400 { - err = errors.New(string(r.StatusCode)) + err = errors.New(strconv.Itoa(r.StatusCode)) return } var root Root From 863d60a23650143fd25eade320a91f67e15244f8 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 25 Sep 2020 10:00:26 +0900 Subject: [PATCH 17/36] fix: makefile --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index d679594b1..66f61d5aa 100644 --- a/Makefile +++ b/Makefile @@ -248,12 +248,12 @@ build-shell: build-linux: # Download, build and add the BSL local library to modules - if [ ! -d $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary ]; then \ - mkdir -p $(SRCPATH)/crypto/bls/internal && \ - git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ - git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ - git clone https://github.com/herumi/bls-eth-go-binary $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ - fi + rm -rf $(SRCPATH)/crypto/bls/internal/mcl + rm -rf $(SRCPATH)/crypto/bls/internal/bls + rm -rf $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary + git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ + git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ + git clone https://github.com/herumi/bls-eth-go-binary $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ # Build Linux binary $(DOCKER_CMD) ${DOCKER_IMG} /bin/sh -c "$(BUILD_CMD)" From 776a34b5e456346c5cead106308af53d8cdbbfb8 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 25 Sep 2020 10:04:03 +0900 Subject: [PATCH 18/36] fix: makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 66f61d5aa..90bba0c78 100644 --- a/Makefile +++ b/Makefile @@ -249,8 +249,8 @@ build-shell: build-linux: # Download, build and add the BSL local library to modules rm -rf $(SRCPATH)/crypto/bls/internal/mcl - rm -rf $(SRCPATH)/crypto/bls/internal/bls - rm -rf $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary + rm -rf $(SRCPATH)/crypto/bls/internal/bls + rm -rf $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ git clone https://github.com/herumi/bls-eth-go-binary $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ From bce40be92a688cf8b2d855d26adab839a93c9424 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 25 Sep 2020 15:41:29 +0900 Subject: [PATCH 19/36] test: add more hard coded test case --- crypto/composite/composite_test.go | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 795d3d528..0c6b43570 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -6,6 +6,11 @@ import ( "encoding/hex" "fmt" "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/tendermint/tendermint/types" "github.com/tendermint/go-amino" @@ -178,6 +183,39 @@ func TestPubKeyComposite_VRFVerify(t *testing.T) { } } +func TestMy(t *testing.T) { + blsKey := bls.PrivKeyBLS12{} + vrfKey := ed25519.PrivKeyEd25519{} + blsKeyBinary := []byte{74, 34, 180, 175, 203, 92, 237, 211, 145, 23, 55, 92, 100, 6, 43, 241, 39, 218, 125, 31, 130, 120, 96, 248, 193, 51, 90, 185, 56, 18, 254, 90} + vrfKeyBinary := []byte{190, 183, 71, 79, 63, 127, 146, 85, 204, 90, 84, 59, 1, 183, 55, 243, 215, 132, 125, 80, 200, 246, 24, 142, 147, 52, 111, 98, 204, 19, 75, 137, 5, 246, 217, 51, 191, 52, 138, 206, 156, 9, 144, 130, 169, 214, 112, 138, 61, 41, 253, 76, 242, 178, 34, 145, 143, 159, 194, 70, 94, 47, 101, 40} + copy(blsKey[:], blsKeyBinary) + copy(vrfKey[:], vrfKeyBinary) + privKey := composite.NewPrivKeyComposite(crypto.PrivKey(blsKey), crypto.PrivKey(vrfKey)) + pubKey := privKey.PubKey() + address := pubKey.Address() + sampleVote := types.Vote{} + sampleVote.Height = 1 + sampleVote.ValidatorIndex = 1 + sampleVote.BlockID = types.BlockID{Hash: address.Bytes(), PartsHeader: types.PartSetHeader{Total: 1, Hash: address.Bytes()}} + sampleVote.Round = 0 + sampleVote.Timestamp = time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) + sampleVote.Type = types.PrevoteType + sampleVote.ValidatorAddress = address + signBytes := sampleVote.SignBytes("chainid") + sampleVote.Signature, _ = privKey.Sign(signBytes) + t.Logf("pubKey: %X", pubKey.Bytes()) + t.Logf("vote: %X", sampleVote.SignBytes("chainid")) + t.Logf("vote signature: %X", sampleVote.Signature) + pubKeyBytes, _ := hex.DecodeString("D68FFBC130B25FCC05C88BE1D2CF5D53BC8E07A67C43F0FC1CD210A7E885FADA369B959FA74E7FF3ACF4BF9A96353D170B9D5C69701624DE642005F6D933BF348ACE9C099082A9D6708A3D29FD4CF2B222918F9FC2465E2F6528") + voteBytes, _ := hex.DecodeString("46080111010000000000000022300A14D9FB38E2F84230D6165FAD29072C9318EED18F5F12180A14D9FB38E2F84230D6165FAD29072C9318EED18F5F10013207636861696E6964") + signatureBytes, _ := hex.DecodeString("83AD3675D4AB4E0C3F9052BF29BE26A209258690DD89D619E64206A2FFFA7E3E8626D546340B44A1DCE4B39543630518123316C5A40EDFB3DE38015C6E1357C14DECD9A9A97949DAF40BDA755DC866AFD12963D85583C2E649138BBCAA27A9B0") + assert.Equal(t, pubKey.Bytes(), pubKeyBytes) + assert.Equal(t, sampleVote.SignBytes("chainid"), voteBytes) + assert.Equal(t, sampleVote.Signature, signatureBytes) + verifying := pubKey.VerifyBytes(sampleVote.SignBytes("chainid"), sampleVote.Signature) + assert.True(t, verifying) +} + func TestEnvironmentalCompatibility(t *testing.T) { var cdc = amino.NewCodec() cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) From 338fffc4c7565b243de02f3785f82f5cc33e2991 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 25 Sep 2020 16:01:42 +0900 Subject: [PATCH 20/36] fix: lint error --- crypto/composite/composite_test.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 0c6b43570..3eabf06e8 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -186,29 +186,41 @@ func TestPubKeyComposite_VRFVerify(t *testing.T) { func TestMy(t *testing.T) { blsKey := bls.PrivKeyBLS12{} vrfKey := ed25519.PrivKeyEd25519{} - blsKeyBinary := []byte{74, 34, 180, 175, 203, 92, 237, 211, 145, 23, 55, 92, 100, 6, 43, 241, 39, 218, 125, 31, 130, 120, 96, 248, 193, 51, 90, 185, 56, 18, 254, 90} - vrfKeyBinary := []byte{190, 183, 71, 79, 63, 127, 146, 85, 204, 90, 84, 59, 1, 183, 55, 243, 215, 132, 125, 80, 200, 246, 24, 142, 147, 52, 111, 98, 204, 19, 75, 137, 5, 246, 217, 51, 191, 52, 138, 206, 156, 9, 144, 130, 169, 214, 112, 138, 61, 41, 253, 76, 242, 178, 34, 145, 143, 159, 194, 70, 94, 47, 101, 40} + blsKeyBinary := []byte{74, 34, 180, 175, 203, 92, 237, 211, 145, 23, 55, 92, 100, 6, 43, 241, 39, + 218, 125, 31, 130, 120, 96, 248, 193, 51, 90, 185, 56, 18, 254, 90} + vrfKeyBinary := []byte{190, 183, 71, 79, 63, 127, 146, 85, 204, 90, 84, 59, 1, 183, 55, 243, 215, 132, 125, 80, + 200, 246, 24, 142, 147, 52, 111, 98, 204, 19, 75, 137, 5, 246, 217, 51, 191, 52, 138, 206, 156, 9, 144, 130, + 169, 214, 112, 138, 61, 41, 253, 76, 242, 178, 34, 145, 143, 159, 194, 70, 94, 47, 101, 40} copy(blsKey[:], blsKeyBinary) copy(vrfKey[:], vrfKeyBinary) privKey := composite.NewPrivKeyComposite(crypto.PrivKey(blsKey), crypto.PrivKey(vrfKey)) + pubKey := privKey.PubKey() address := pubKey.Address() + sampleVote := types.Vote{} sampleVote.Height = 1 sampleVote.ValidatorIndex = 1 - sampleVote.BlockID = types.BlockID{Hash: address.Bytes(), PartsHeader: types.PartSetHeader{Total: 1, Hash: address.Bytes()}} + sampleVote.BlockID = types.BlockID{ + Hash: address.Bytes(), + PartsHeader: types.PartSetHeader{Total: 1, Hash: address.Bytes()}} sampleVote.Round = 0 sampleVote.Timestamp = time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) sampleVote.Type = types.PrevoteType sampleVote.ValidatorAddress = address + signBytes := sampleVote.SignBytes("chainid") sampleVote.Signature, _ = privKey.Sign(signBytes) t.Logf("pubKey: %X", pubKey.Bytes()) t.Logf("vote: %X", sampleVote.SignBytes("chainid")) t.Logf("vote signature: %X", sampleVote.Signature) - pubKeyBytes, _ := hex.DecodeString("D68FFBC130B25FCC05C88BE1D2CF5D53BC8E07A67C43F0FC1CD210A7E885FADA369B959FA74E7FF3ACF4BF9A96353D170B9D5C69701624DE642005F6D933BF348ACE9C099082A9D6708A3D29FD4CF2B222918F9FC2465E2F6528") - voteBytes, _ := hex.DecodeString("46080111010000000000000022300A14D9FB38E2F84230D6165FAD29072C9318EED18F5F12180A14D9FB38E2F84230D6165FAD29072C9318EED18F5F10013207636861696E6964") - signatureBytes, _ := hex.DecodeString("83AD3675D4AB4E0C3F9052BF29BE26A209258690DD89D619E64206A2FFFA7E3E8626D546340B44A1DCE4B39543630518123316C5A40EDFB3DE38015C6E1357C14DECD9A9A97949DAF40BDA755DC866AFD12963D85583C2E649138BBCAA27A9B0") + pubKeyBytes, _ := hex.DecodeString("D68FFBC130B25FCC05C88BE1D2CF5D53BC8E07A67C43F0FC1CD210A7E885FADA369B959F" + + "A74E7FF3ACF4BF9A96353D170B9D5C69701624DE642005F6D933BF348ACE9C099082A9D6708A3D29FD4CF2B222918F9FC2465E2F6528") + voteBytes, _ := hex.DecodeString("46080111010000000000000022300A14D9FB38E2F84230D6165FAD29072C9318EED18F5F12" + + "180A14D9FB38E2F84230D6165FAD29072C9318EED18F5F10013207636861696E6964") + signatureBytes, _ := hex.DecodeString("83AD3675D4AB4E0C3F9052BF29BE26A209258690DD89D619E64206A2FFFA7E3E8626D" + + "546340B44A1DCE4B39543630518123316C5A40EDFB3DE38015C6E1357C14DECD9A9A97949DAF40BDA755DC866AFD12963D85583C2E6" + + "49138BBCAA27A9B0") assert.Equal(t, pubKey.Bytes(), pubKeyBytes) assert.Equal(t, sampleVote.SignBytes("chainid"), voteBytes) assert.Equal(t, sampleVote.Signature, signatureBytes) From a61c8023b1f025c7f85ba3d121b0d230d75c3837 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 25 Sep 2020 16:24:30 +0900 Subject: [PATCH 21/36] fix: circleci failed too often --- mempool/reactor_test.go | 2 +- types/voter_set_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mempool/reactor_test.go b/mempool/reactor_test.go index 87da0557d..f362dd71a 100644 --- a/mempool/reactor_test.go +++ b/mempool/reactor_test.go @@ -108,7 +108,7 @@ func ensureNoTxs(t *testing.T, reactor *Reactor, timeout time.Duration) { } const ( - NumTxs = 1000 + NumTxs = 100 // circleci failed too often with the value 1000 Timeout = 120 * time.Second // ridiculously high because CircleCI is slow ) diff --git a/types/voter_set_test.go b/types/voter_set_test.go index c90a4fb1a..a2bec7462 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -339,7 +339,7 @@ func TestCalVotersNum2(t *testing.T) { } hash := genDoc.Hash() - loopCount := 1000 + loopCount := 100 // circleci failed too often with the value 1000 electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 1) electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 2) electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 3) From bc238cbe2ecf369160cf184ea0447f4bd532b215 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Fri, 25 Sep 2020 16:46:03 +0900 Subject: [PATCH 22/36] fix: test failed --- types/voter_set_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/voter_set_test.go b/types/voter_set_test.go index a2bec7462..878138f6e 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -339,7 +339,7 @@ func TestCalVotersNum2(t *testing.T) { } hash := genDoc.Hash() - loopCount := 100 // circleci failed too often with the value 1000 + loopCount := 300 // circleci failed too often with the value 1000 electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 1) electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 2) electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 3) From 204e6fea690da4cc00143d46b45d475164b9569a Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Mon, 28 Sep 2020 09:12:30 +0900 Subject: [PATCH 23/36] fix: remove temporary test code --- crypto/composite/composite_test.go | 50 ------------------------------ 1 file changed, 50 deletions(-) diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 3eabf06e8..795d3d528 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -6,11 +6,6 @@ import ( "encoding/hex" "fmt" "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/tendermint/tendermint/types" "github.com/tendermint/go-amino" @@ -183,51 +178,6 @@ func TestPubKeyComposite_VRFVerify(t *testing.T) { } } -func TestMy(t *testing.T) { - blsKey := bls.PrivKeyBLS12{} - vrfKey := ed25519.PrivKeyEd25519{} - blsKeyBinary := []byte{74, 34, 180, 175, 203, 92, 237, 211, 145, 23, 55, 92, 100, 6, 43, 241, 39, - 218, 125, 31, 130, 120, 96, 248, 193, 51, 90, 185, 56, 18, 254, 90} - vrfKeyBinary := []byte{190, 183, 71, 79, 63, 127, 146, 85, 204, 90, 84, 59, 1, 183, 55, 243, 215, 132, 125, 80, - 200, 246, 24, 142, 147, 52, 111, 98, 204, 19, 75, 137, 5, 246, 217, 51, 191, 52, 138, 206, 156, 9, 144, 130, - 169, 214, 112, 138, 61, 41, 253, 76, 242, 178, 34, 145, 143, 159, 194, 70, 94, 47, 101, 40} - copy(blsKey[:], blsKeyBinary) - copy(vrfKey[:], vrfKeyBinary) - privKey := composite.NewPrivKeyComposite(crypto.PrivKey(blsKey), crypto.PrivKey(vrfKey)) - - pubKey := privKey.PubKey() - address := pubKey.Address() - - sampleVote := types.Vote{} - sampleVote.Height = 1 - sampleVote.ValidatorIndex = 1 - sampleVote.BlockID = types.BlockID{ - Hash: address.Bytes(), - PartsHeader: types.PartSetHeader{Total: 1, Hash: address.Bytes()}} - sampleVote.Round = 0 - sampleVote.Timestamp = time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) - sampleVote.Type = types.PrevoteType - sampleVote.ValidatorAddress = address - - signBytes := sampleVote.SignBytes("chainid") - sampleVote.Signature, _ = privKey.Sign(signBytes) - t.Logf("pubKey: %X", pubKey.Bytes()) - t.Logf("vote: %X", sampleVote.SignBytes("chainid")) - t.Logf("vote signature: %X", sampleVote.Signature) - pubKeyBytes, _ := hex.DecodeString("D68FFBC130B25FCC05C88BE1D2CF5D53BC8E07A67C43F0FC1CD210A7E885FADA369B959F" + - "A74E7FF3ACF4BF9A96353D170B9D5C69701624DE642005F6D933BF348ACE9C099082A9D6708A3D29FD4CF2B222918F9FC2465E2F6528") - voteBytes, _ := hex.DecodeString("46080111010000000000000022300A14D9FB38E2F84230D6165FAD29072C9318EED18F5F12" + - "180A14D9FB38E2F84230D6165FAD29072C9318EED18F5F10013207636861696E6964") - signatureBytes, _ := hex.DecodeString("83AD3675D4AB4E0C3F9052BF29BE26A209258690DD89D619E64206A2FFFA7E3E8626D" + - "546340B44A1DCE4B39543630518123316C5A40EDFB3DE38015C6E1357C14DECD9A9A97949DAF40BDA755DC866AFD12963D85583C2E6" + - "49138BBCAA27A9B0") - assert.Equal(t, pubKey.Bytes(), pubKeyBytes) - assert.Equal(t, sampleVote.SignBytes("chainid"), voteBytes) - assert.Equal(t, sampleVote.Signature, signatureBytes) - verifying := pubKey.VerifyBytes(sampleVote.SignBytes("chainid"), sampleVote.Signature) - assert.True(t, verifying) -} - func TestEnvironmentalCompatibility(t *testing.T) { var cdc = amino.NewCodec() cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) From 9986ce583d435d424291cb55a21cd4707d744efa Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Mon, 28 Sep 2020 13:47:15 +0900 Subject: [PATCH 24/36] fix: force to use bls source of specific revision --- Makefile | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 90bba0c78..47703095a 100644 --- a/Makefile +++ b/Makefile @@ -231,7 +231,7 @@ DOCKER_CMD = docker run --rm \ -v `pwd`:$(DOCKER_HOME) \ -w $(DOCKER_HOME) DOCKER_IMG = golang:1.14.6-alpine3.12 -BUILD_CMD = apk add --update --no-cache git make gcc libc-dev build-base curl jq file gmp-dev clang nasm \ +BUILD_CMD = apk add --update --no-cache git make gcc libc-dev build-base curl jq file gmp-dev clang \ && cd crypto/bls/internal/bls-eth-go-binary \ && make CXX=clang++ \ && cd $(DOCKER_HOME) \ @@ -248,20 +248,27 @@ build-shell: build-linux: # Download, build and add the BSL local library to modules - rm -rf $(SRCPATH)/crypto/bls/internal/mcl - rm -rf $(SRCPATH)/crypto/bls/internal/bls - rm -rf $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary - git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ - git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ - git clone https://github.com/herumi/bls-eth-go-binary $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ + if [ ! -d $(SRCPATH)/crypto/bls/internal ]; then \ + mkdir -p $(SRCPATH)/crypto/bls/internal && \ + git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ + cd $(SRCPATH)/crypto/bls/internal/mcl && \ + git checkout d51fd79c86954a443b1c7ce67d7bcdb8a63ddedb && \ + cd .. && \ + git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ + cd $(SRCPATH)/crypto/bls/internal/bls && \ + git checkout accfc25c06c017f2686bb59c5106655293dd47d3 && \ + cd .. && \ + git clone https://github.com/herumi/bls-eth-go-binary $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary && \ + cd $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary && \ + git checkout 01d282b5380b0b0a59a880a2a645143807595ff3 && \ + cd ..; \ + fi # Build Linux binary $(DOCKER_CMD) ${DOCKER_IMG} /bin/sh -c "$(BUILD_CMD)" # Remove the BLS local library from modules - rm -rf $(SRCPATH)/crypto/bls/internal/mcl - rm -rf $(SRCPATH)/crypto/bls/internal/bls - rm -rf $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary + rm -rf $(SRCPATH)/crypto/bls/internal .PHONY: build-linux build-docker-localnode: From ccc1ac6517325279c8fe203ec1ec4f2e605d4c9a Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Mon, 28 Sep 2020 14:39:35 +0900 Subject: [PATCH 25/36] fix: increase circleci timeout to 10m --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 497bb1856..cb4124463 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -130,7 +130,7 @@ jobs: mkdir -p /tmp/logs /tmp/workspace/profiles for pkg in $(go list github.com/tendermint/tendermint/... | circleci tests split --split-by=timings); do id=$(basename "$pkg") - go test -v -timeout 5m -mod=readonly -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" + go test -v -timeout 10m -mod=readonly -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" done - persist_to_workspace: root: /tmp/workspace From c9d170cecc273a69d0074c2cf79e087599d0ff57 Mon Sep 17 00:00:00 2001 From: Satoshi Sakamori Date: Fri, 16 Oct 2020 17:30:25 +0900 Subject: [PATCH 26/36] fix compile error --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6cab37f78..9d3ade756 100644 --- a/Makefile +++ b/Makefile @@ -231,7 +231,7 @@ DOCKER_CMD = docker run --rm \ -v `pwd`:$(DOCKER_HOME) \ -w $(DOCKER_HOME) DOCKER_IMG = golang:1.14.6-alpine3.12 -BUILD_CMD = apk add --update --no-cache git make gcc libc-dev build-base curl jq file gmp-dev clang \ +BUILD_CMD = apk add --update --no-cache git make gcc nasm libc-dev build-base curl jq file gmp-dev clang \ && cd crypto/bls/internal/bls-eth-go-binary \ && make CXX=clang++ \ && cd $(DOCKER_HOME) \ @@ -252,7 +252,7 @@ build-linux: mkdir -p $(SRCPATH)/crypto/bls/internal && \ git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ - git clone https://github.com/herumi/bls-eth-go-binary $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ + git clone https://github.com/herumi/bls-eth-go-binary -b v1.12 --depth 1 $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ fi # Build Linux binary From 7abe3307c27f0ea7aac34fd87f4223f5546bc68f Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Mon, 19 Oct 2020 18:27:23 +0900 Subject: [PATCH 27/36] moved the signature aggregation to after signature verification in Commit --- consensus/invalid_test.go | 4 +- crypto/composite/composite_test.go | 12 +- proto/types/types.pb.go | 198 +++++++++++++++-------------- proto/types/types.proto | 9 +- types/block.go | 60 +++++---- types/vote_set.go | 12 -- types/voter_set.go | 10 +- 7 files changed, 157 insertions(+), 148 deletions(-) diff --git a/consensus/invalid_test.go b/consensus/invalid_test.go index 627240b3e..64a4774f7 100644 --- a/consensus/invalid_test.go +++ b/consensus/invalid_test.go @@ -82,7 +82,9 @@ func invalidDoPrevoteFunc(t *testing.T, height int64, round int, cs *State, sw * PartsHeader: types.PartSetHeader{Total: 1, Hash: tmrand.Bytes(32)}}, Signature: []byte{}, } - cs.privValidator.SignVote(cs.state.ChainID, precommit) + if err = cs.privValidator.SignVote(cs.state.ChainID, precommit); err != nil { + panic(err) + } cs.privValidator = nil // disable priv val so we don't do normal votes cs.mtx.Unlock() diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 795d3d528..c61aeaebc 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -214,9 +214,9 @@ func TestEnvironmentalCompatibility(t *testing.T) { if err != nil { t.Fatal(err) } - expected, err := hex.DecodeString("894d184a975aed6b442991e2ce60f7b39d6e810bde5ccb5d5d45dd6e461028339aa43" + - "2d027462f8ce7a5cf7db71ddabb0446cbb90ac817c26cc65614536cf99a9b739592dce467cc3d26bff63f5509157c2daa3b485" + - "afa3d2efab804c16f271d") + expected, err := hex.DecodeString("b0896e20be15f757686cec647bf86c97bc73f6607b730abb80828df110226498ba1c6" + + "ba9ccc645a7ee4a08b7db07e6d103699b40369475df592343b77d7e798f659b1f85eb9e237f870a1c98fa5e70e685e82ccf6de" + + "c4355e61450b5fa4ddb9b") if err != nil { t.Fatal(err) } @@ -272,9 +272,9 @@ func TestEnvironmentalCompatibility(t *testing.T) { if err != nil { t.Fatal(err) } - expectedSig, err := hex.DecodeString("A97BA8640BC3A1E05A2F4979D7FCF21DAA2A5D4AC64E16F76C6B1CBD646D6BF6EE6" + - "DF18B212CAE18D100A7CA0BC3EF8A17B3E7B82ED4105BEABA9F3B857B067E73D9251962B5BC4CF39B10547287DF8ADB9ED24AF7" + - "AA834114AE20B0BB6DD98D") + expectedSig, err := hex.DecodeString("9427431821a7db58542bf52289cfa1ffb087673541df3558f5a5d69633d27bb733" + + "7846befc581536d48b9a805309c9b811eecb92336b290e52ee5be83e081f788ee4c66a154c8a954f5330744058c44aaa95d810" + + "3fcddab0dfdda3b5a8551944") if err != nil { t.Fatal(err) } diff --git a/proto/types/types.pb.go b/proto/types/types.pb.go index 6b7c0c6e9..9c7a2cba5 100644 --- a/proto/types/types.pb.go +++ b/proto/types/types.pb.go @@ -536,16 +536,18 @@ func (m *Vote) GetSignature() []byte { // Commit contains the evidence that a block was committed by a set of validators. type Commit struct { - Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` - Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` - BlockID BlockID `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id"` - Signatures []CommitSig `protobuf:"bytes,4,rep,name=signatures,proto3" json:"signatures"` - AggregatedSignature []byte `protobuf:"bytes,5,opt,name=aggregated_signature,json=aggregatedSignature,proto3" json:"aggregated_signature,omitempty"` - Hash []byte `protobuf:"bytes,6,opt,name=hash,proto3" json:"hash,omitempty"` - BitArray *bits.BitArray `protobuf:"bytes,7,opt,name=bit_array,json=bitArray,proto3" json:"bit_array,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + BlockID BlockID `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id"` + Signatures []CommitSig `protobuf:"bytes,4,rep,name=signatures,proto3" json:"signatures"` + Hash []byte `protobuf:"bytes,5,opt,name=hash,proto3" json:"hash,omitempty"` + BitArray *bits.BitArray `protobuf:"bytes,6,opt,name=bit_array,json=bitArray,proto3" json:"bit_array,omitempty"` + // Additional field that LINE Blockchain uses for aggregated signature. For protobuf, the tags in the 1 to 15 is + // packed into a single byte, so we use 15 to account for differential expansions imported from Tendermint. + AggregatedSignature []byte `protobuf:"bytes,15,opt,name=aggregated_signature,json=aggregatedSignature,proto3" json:"aggregated_signature,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Commit) Reset() { *m = Commit{} } @@ -600,23 +602,23 @@ func (m *Commit) GetSignatures() []CommitSig { return nil } -func (m *Commit) GetAggregatedSignature() []byte { +func (m *Commit) GetHash() []byte { if m != nil { - return m.AggregatedSignature + return m.Hash } return nil } -func (m *Commit) GetHash() []byte { +func (m *Commit) GetBitArray() *bits.BitArray { if m != nil { - return m.Hash + return m.BitArray } return nil } -func (m *Commit) GetBitArray() *bits.BitArray { +func (m *Commit) GetAggregatedSignature() []byte { if m != nil { - return m.BitArray + return m.AggregatedSignature } return nil } @@ -897,87 +899,87 @@ func init() { func init() { proto.RegisterFile("proto/types/types.proto", fileDescriptor_ff06f8095857fb18) } var fileDescriptor_ff06f8095857fb18 = []byte{ - // 1308 bytes of a gzipped FileDescriptorProto + // 1307 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcb, 0x6e, 0xdb, 0x46, - 0x17, 0x36, 0x25, 0xea, 0x76, 0x28, 0xd9, 0x32, 0xe3, 0x3f, 0xd1, 0x2f, 0xb7, 0x96, 0x22, 0x37, - 0xa9, 0x73, 0x01, 0xd5, 0xba, 0x40, 0xd1, 0x00, 0xdd, 0x48, 0xb6, 0xe3, 0x08, 0xb1, 0x25, 0x81, - 0x52, 0xd3, 0xcb, 0x86, 0x18, 0x89, 0x13, 0x8a, 0x08, 0x45, 0x12, 0xe4, 0xc8, 0xb0, 0x52, 0xa0, - 0xeb, 0xc2, 0xab, 0xbe, 0x80, 0x37, 0xbd, 0x00, 0x7d, 0x8b, 0x76, 0xd9, 0x55, 0x97, 0x5d, 0xa6, - 0x40, 0xfa, 0x0a, 0x7d, 0x80, 0x62, 0x2e, 0xa4, 0xa4, 0xc8, 0x6e, 0x8d, 0x26, 0xdd, 0x24, 0x9c, - 0x73, 0xbe, 0x73, 0xe6, 0x9c, 0x6f, 0xbe, 0x33, 0x23, 0xc3, 0x0d, 0x3f, 0xf0, 0x88, 0x57, 0x27, - 0x53, 0x1f, 0x87, 0xfc, 0x5f, 0x8d, 0x59, 0xd4, 0xeb, 0x04, 0xbb, 0x26, 0x0e, 0xc6, 0xb6, 0x4b, - 0xb8, 0x45, 0x63, 0xde, 0xf2, 0x6d, 0x32, 0xb2, 0x03, 0xd3, 0xf0, 0x51, 0x40, 0xa6, 0x75, 0x1e, - 0x6c, 0x79, 0x96, 0x37, 0xfb, 0xe2, 0xe8, 0x72, 0xc5, 0xf2, 0x3c, 0xcb, 0xc1, 0x1c, 0x32, 0x98, - 0x3c, 0xad, 0x13, 0x7b, 0x8c, 0x43, 0x82, 0xc6, 0xbe, 0x00, 0x6c, 0xf2, 0x10, 0xc7, 0x1e, 0x84, - 0xf5, 0x81, 0x4d, 0x16, 0x76, 0x2f, 0x57, 0xb8, 0x73, 0x18, 0x4c, 0x7d, 0xe2, 0xd5, 0xc7, 0x38, - 0x78, 0xe6, 0xe0, 0x05, 0x80, 0x88, 0x3e, 0xc1, 0x41, 0x68, 0x7b, 0x6e, 0xf4, 0x3f, 0x77, 0xd6, - 0x1e, 0x40, 0xa1, 0x8b, 0x02, 0xd2, 0xc3, 0xe4, 0x11, 0x46, 0x26, 0x0e, 0xd4, 0x0d, 0x48, 0x11, - 0x8f, 0x20, 0xa7, 0x24, 0x55, 0xa5, 0x9d, 0xa4, 0xce, 0x17, 0xaa, 0x0a, 0xf2, 0x08, 0x85, 0xa3, - 0x52, 0xa2, 0x2a, 0xed, 0xe4, 0x75, 0xf6, 0x5d, 0xfb, 0x12, 0x64, 0x1a, 0x4a, 0x23, 0x6c, 0xd7, - 0xc4, 0xa7, 0x2c, 0xa2, 0xa0, 0xf3, 0x05, 0xb5, 0x0e, 0xa6, 0x04, 0x87, 0x22, 0x84, 0x2f, 0xd4, - 0x43, 0x48, 0xf9, 0x81, 0xe7, 0x3d, 0x2d, 0x25, 0xab, 0xd2, 0x8e, 0xb2, 0x7b, 0x4f, 0x5b, 0xa2, - 0x8e, 0xf7, 0xa1, 0xf1, 0x3e, 0xb4, 0x9e, 0x3d, 0xf6, 0x1d, 0xdc, 0xa5, 0x21, 0x4d, 0xf9, 0x97, - 0x17, 0x95, 0x15, 0x9d, 0xc7, 0xd7, 0xc6, 0x90, 0x69, 0x3a, 0xde, 0xf0, 0x59, 0x6b, 0x3f, 0xae, - 0x4d, 0x9a, 0xd5, 0xa6, 0xb6, 0x21, 0x4f, 0x69, 0x0f, 0x8d, 0x11, 0xeb, 0x8a, 0x15, 0xa1, 0xec, - 0xde, 0xd2, 0x2e, 0x3e, 0x29, 0x6d, 0x81, 0x02, 0xb1, 0x91, 0xc2, 0x12, 0x70, 0x53, 0xed, 0xdb, - 0x14, 0xa4, 0x05, 0x41, 0x7b, 0x90, 0x11, 0x14, 0xb2, 0x1d, 0x95, 0xdd, 0xed, 0xe5, 0xac, 0x11, - 0xc7, 0x7b, 0x9e, 0x1b, 0x62, 0x37, 0x9c, 0x84, 0x22, 0x67, 0x14, 0xa9, 0xde, 0x86, 0xec, 0x70, - 0x84, 0x6c, 0xd7, 0xb0, 0x4d, 0x56, 0x5b, 0xae, 0xa9, 0xbc, 0x7c, 0x51, 0xc9, 0xec, 0x51, 0x5b, - 0x6b, 0x5f, 0xcf, 0x30, 0x67, 0xcb, 0x54, 0xaf, 0x43, 0x7a, 0x84, 0x6d, 0x6b, 0x44, 0x18, 0x61, - 0x49, 0x5d, 0xac, 0xd4, 0x8f, 0x40, 0xa6, 0x22, 0x29, 0xc9, 0xac, 0x82, 0xb2, 0xc6, 0x15, 0xa4, - 0x45, 0x0a, 0xd2, 0xfa, 0x91, 0x82, 0x9a, 0x59, 0xba, 0xf1, 0x37, 0xbf, 0x57, 0x24, 0x9d, 0x45, - 0xa8, 0x9f, 0x41, 0xc1, 0x41, 0x21, 0x31, 0x06, 0x94, 0x3d, 0xba, 0x7d, 0x8a, 0xa5, 0xa8, 0x5c, - 0x46, 0x8d, 0x60, 0xb9, 0x79, 0x8d, 0xe6, 0x79, 0xf9, 0xa2, 0xa2, 0x1c, 0xa1, 0x90, 0x08, 0xa3, - 0xae, 0x38, 0xf1, 0xc2, 0x54, 0x77, 0xa0, 0xc8, 0x32, 0x0f, 0xbd, 0xf1, 0xd8, 0x26, 0x06, 0x3b, - 0x93, 0x34, 0x3b, 0x93, 0x55, 0x6a, 0xdf, 0x63, 0xe6, 0x47, 0xf4, 0x74, 0x36, 0x21, 0x67, 0x22, - 0x82, 0x38, 0x24, 0xc3, 0x20, 0x59, 0x6a, 0x60, 0xce, 0x0a, 0x28, 0x27, 0x1e, 0xc1, 0x41, 0xc8, - 0xdd, 0x59, 0xe6, 0x06, 0x6e, 0x62, 0x80, 0x77, 0x61, 0xed, 0x04, 0x39, 0xb6, 0x89, 0x88, 0x17, - 0x81, 0x72, 0x7c, 0x9b, 0x99, 0x99, 0x01, 0xdf, 0x83, 0x0d, 0x17, 0x9f, 0x12, 0xe3, 0x55, 0x34, - 0x30, 0xb4, 0x4a, 0x7d, 0x4f, 0x16, 0x23, 0x6e, 0xc1, 0xea, 0x30, 0x3a, 0x32, 0x8e, 0x55, 0x18, - 0xb6, 0x10, 0x5b, 0x19, 0xec, 0xff, 0x90, 0x45, 0xbe, 0xcf, 0x01, 0x79, 0x06, 0xc8, 0x20, 0xdf, - 0x67, 0xae, 0xbb, 0xb0, 0xce, 0x48, 0x08, 0x70, 0x38, 0x71, 0x88, 0x48, 0x52, 0x60, 0x98, 0x35, - 0xea, 0xd0, 0xb9, 0x9d, 0x61, 0xb7, 0xa1, 0x80, 0x4f, 0x6c, 0x13, 0xbb, 0x43, 0xcc, 0x71, 0xab, - 0x0c, 0x97, 0x8f, 0x8c, 0x0c, 0x74, 0x07, 0x8a, 0x7e, 0xe0, 0xf9, 0x5e, 0x88, 0x03, 0x03, 0x99, - 0x66, 0x80, 0xc3, 0xb0, 0xb4, 0xc6, 0xf3, 0x45, 0xf6, 0x06, 0x37, 0xd7, 0xee, 0x83, 0xbc, 0x8f, - 0x08, 0x52, 0x8b, 0x90, 0x24, 0xa7, 0x61, 0x49, 0xaa, 0x26, 0x77, 0xf2, 0x3a, 0xfd, 0xbc, 0x70, - 0x7c, 0xff, 0x4c, 0x80, 0xfc, 0xc4, 0x23, 0x58, 0x7d, 0x00, 0x32, 0x3d, 0x6a, 0xa6, 0xe6, 0xd5, - 0xcb, 0x67, 0xa4, 0x67, 0x5b, 0x2e, 0x36, 0x8f, 0x43, 0xab, 0x3f, 0xf5, 0xb1, 0xce, 0x42, 0xe6, - 0xe4, 0x99, 0x58, 0x90, 0xe7, 0x06, 0xa4, 0x02, 0x6f, 0xe2, 0x9a, 0x42, 0xb5, 0x7c, 0xa1, 0x3e, - 0x86, 0x6c, 0xac, 0x3a, 0xf9, 0x6a, 0xaa, 0x5b, 0x13, 0xaa, 0x8b, 0x86, 0x5d, 0xcf, 0x0c, 0x84, - 0xda, 0x9a, 0x90, 0x8b, 0xaf, 0x49, 0xa1, 0xe1, 0xab, 0x8d, 0xc1, 0x2c, 0x4c, 0xbd, 0x07, 0xeb, - 0xb1, 0x36, 0x62, 0x72, 0xb9, 0x64, 0x8b, 0xb1, 0x43, 0xb0, 0xbb, 0x20, 0x3b, 0x83, 0x5f, 0x78, - 0x19, 0xd6, 0xdd, 0x4c, 0x76, 0x2d, 0x76, 0xf3, 0xbd, 0x05, 0xb9, 0xd0, 0xb6, 0x5c, 0x44, 0x26, - 0x01, 0x16, 0xf2, 0x9d, 0x19, 0x6a, 0xbf, 0x25, 0x20, 0xcd, 0x47, 0x61, 0x8e, 0x3d, 0xe9, 0x62, - 0xf6, 0x28, 0xa9, 0xa9, 0x8b, 0xd8, 0x4b, 0xbe, 0x2e, 0x7b, 0x87, 0x00, 0x71, 0x49, 0x61, 0x49, - 0xae, 0x26, 0x77, 0x94, 0xdd, 0x9b, 0x97, 0xa5, 0xe3, 0xe5, 0xf6, 0x6c, 0x4b, 0xdc, 0x62, 0x73, - 0xa1, 0xea, 0xfb, 0xb0, 0x81, 0x2c, 0x2b, 0xc0, 0x16, 0x22, 0xd8, 0x34, 0x66, 0x7d, 0xa7, 0x58, - 0xdf, 0xd7, 0x66, 0xbe, 0x5e, 0xe4, 0x8a, 0xc5, 0x98, 0x9e, 0xbb, 0xaf, 0x1b, 0x90, 0x1b, 0xd8, - 0xc4, 0x40, 0x41, 0x80, 0xa6, 0x8c, 0x56, 0x65, 0xf7, 0x9d, 0xe5, 0x72, 0xe8, 0x03, 0xa8, 0xd1, - 0x07, 0x50, 0x6b, 0xda, 0xa4, 0x41, 0xb1, 0x7a, 0x76, 0x20, 0xbe, 0x6a, 0x7f, 0x48, 0x90, 0x8b, - 0x2b, 0x55, 0x0f, 0xa1, 0x10, 0xb1, 0x65, 0x3c, 0x75, 0x90, 0x25, 0xd4, 0xbd, 0xfd, 0x0f, 0x94, - 0x3d, 0x74, 0x90, 0xa5, 0x2b, 0x82, 0x25, 0xba, 0xb8, 0x58, 0x23, 0x89, 0x4b, 0x34, 0xb2, 0x20, - 0xca, 0xe4, 0xbf, 0x13, 0xe5, 0x82, 0x7c, 0xe4, 0x57, 0xe5, 0xf3, 0x53, 0x02, 0xb2, 0x5d, 0x36, - 0xf7, 0xc8, 0xf9, 0xcf, 0x27, 0x37, 0xd6, 0xde, 0x26, 0xe4, 0x7c, 0xcf, 0x31, 0xb8, 0x47, 0x66, - 0x9e, 0xac, 0xef, 0x39, 0xfa, 0x92, 0x30, 0x53, 0x6f, 0x74, 0xac, 0xd3, 0x6f, 0x80, 0xc1, 0xcc, - 0xab, 0x0c, 0x7e, 0x05, 0x79, 0x4e, 0x88, 0x78, 0xcf, 0x3f, 0xa4, 0x4c, 0xb0, 0x1f, 0x09, 0xfc, - 0x39, 0xdf, 0xba, 0xac, 0x78, 0x8e, 0xd7, 0x05, 0x9a, 0xc6, 0xf1, 0x97, 0x4e, 0xfc, 0xb8, 0xd8, - 0xfa, 0xfb, 0xf1, 0xd1, 0x05, 0xba, 0xf6, 0xab, 0x04, 0x39, 0xd6, 0xf6, 0x31, 0x26, 0x68, 0x81, - 0x3c, 0xe9, 0x75, 0xc9, 0x7b, 0x1b, 0x80, 0x27, 0x0b, 0xed, 0xe7, 0x58, 0x1c, 0x6c, 0x8e, 0x59, - 0x7a, 0xf6, 0x73, 0xac, 0x7e, 0x1c, 0x77, 0x9a, 0xbc, 0x4a, 0xa7, 0x62, 0xda, 0xa3, 0x7e, 0x6f, - 0x40, 0xc6, 0x9d, 0x8c, 0x0d, 0xfa, 0xb2, 0xc8, 0x5c, 0x32, 0xee, 0x64, 0xdc, 0x3f, 0x0d, 0xef, - 0xfe, 0x2c, 0x81, 0x32, 0x37, 0x3e, 0x6a, 0x19, 0xae, 0x37, 0x8f, 0x3a, 0x7b, 0x8f, 0xf7, 0x8d, - 0xd6, 0xbe, 0xf1, 0xf0, 0xa8, 0x71, 0x68, 0x7c, 0xd2, 0x7e, 0xdc, 0xee, 0x7c, 0xda, 0x2e, 0xae, - 0xa8, 0x75, 0xd8, 0x60, 0xbe, 0xd8, 0xd5, 0x68, 0xf6, 0x0e, 0xda, 0xfd, 0xa2, 0x54, 0xfe, 0xdf, - 0xd9, 0x79, 0x75, 0x7d, 0x2e, 0x4d, 0x63, 0x10, 0x62, 0x97, 0x2c, 0x07, 0xec, 0x75, 0x8e, 0x8f, - 0x5b, 0xfd, 0x62, 0x62, 0x29, 0x40, 0x5c, 0xaa, 0x77, 0x60, 0x7d, 0x31, 0xa0, 0xdd, 0x3a, 0x2a, - 0x26, 0xcb, 0xea, 0xd9, 0x79, 0x75, 0x75, 0x0e, 0xdd, 0xb6, 0x9d, 0x72, 0xf6, 0xeb, 0xef, 0xb6, - 0x56, 0x7e, 0xfc, 0x7e, 0x6b, 0xe5, 0xee, 0x0f, 0x12, 0x14, 0x16, 0xa6, 0x44, 0xdd, 0x84, 0x1b, - 0xbd, 0xd6, 0x61, 0xfb, 0x60, 0xdf, 0x38, 0xee, 0x1d, 0x1a, 0xfd, 0xcf, 0xbb, 0x07, 0x73, 0x5d, - 0xdc, 0x84, 0x7c, 0x57, 0x3f, 0x78, 0xd2, 0xe9, 0x1f, 0x30, 0x4f, 0x51, 0x2a, 0xaf, 0x9d, 0x9d, - 0x57, 0x95, 0x6e, 0x80, 0xe9, 0xcf, 0x14, 0x16, 0x7f, 0x0b, 0x56, 0xbb, 0xfa, 0x01, 0x2f, 0x96, - 0x83, 0x12, 0xe5, 0xf5, 0xb3, 0xf3, 0x6a, 0xa1, 0x1b, 0x60, 0x2e, 0x04, 0x06, 0xdb, 0x86, 0x42, - 0x57, 0xef, 0x74, 0x3b, 0xbd, 0xc6, 0x11, 0x47, 0x25, 0xcb, 0xc5, 0xb3, 0xf3, 0x6a, 0x3e, 0x1a, - 0x71, 0x0a, 0x9a, 0xd5, 0xd9, 0xd4, 0xbe, 0xb8, 0x6f, 0xd9, 0x64, 0x34, 0x19, 0x68, 0x43, 0x6f, - 0x5c, 0x9f, 0x9d, 0xde, 0xfc, 0xe7, 0xdc, 0x5f, 0x29, 0x83, 0x34, 0x5b, 0x7c, 0xf0, 0x57, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x16, 0x51, 0x97, 0x28, 0xbb, 0x0c, 0x00, 0x00, + 0x17, 0x36, 0x25, 0xca, 0x92, 0x0e, 0x25, 0x5b, 0x66, 0xfc, 0x27, 0xfa, 0xe5, 0xd6, 0x52, 0xe4, + 0x26, 0x75, 0x2e, 0xa0, 0x5a, 0x17, 0x28, 0x1a, 0xa0, 0x1b, 0xc9, 0x76, 0x1c, 0x21, 0xb6, 0x2c, + 0x50, 0x6a, 0x7a, 0xd9, 0x10, 0x23, 0x71, 0x42, 0x11, 0xa1, 0x48, 0x82, 0x33, 0x32, 0xec, 0x14, + 0xe8, 0xba, 0xf0, 0xaa, 0x2f, 0xe0, 0x4d, 0x2f, 0x40, 0xdf, 0xa2, 0x5d, 0x76, 0xd5, 0x65, 0x97, + 0x29, 0x90, 0xbe, 0x42, 0x1f, 0xa0, 0x98, 0x0b, 0x29, 0x29, 0xb6, 0x5b, 0xa3, 0x49, 0x37, 0xf6, + 0xcc, 0x39, 0xdf, 0x39, 0x73, 0xe6, 0x9b, 0xef, 0xcc, 0x50, 0x70, 0x23, 0x8c, 0x02, 0x1a, 0x34, + 0xe8, 0x49, 0x88, 0x89, 0xf8, 0x6b, 0x70, 0x8b, 0x7e, 0x9d, 0x62, 0xdf, 0xc6, 0xd1, 0xd8, 0xf5, + 0xa9, 0xb0, 0x18, 0xdc, 0x5b, 0xb9, 0x4d, 0x47, 0x6e, 0x64, 0x5b, 0x21, 0x8a, 0xe8, 0x49, 0x43, + 0x04, 0x3b, 0x81, 0x13, 0x4c, 0x47, 0x02, 0x5d, 0xa9, 0x3a, 0x41, 0xe0, 0x78, 0x58, 0x40, 0x06, + 0x93, 0xa7, 0x0d, 0xea, 0x8e, 0x31, 0xa1, 0x68, 0x1c, 0x4a, 0xc0, 0x9a, 0x08, 0xf1, 0xdc, 0x01, + 0x69, 0x0c, 0x5c, 0x3a, 0xb7, 0x7a, 0xa5, 0x2a, 0x9c, 0xc3, 0xe8, 0x24, 0xa4, 0x41, 0x63, 0x8c, + 0xa3, 0x67, 0x1e, 0x9e, 0x03, 0xc8, 0xe8, 0x23, 0x1c, 0x11, 0x37, 0xf0, 0xe3, 0xff, 0xc2, 0x59, + 0x7f, 0x00, 0xc5, 0x2e, 0x8a, 0x68, 0x0f, 0xd3, 0x47, 0x18, 0xd9, 0x38, 0xd2, 0x57, 0x21, 0x43, + 0x03, 0x8a, 0xbc, 0xb2, 0x52, 0x53, 0x36, 0xd3, 0xa6, 0x98, 0xe8, 0x3a, 0xa8, 0x23, 0x44, 0x46, + 0xe5, 0x54, 0x4d, 0xd9, 0x2c, 0x98, 0x7c, 0x5c, 0xff, 0x12, 0x54, 0x16, 0xca, 0x22, 0x5c, 0xdf, + 0xc6, 0xc7, 0x3c, 0xa2, 0x68, 0x8a, 0x09, 0xb3, 0x0e, 0x4e, 0x28, 0x26, 0x32, 0x44, 0x4c, 0xf4, + 0x3d, 0xc8, 0x84, 0x51, 0x10, 0x3c, 0x2d, 0xa7, 0x6b, 0xca, 0xa6, 0xb6, 0x75, 0xcf, 0x38, 0x47, + 0x9d, 0xd8, 0x87, 0x21, 0xf6, 0x61, 0xf4, 0xdc, 0x71, 0xe8, 0xe1, 0x2e, 0x0b, 0x69, 0xa9, 0xbf, + 0xbc, 0xa8, 0x2e, 0x98, 0x22, 0xbe, 0x3e, 0x86, 0x6c, 0xcb, 0x0b, 0x86, 0xcf, 0xda, 0x3b, 0x49, + 0x6d, 0xca, 0xb4, 0x36, 0xbd, 0x03, 0x05, 0x46, 0x3b, 0xb1, 0x46, 0x7c, 0x57, 0xbc, 0x08, 0x6d, + 0xeb, 0x96, 0x71, 0xf1, 0x49, 0x19, 0x73, 0x14, 0xc8, 0x85, 0x34, 0x9e, 0x40, 0x98, 0xea, 0xdf, + 0x66, 0x60, 0x51, 0x12, 0xb4, 0x0d, 0x59, 0x49, 0x21, 0x5f, 0x51, 0xdb, 0xda, 0x38, 0x9f, 0x35, + 0xe6, 0x78, 0x3b, 0xf0, 0x09, 0xf6, 0xc9, 0x84, 0xc8, 0x9c, 0x71, 0xa4, 0x7e, 0x1b, 0x72, 0xc3, + 0x11, 0x72, 0x7d, 0xcb, 0xb5, 0x79, 0x6d, 0xf9, 0x96, 0xf6, 0xf2, 0x45, 0x35, 0xbb, 0xcd, 0x6c, + 0xed, 0x1d, 0x33, 0xcb, 0x9d, 0x6d, 0x5b, 0xbf, 0x0e, 0x8b, 0x23, 0xec, 0x3a, 0x23, 0xca, 0x09, + 0x4b, 0x9b, 0x72, 0xa6, 0x7f, 0x04, 0x2a, 0x13, 0x49, 0x59, 0xe5, 0x15, 0x54, 0x0c, 0xa1, 0x20, + 0x23, 0x56, 0x90, 0xd1, 0x8f, 0x15, 0xd4, 0xca, 0xb1, 0x85, 0xbf, 0xf9, 0xbd, 0xaa, 0x98, 0x3c, + 0x42, 0xff, 0x0c, 0x8a, 0x1e, 0x22, 0xd4, 0x1a, 0x30, 0xf6, 0xd8, 0xf2, 0x19, 0x9e, 0xa2, 0x7a, + 0x19, 0x35, 0x92, 0xe5, 0xd6, 0x35, 0x96, 0xe7, 0xe5, 0x8b, 0xaa, 0xb6, 0x8f, 0x08, 0x95, 0x46, + 0x53, 0xf3, 0x92, 0x89, 0xad, 0x6f, 0x42, 0x89, 0x67, 0x1e, 0x06, 0xe3, 0xb1, 0x4b, 0x2d, 0x7e, + 0x26, 0x8b, 0xfc, 0x4c, 0x96, 0x98, 0x7d, 0x9b, 0x9b, 0x1f, 0xb1, 0xd3, 0x59, 0x83, 0xbc, 0x8d, + 0x28, 0x12, 0x90, 0x2c, 0x87, 0xe4, 0x98, 0x81, 0x3b, 0xab, 0xa0, 0x1d, 0x05, 0x14, 0x47, 0x44, + 0xb8, 0x73, 0xdc, 0x0d, 0xc2, 0xc4, 0x01, 0xef, 0xc2, 0xf2, 0x11, 0xf2, 0x5c, 0x1b, 0xd1, 0x20, + 0x06, 0xe5, 0xc5, 0x32, 0x53, 0x33, 0x07, 0xbe, 0x07, 0xab, 0x3e, 0x3e, 0xa6, 0xd6, 0xab, 0x68, + 0xe0, 0x68, 0x9d, 0xf9, 0x9e, 0xcc, 0x47, 0xdc, 0x82, 0xa5, 0x61, 0x7c, 0x64, 0x02, 0xab, 0x71, + 0x6c, 0x31, 0xb1, 0x72, 0xd8, 0xff, 0x21, 0x87, 0xc2, 0x50, 0x00, 0x0a, 0x1c, 0x90, 0x45, 0x61, + 0xc8, 0x5d, 0x77, 0x61, 0x85, 0x93, 0x10, 0x61, 0x32, 0xf1, 0xa8, 0x4c, 0x52, 0xe4, 0x98, 0x65, + 0xe6, 0x30, 0x85, 0x9d, 0x63, 0x37, 0xa0, 0x88, 0x8f, 0x5c, 0x1b, 0xfb, 0x43, 0x2c, 0x70, 0x4b, + 0x1c, 0x57, 0x88, 0x8d, 0x1c, 0x74, 0x07, 0x4a, 0x61, 0x14, 0x84, 0x01, 0xc1, 0x91, 0x85, 0x6c, + 0x3b, 0xc2, 0x84, 0x94, 0x97, 0x45, 0xbe, 0xd8, 0xde, 0x14, 0xe6, 0xfa, 0x7d, 0x50, 0x77, 0x10, + 0x45, 0x7a, 0x09, 0xd2, 0xf4, 0x98, 0x94, 0x95, 0x5a, 0x7a, 0xb3, 0x60, 0xb2, 0xe1, 0x85, 0xed, + 0xfb, 0x67, 0x0a, 0xd4, 0x27, 0x01, 0xc5, 0xfa, 0x03, 0x50, 0xd9, 0x51, 0x73, 0x35, 0x2f, 0x5d, + 0xde, 0x23, 0x3d, 0xd7, 0xf1, 0xb1, 0x7d, 0x40, 0x9c, 0xfe, 0x49, 0x88, 0x4d, 0x1e, 0x32, 0x23, + 0xcf, 0xd4, 0x9c, 0x3c, 0x57, 0x21, 0x13, 0x05, 0x13, 0xdf, 0x96, 0xaa, 0x15, 0x13, 0xfd, 0x31, + 0xe4, 0x12, 0xd5, 0xa9, 0x57, 0x53, 0xdd, 0xb2, 0x54, 0x5d, 0xdc, 0xec, 0x66, 0x76, 0x20, 0xd5, + 0xd6, 0x82, 0x7c, 0x72, 0x4d, 0x4a, 0x0d, 0x5f, 0xad, 0x0d, 0xa6, 0x61, 0xfa, 0x3d, 0x58, 0x49, + 0xb4, 0x91, 0x90, 0x2b, 0x24, 0x5b, 0x4a, 0x1c, 0x92, 0xdd, 0x39, 0xd9, 0x59, 0xe2, 0xc2, 0xcb, + 0xf2, 0xdd, 0x4d, 0x65, 0xd7, 0xe6, 0x37, 0xdf, 0x5b, 0x90, 0x27, 0xae, 0xe3, 0x23, 0x3a, 0x89, + 0xb0, 0x94, 0xef, 0xd4, 0x50, 0xff, 0x2d, 0x05, 0x8b, 0xa2, 0x15, 0x66, 0xd8, 0x53, 0x2e, 0x66, + 0x8f, 0x91, 0x9a, 0xb9, 0x88, 0xbd, 0xf4, 0xeb, 0xb2, 0xb7, 0x07, 0x90, 0x94, 0x44, 0xca, 0x6a, + 0x2d, 0xbd, 0xa9, 0x6d, 0xdd, 0xbc, 0x2c, 0x9d, 0x28, 0xb7, 0xe7, 0x3a, 0xf2, 0x16, 0x9b, 0x09, + 0x4d, 0x94, 0x95, 0x99, 0xb9, 0x7c, 0x9b, 0x90, 0x1f, 0xb8, 0xd4, 0x42, 0x51, 0x84, 0x4e, 0x38, + 0x9d, 0xda, 0xd6, 0x3b, 0xe7, 0x73, 0xb3, 0xd7, 0xcc, 0x60, 0xaf, 0x99, 0xd1, 0x72, 0x69, 0x93, + 0x61, 0xcd, 0xdc, 0x40, 0x8e, 0xf4, 0xf7, 0x61, 0x15, 0x39, 0x4e, 0x84, 0x1d, 0x44, 0xb1, 0x6d, + 0x4d, 0xe9, 0x14, 0xca, 0xbf, 0x36, 0xf5, 0xf5, 0x12, 0x62, 0xff, 0x50, 0x20, 0x9f, 0x54, 0xaa, + 0xef, 0x41, 0x31, 0x66, 0xcb, 0x7a, 0xea, 0x21, 0x47, 0xaa, 0x7b, 0xe3, 0x1f, 0x28, 0x7b, 0xe8, + 0x21, 0xc7, 0xd4, 0x24, 0x4b, 0x6c, 0x72, 0xb1, 0x46, 0x52, 0x97, 0x68, 0x64, 0x4e, 0x94, 0xe9, + 0x7f, 0x27, 0xca, 0x39, 0xf9, 0xa8, 0xaf, 0xca, 0xe7, 0xa7, 0x14, 0xe4, 0xba, 0xbc, 0xef, 0x91, + 0xf7, 0x9f, 0x77, 0x6e, 0xa2, 0xbd, 0x35, 0xc8, 0x87, 0x81, 0x67, 0x09, 0x8f, 0xca, 0x3d, 0xb9, + 0x30, 0xf0, 0xcc, 0x73, 0xc2, 0xcc, 0xbc, 0xd1, 0xb6, 0x5e, 0x7c, 0x03, 0x0c, 0x66, 0x5f, 0x65, + 0xf0, 0x2b, 0x28, 0x08, 0x42, 0xe4, 0x7b, 0xfe, 0x21, 0x63, 0x82, 0x7f, 0x24, 0x88, 0xe7, 0x7c, + 0xfd, 0xb2, 0xe2, 0x05, 0xde, 0x94, 0x68, 0x16, 0x27, 0x5e, 0x3a, 0xf9, 0x71, 0xb1, 0xfe, 0xf7, + 0xed, 0x63, 0x4a, 0x74, 0xfd, 0x57, 0x05, 0xf2, 0x7c, 0xdb, 0x07, 0x98, 0xa2, 0x39, 0xf2, 0x94, + 0xd7, 0x25, 0xef, 0x6d, 0x00, 0x91, 0x8c, 0xb8, 0xcf, 0xb1, 0x3c, 0xd8, 0x3c, 0xb7, 0xf4, 0xdc, + 0xe7, 0x58, 0xff, 0x38, 0xd9, 0x69, 0xfa, 0x2a, 0x3b, 0x95, 0xdd, 0x1e, 0xef, 0xf7, 0x06, 0x64, + 0xfd, 0xc9, 0xd8, 0x62, 0x2f, 0x8b, 0x2a, 0x24, 0xe3, 0x4f, 0xc6, 0xfd, 0x63, 0x72, 0xf7, 0x67, + 0x05, 0xb4, 0x99, 0xf6, 0xd1, 0x2b, 0x70, 0xbd, 0xb5, 0x7f, 0xb8, 0xfd, 0x78, 0xc7, 0x6a, 0xef, + 0x58, 0x0f, 0xf7, 0x9b, 0x7b, 0xd6, 0x27, 0x9d, 0xc7, 0x9d, 0xc3, 0x4f, 0x3b, 0xa5, 0x05, 0xbd, + 0x01, 0xab, 0xdc, 0x97, 0xb8, 0x9a, 0xad, 0xde, 0x6e, 0xa7, 0x5f, 0x52, 0x2a, 0xff, 0x3b, 0x3d, + 0xab, 0xad, 0xcc, 0xa4, 0x69, 0x0e, 0x08, 0xf6, 0xe9, 0xf9, 0x80, 0xed, 0xc3, 0x83, 0x83, 0x76, + 0xbf, 0x94, 0x3a, 0x17, 0x20, 0x2f, 0xd5, 0x3b, 0xb0, 0x32, 0x1f, 0xd0, 0x69, 0xef, 0x97, 0xd2, + 0x15, 0xfd, 0xf4, 0xac, 0xb6, 0x34, 0x83, 0xee, 0xb8, 0x5e, 0x25, 0xf7, 0xf5, 0x77, 0xeb, 0x0b, + 0x3f, 0x7e, 0xbf, 0xbe, 0x70, 0xf7, 0x07, 0x05, 0x8a, 0x73, 0x5d, 0xa2, 0xaf, 0xc1, 0x8d, 0x5e, + 0x7b, 0xaf, 0xb3, 0xbb, 0x63, 0x1d, 0xf4, 0xf6, 0xac, 0xfe, 0xe7, 0xdd, 0xdd, 0x99, 0x5d, 0xdc, + 0x84, 0x42, 0xd7, 0xdc, 0x7d, 0x72, 0xd8, 0xdf, 0xe5, 0x9e, 0x92, 0x52, 0x59, 0x3e, 0x3d, 0xab, + 0x69, 0xdd, 0x08, 0xb3, 0xcf, 0x14, 0x1e, 0x7f, 0x0b, 0x96, 0xba, 0xe6, 0xae, 0x28, 0x56, 0x80, + 0x52, 0x95, 0x95, 0xd3, 0xb3, 0x5a, 0xb1, 0x1b, 0x61, 0x21, 0x04, 0x0e, 0xdb, 0x80, 0x62, 0xd7, + 0x3c, 0xec, 0x1e, 0xf6, 0x9a, 0xfb, 0x02, 0x95, 0xae, 0x94, 0x4e, 0xcf, 0x6a, 0x85, 0xb8, 0xc5, + 0x19, 0x68, 0x5a, 0x67, 0xcb, 0xf8, 0xe2, 0xbe, 0xe3, 0xd2, 0xd1, 0x64, 0x60, 0x0c, 0x83, 0x71, + 0x63, 0x7a, 0x7a, 0xb3, 0xc3, 0x99, 0x5f, 0x29, 0x83, 0x45, 0x3e, 0xf9, 0xe0, 0xaf, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x42, 0x13, 0xc4, 0x21, 0xbb, 0x0c, 0x00, 0x00, } diff --git a/proto/types/types.proto b/proto/types/types.proto index 6fe888c5c..bf365ccff 100644 --- a/proto/types/types.proto +++ b/proto/types/types.proto @@ -108,9 +108,12 @@ message Commit { int32 round = 2; BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; - bytes aggregated_signature = 5; - bytes hash = 6; - tendermint.proto.libs.bits.BitArray bit_array = 7; + bytes hash = 5; + tendermint.proto.libs.bits.BitArray bit_array = 6; + + // Additional field that LINE Blockchain uses for aggregated signature. For protobuf, the tags in the 1 to 15 is + // packed into a single byte, so we use 15 to account for differential expansions imported from Tendermint. + bytes aggregated_signature = 15; } // CommitSig is a part of the Vote included in a Commit. diff --git a/types/block.go b/types/block.go index 873652d96..38bfa561c 100644 --- a/types/block.go +++ b/types/block.go @@ -554,7 +554,9 @@ type CommitSig struct { BlockIDFlag BlockIDFlag `json:"block_id_flag"` ValidatorAddress Address `json:"validator_address"` Timestamp time.Time `json:"timestamp"` - Signature []byte `json:"signature"` + + // This can take a nil in case when the signature is being aggregated. + Signature []byte `json:"signature"` } // NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit. @@ -703,29 +705,8 @@ func NewCommit(height int64, round int, blockID BlockID, commitSigs []CommitSig) } // NewCommitWithAggregatedSignature returns a new Commit with . -// The aggrSig can be set to null. Then all commitSigs must have a valid signature. func NewCommitWithAggregatedSignature( height int64, round int, blockID BlockID, commitSigs []CommitSig, aggrSig []byte) *Commit { - - // Make a copy of CommitSig to avoid side effects - newCommitSigs := make([]CommitSig, len(commitSigs)) - copy(newCommitSigs, commitSigs) - commitSigs = newCommitSigs - - // Create aggregate signature and reset per-CommitSig signature if possible - for i := range commitSigs { - if commitSigs[i].Absent() { - continue - } - curAggrSig, err := bls.AddSignature(aggrSig, commitSigs[i].Signature) - if err == nil { - aggrSig = curAggrSig - commitSigs[i].Signature = nil - } else { - // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. - } - } - return &Commit{ Height: height, Round: round, @@ -903,12 +884,14 @@ func (commit *Commit) StringIndented(indent string) string { %s Height: %d %s Round: %d %s BlockID: %v +%s AggregatedSignature: %X %s Signatures: %s %v %s}#%v`, indent, commit.Height, indent, commit.Round, indent, commit.BlockID, + indent, tmbytes.Fingerprint(commit.AggregatedSignature), indent, indent, strings.Join(commitSigStrings, "\n"+indent+" "), indent, commit.hash) @@ -938,7 +921,38 @@ func (commit *Commit) ToProto() *tmproto.Commit { return c } -func (commit *Commit) VerifySignatures(chainID string, vals []*Validator) error { +// VerifyAndPackSignatures validates the signatures in this commit and then reduces the size of the commit by +// aggregating the signatures that can be aggregated. +func (commit *Commit) VerifyAndPackSignatures(chainID string, vals []*Validator) error { + if err := commit.verifySignatures(chainID, vals); err != nil { + return err + } + + // aggregate BLS-scheme signatures + for i := range commit.Signatures { + commitSig := &commit.Signatures[i] + if commitSig.Absent() { + continue // OK, some signatures can be absent. + } + if commitSig.Signature != nil && len(commitSig.Signature) == bls.SignatureSize { + if commit.AggregatedSignature == nil { + commit.AggregatedSignature = commitSig.Signature + commitSig.Signature = nil + } else { + aggrSig, err := bls.AddSignature(commit.AggregatedSignature, commitSig.Signature) + if err == nil { + commit.AggregatedSignature = aggrSig + commitSig.Signature = nil + } + // The BLS signature that fail to aggregate is remained intact. + } + } + } + + return nil +} + +func (commit *Commit) verifySignatures(chainID string, vals []*Validator) error { blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) messages := make([][]byte, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { diff --git a/types/vote_set.go b/types/vote_set.go index 08157c219..66212f9cf 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -6,8 +6,6 @@ import ( "strings" "sync" - "github.com/tendermint/tendermint/crypto/bls" - "github.com/pkg/errors" "github.com/tendermint/tendermint/libs/bits" @@ -219,16 +217,6 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { panic("Expected to add non-conflicting vote") } - // Aggregate signature in added vote if possible. - aggrSign, err := bls.AddSignature(voteSet.aggregatedSignature, vote.Signature) - if err == nil { - voteSet.aggregatedSignature = aggrSign - vote.Signature = nil - } - // else { - // TODO It's possible to continue if the signature aggregation fails, but a warning log output is wanted here. - // } - return added, nil } diff --git a/types/voter_set.go b/types/voter_set.go index fa51d6505..6dcd91163 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -167,7 +167,7 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, } // Validate signature. - if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { + if err := commit.VerifyAndPackSignatures(chainID, voters.Voters); err != nil { return err } @@ -219,7 +219,7 @@ func (voters *VoterSet) VerifyCommitLight(chainID string, blockID BlockID, // return as soon as +2/3 of the signatures are verified if talliedVotingPower > votingPowerNeeded { // Validate signature. - if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { + if err := commit.VerifyAndPackSignatures(chainID, voters.Voters); err != nil { return err } return nil @@ -297,7 +297,7 @@ func (voters *VoterSet) VerifyFutureCommit(newSet *VoterSet, chainID string, } // Validate signature. - if err := commit.VerifySignatures(chainID, vals); err != nil { + if err := commit.VerifyAndPackSignatures(chainID, vals); err != nil { return err } @@ -369,7 +369,7 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, } // Validate signature. - if err := commit.VerifySignatures(chainID, vals); err != nil { + if err := commit.VerifyAndPackSignatures(chainID, vals); err != nil { return err } @@ -438,7 +438,7 @@ func (voters *VoterSet) VerifyCommitLightTrusting(chainID string, blockID BlockI _, val := voters.GetByAddress(cs.ValidatorAddress) vals = append(vals, val) } - if err := commit.VerifySignatures(chainID, vals); err != nil { + if err := commit.VerifyAndPackSignatures(chainID, vals); err != nil { return err } return nil From e32451af7194a33641fb2584b8077ee2e17a3c08 Mon Sep 17 00:00:00 2001 From: Satoshi Sakamori Date: Tue, 20 Oct 2020 17:06:34 +0900 Subject: [PATCH 28/36] fix compile error on alpine linux --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e59dd08ef..997b02a21 100644 --- a/Makefile +++ b/Makefile @@ -252,9 +252,12 @@ build-linux: mkdir -p $(SRCPATH)/crypto/bls/internal && \ git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ cd $(SRCPATH)/crypto/bls/internal/mcl && \ - git checkout d51fd79c86954a443b1c7ce67d7bcdb8a63ddedb && \ + git checkout 71e4f39fda890701914d521750490ecfe362f1da && \ cd .. && \ git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ + cd $(SRCPATH)/crypto/bls/internal/bls && \ + git checkout 5e2af1489a06eddb58869b66a33aa855b27bb41c && \ + cd .. && \ git clone https://github.com/herumi/bls-eth-go-binary -b v1.12 --depth 1 $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ fi From b78963af347c9dfcdba0e71e92e6dd5a8fb80d3b Mon Sep 17 00:00:00 2001 From: Satoshi Sakamori Date: Tue, 20 Oct 2020 17:36:39 +0900 Subject: [PATCH 29/36] change image for contract_test --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cb4124463..6b0688e44 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -351,7 +351,7 @@ jobs: contract_tests: working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint machine: - image: circleci/classic:latest + image: ubuntu-1604:201903-01 environment: GOBIN: /home/circleci/.go_workspace/bin GOPATH: /home/circleci/.go_workspace/ From 201d419d1b0b27f4ba59a25ea2f29dc5d025d97c Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Mon, 26 Oct 2020 18:13:46 +0900 Subject: [PATCH 30/36] move signature aggregation to VoteSet.MakeCommit() --- blockchain/v1/reactor_test.go | 4 ++-- types/block.go | 36 ++++++++++++++++++++--------------- types/vote_set.go | 23 +++++++++++++++++++++- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index 7ea194614..417926804 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -228,9 +228,9 @@ func TestFastSyncNoBlockResponse(t *testing.T) { for _, tt := range tests { block := reactorPairs[1].bcR.store.LoadBlock(tt.height) if tt.existent { - assert.True(t, block != nil) + assert.True(t, block != nil, "height=%d, existent=%t", tt.height, tt.existent) } else { - assert.True(t, block == nil) + assert.True(t, block == nil, "height=%d, existent=%t", tt.height, tt.existent) } } } diff --git a/types/block.go b/types/block.go index 38bfa561c..4a99f3a41 100644 --- a/types/block.go +++ b/types/block.go @@ -929,25 +929,31 @@ func (commit *Commit) VerifyAndPackSignatures(chainID string, vals []*Validator) } // aggregate BLS-scheme signatures - for i := range commit.Signatures { - commitSig := &commit.Signatures[i] - if commitSig.Absent() { - continue // OK, some signatures can be absent. - } - if commitSig.Signature != nil && len(commitSig.Signature) == bls.SignatureSize { - if commit.AggregatedSignature == nil { - commit.AggregatedSignature = commitSig.Signature - commitSig.Signature = nil - } else { - aggrSig, err := bls.AddSignature(commit.AggregatedSignature, commitSig.Signature) - if err == nil { - commit.AggregatedSignature = aggrSig + /* + for i := range commit.Signatures { + commitSig := &commit.Signatures[i] + if commitSig.Absent() { + continue // OK, some signatures can be absent. + } + + // BLS signature aggregation for all signatures that have been verified to be cleared. + // Signatures that fail to validate are rejected and be Absent. + if commitSig.Signature != nil && len(commitSig.Signature) == bls.SignatureSize { + if commit.AggregatedSignature == nil { + commit.AggregatedSignature = commitSig.Signature commitSig.Signature = nil + } else { + aggrSig, err := bls.AddSignature(commit.AggregatedSignature, commitSig.Signature) + if err == nil { + commit.AggregatedSignature = aggrSig + commitSig.Signature = nil + } + // The BLS signature that fail to aggregate is remained intact. } - // The BLS signature that fail to aggregate is remained intact. + fmt.Printf("*** Commit.VerifyAndPackSignatures(): signature aggregated\n") } } - } + */ return nil } diff --git a/types/vote_set.go b/types/vote_set.go index 66212f9cf..c54090e82 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -3,6 +3,7 @@ package types import ( "bytes" "fmt" + "github.com/tendermint/tendermint/crypto/bls" "strings" "sync" @@ -197,6 +198,9 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { // If we already know of this vote, return false. if existing, ok := voteSet.getVote(valIndex, blockKey); ok { + if existing.Signature == nil { + return false, nil // probably aggregated + } if bytes.Equal(existing.Signature, vote.Signature) { return false, nil // duplicate } @@ -572,6 +576,7 @@ func (voteSet *VoteSet) MakeCommit() *Commit { } // For every validator, get the precommit + aggregatedSignature := voteSet.aggregatedSignature commitSigs := make([]CommitSig, len(voteSet.votes)) for i, v := range voteSet.votes { commitSig := v.CommitSig() @@ -580,10 +585,26 @@ func (voteSet *VoteSet) MakeCommit() *Commit { commitSig = NewCommitSigAbsent() } commitSigs[i] = commitSig + + if !commitSigs[i].Absent() && len(commitSigs[i].Signature) == bls.SignatureSize { + if aggregatedSignature == nil { + aggregatedSignature = commitSigs[i].Signature + commitSigs[i].Signature = nil + } else { + aggrSig, err := bls.AddSignature(aggregatedSignature, commitSigs[i].Signature) + if err == nil { + aggregatedSignature = aggrSig + commitSigs[i].Signature = nil + } else { + // The BLS signature that fail to aggregate is remained intact. + fmt.Printf("*** ERROR: fail to aggregate signature: %s\n", err) + } + } + } } return NewCommitWithAggregatedSignature( - voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs, voteSet.aggregatedSignature) + voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs, aggregatedSignature) } //-------------------------------------------------------------------------------- From 86433ffb34b4a1f3f5a4b7e4b0eb527d4d329097 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Thu, 5 Nov 2020 08:52:32 +0900 Subject: [PATCH 31/36] merge: develop --- .circleci/config.yml | 2 +- CHANGELOG_PENDING.md | 2 + DOCKER/Dockerfile | 4 +- Makefile | 25 +- cmd/tendermint/commands/gen_validator.go | 2 +- cmd/tendermint/commands/init.go | 30 +- .../commands/reset_priv_validator.go | 17 +- cmd/tendermint/commands/testnet.go | 4 + cmd/tendermint/main.go | 2 +- config/config.go | 9 + consensus/common_test.go | 5 +- consensus/metrics.go | 115 ++++++ consensus/state.go | 115 +++++- consensus/state_test.go | 19 +- consensus/wal_generator.go | 6 +- docker_push.sh | 15 + go.mod | 4 +- go.sum | 4 + libs/cmap/cmap_test.go | 4 +- libs/pubsub/pubsub_test.go | 2 +- libs/rand/sampling.go | 96 +---- libs/rand/sampling_test.go | 306 +------------- lite/dynamic_verifier_test.go | 3 +- lite2/client_test.go | 1 - lite2/verifier_test.go | 1 - node/node.go | 12 +- node/node_test.go | 4 +- p2p/upnp/upnp.go | 2 +- privval/file.go | 34 +- privval/file_test.go | 27 +- privval/signer_client_test.go | 32 +- rpc/client/rpc_test.go | 3 +- rpc/test/helpers.go | 3 +- state/execution.go | 16 +- state/metrics.go | 26 +- state/state_test.go | 2 - types/genesis.go | 9 - types/params.go | 3 +- types/params_test.go | 17 - types/validator_set.go | 18 + types/validator_set_test.go | 2 +- types/voter_set.go | 202 +++++++--- types/voter_set_test.go | 378 ++++++++++++++++-- 43 files changed, 975 insertions(+), 608 deletions(-) create mode 100644 docker_push.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 6b0688e44..9c35933ee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -372,7 +372,7 @@ jobs: # Build Tendermint make build-linux # Build contract-tests - docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint ubuntu:20.10 ./scripts/prepare_dredd_test.sh + docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint ubuntu:19.10 ./scripts/prepare_dredd_test.sh # This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use go get github.com/snikch/goodman/cmd/goodman make contract-tests diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 561d83ff5..1e136585b 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,6 +18,8 @@ - [consensus] [\#101](https://github.com/line/tendermint/pull/101) Introduce composite key to delegate features to each function key ### FEATURES: +- [init command] [\#125](https://github.com/line/tendermint/pull/125) Add an option selecting private key type to init, testnet commands +- [consensus] [\#126](https://github.com/line/tendermint/pull/126) Add some metrics measuring duration of each consensus steps ### IMPROVEMENTS: diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index 2bac91cb7..6ea8315d8 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -16,8 +16,8 @@ ENV TMHOME /tendermint RUN apk update && \ apk upgrade && \ apk --no-cache add git make gcc libc-dev build-base curl jq bash && \ - addgroup tmuser && \ - adduser -S -G tmuser tmuser -h "$TMHOME" + addgroup --g 503 tmuser && \ + adduser -S -G tmuser --uid 503 tmuser -h "$TMHOME" # Run the container with tmuser by default. (UID=100, GID=1000) USER tmuser diff --git a/Makefile b/Makefile index 997b02a21..0c5d1c6d5 100644 --- a/Makefile +++ b/Makefile @@ -231,13 +231,9 @@ DOCKER_CMD = docker run --rm \ -v `pwd`:$(DOCKER_HOME) \ -w $(DOCKER_HOME) DOCKER_IMG = golang:1.14.6-alpine3.12 -BUILD_CMD = apk add --update --no-cache git make gcc nasm libc-dev build-base curl jq file gmp-dev clang \ - && cd crypto/bls/internal/bls-eth-go-binary \ - && make CXX=clang++ \ +BUILD_CMD = apk add --update --no-cache git make gcc libc-dev build-base curl jq file gmp-dev clang \ && cd $(DOCKER_HOME) \ - && go mod edit -replace github.com/herumi/bls-eth-go-binary=./crypto/bls/internal/bls-eth-go-binary \ - && make build \ - && go mod edit -dropreplace github.com/herumi/bls-eth-go-binary + && make build # Login docker-container for confirmation building linux binary build-shell: @@ -247,25 +243,10 @@ build-shell: # Build linux binary on other platforms build-linux: - # Download, build and add the BSL local library to modules - if [ ! -d $(SRCPATH)/crypto/bls/internal ]; then \ - mkdir -p $(SRCPATH)/crypto/bls/internal && \ - git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ - cd $(SRCPATH)/crypto/bls/internal/mcl && \ - git checkout 71e4f39fda890701914d521750490ecfe362f1da && \ - cd .. && \ - git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ - cd $(SRCPATH)/crypto/bls/internal/bls && \ - git checkout 5e2af1489a06eddb58869b66a33aa855b27bb41c && \ - cd .. && \ - git clone https://github.com/herumi/bls-eth-go-binary -b v1.12 --depth 1 $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ - fi - # Build Linux binary $(DOCKER_CMD) ${DOCKER_IMG} /bin/sh -c "$(BUILD_CMD)" - # Remove the BLS local library from modules - rm -rf $(SRCPATH)/crypto/bls/internal +>>>>>>> 6f8f870e494229f7b5a3b97a6f8b433e1ae1ef86 .PHONY: build-linux build-docker-localnode: diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index 572bc974f..72ba66340 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -17,7 +17,7 @@ var GenValidatorCmd = &cobra.Command{ } func genValidator(cmd *cobra.Command, args []string) { - pv := privval.GenFilePV("", "") + pv, _ := privval.GenFilePV("", "", privval.PrevKeyTypeEd25519) jsbz, err := cdc.MarshalJSON(pv) if err != nil { panic(err) diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index cb93d5d3a..35f4c4e02 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -15,11 +15,20 @@ import ( tmtime "github.com/tendermint/tendermint/types/time" ) -// InitFilesCmd initialises a fresh Tendermint Core instance. -var InitFilesCmd = &cobra.Command{ - Use: "init", - Short: "Initialize Tendermint", - RunE: initFiles, +func NewInitCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "init", + Short: "Initialize Tendermint", + RunE: initFiles, + } + + AddInitFlags(cmd) + return cmd +} + +func AddInitFlags(cmd *cobra.Command) { + cmd.Flags().String("priv_key_type", config.PrivKeyType, + "Specify validator's private key type (ed25519 | composite)") } func initFiles(cmd *cobra.Command, args []string) error { @@ -30,14 +39,21 @@ func initFilesWithConfig(config *cfg.Config) error { // private validator privValKeyFile := config.PrivValidatorKeyFile() privValStateFile := config.PrivValidatorStateFile() + privKeyType := config.PrivValidatorKeyType() var pv *privval.FilePV if tmos.FileExists(privValKeyFile) { pv = privval.LoadFilePV(privValKeyFile, privValStateFile) logger.Info("Found private validator", "keyFile", privValKeyFile, "stateFile", privValStateFile) } else { - pv = privval.GenFilePV(privValKeyFile, privValStateFile) - pv.Save() + var err error + pv, err = privval.GenFilePV(privValKeyFile, privValStateFile, privKeyType) + if err != nil { + return err + } + if pv != nil { + pv.Save() + } logger.Info("Generated private validator", "keyFile", privValKeyFile, "stateFile", privValStateFile) } diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index eef837c60..db7dbeaf6 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -35,18 +35,19 @@ var ResetPrivValidatorCmd = &cobra.Command{ // it's only suitable for testnets. func resetAll(cmd *cobra.Command, args []string) { ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorKeyFile(), - config.PrivValidatorStateFile(), logger) + config.PrivValidatorKeyType(), config.PrivValidatorStateFile(), logger) } // XXX: this is totally unsafe. // it's only suitable for testnets. func resetPrivValidator(cmd *cobra.Command, args []string) { - resetFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), logger) + resetFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), + config.PrivValidatorKeyType(), logger) } // ResetAll removes address book files plus all data, and resets the privValdiator data. // Exported so other CLI tools can use it. -func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logger log.Logger) { +func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile, privKeyType string, logger log.Logger) error { if keepAddrBook { logger.Info("The address book remains intact") } else { @@ -59,21 +60,25 @@ func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logg } // recreate the dbDir since the privVal state needs to live there tmos.EnsureDir(dbDir, 0700) - resetFilePV(privValKeyFile, privValStateFile, logger) + return resetFilePV(privValKeyFile, privValStateFile, privKeyType, logger) } -func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) { +func resetFilePV(privValKeyFile, privValStateFile, privKeyType string, logger log.Logger) error { if _, err := os.Stat(privValKeyFile); err == nil { pv := privval.LoadFilePVEmptyState(privValKeyFile, privValStateFile) pv.Reset() logger.Info("Reset private validator file to genesis state", "keyFile", privValKeyFile, "stateFile", privValStateFile) } else { - pv := privval.GenFilePV(privValKeyFile, privValStateFile) + pv, err := privval.GenFilePV(privValKeyFile, privValStateFile, privKeyType) + if err != nil { + return err + } pv.Save() logger.Info("Generated private validator file", "keyFile", privValKeyFile, "stateFile", privValStateFile) } + return nil } func removeAddrBook(addrBookFile string, logger log.Logger) { diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 54bb1363e..688d06eda 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -34,6 +34,7 @@ var ( hostnames []string p2pPort int randomMonikers bool + privKeyType string ) const ( @@ -72,6 +73,8 @@ func init() { "P2P Port") TestnetFilesCmd.Flags().BoolVar(&randomMonikers, "random-monikers", false, "Randomize the moniker for each generated node") + TestnetFilesCmd.Flags().StringVar(&privKeyType, "priv_key_type", privval.PrevKeyTypeEd25519, + "Specify validator's private key type (ed25519 | composite)") } // TestnetFilesCmd allows initialisation of files for a Tendermint testnet. @@ -134,6 +137,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error { return err } + config.PrivKeyType = privKeyType initFilesWithConfig(config) pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey) diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index 615b7e065..95c522839 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -15,7 +15,6 @@ func main() { rootCmd := cmd.RootCmd rootCmd.AddCommand( cmd.GenValidatorCmd, - cmd.InitFilesCmd, cmd.ProbeUpnpCmd, cmd.LiteCmd, cmd.ReplayCmd, @@ -42,6 +41,7 @@ func main() { nodeFunc := nm.DefaultNewNode // Create & start node + rootCmd.AddCommand(cmd.NewInitCmd()) rootCmd.AddCommand(cmd.NewRunNodeCmd(nodeFunc)) cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv(filepath.Join("$HOME", cfg.DefaultTendermintDir))) diff --git a/config/config.go b/config/config.go index 173713279..b4feb5b25 100644 --- a/config/config.go +++ b/config/config.go @@ -8,6 +8,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/privval" ) const ( @@ -212,6 +213,9 @@ type BaseConfig struct { //nolint: maligned // If true, query the ABCI app on connecting to a new peer // so the app can decide if we should keep the connection or not FilterPeers bool `mapstructure:"filter_peers"` // false + + // Specify validator's private key type + PrivKeyType string `mapstructure:"priv_key_type"` } // DefaultBaseConfig returns a default base configuration for a Tendermint node @@ -231,6 +235,7 @@ func DefaultBaseConfig() BaseConfig { FilterPeers: false, DBBackend: "goleveldb", DBPath: "data", + PrivKeyType: privval.PrevKeyTypeEd25519, } } @@ -273,6 +278,10 @@ func (cfg BaseConfig) DBDir() string { return rootify(cfg.DBPath, cfg.RootDir) } +func (cfg BaseConfig) PrivValidatorKeyType() string { + return cfg.PrivKeyType +} + // ValidateBasic performs basic validation (checking param bounds, etc.) and // returns an error if any check fails. func (cfg BaseConfig) ValidateBasic() error { diff --git a/consensus/common_test.go b/consensus/common_test.go index 8ee2f1f38..6a61cc764 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -420,7 +420,8 @@ func loadPrivValidator(config *cfg.Config) *privval.FilePV { privValidatorKeyFile := config.PrivValidatorKeyFile() ensureDir(filepath.Dir(privValidatorKeyFile), 0700) privValidatorStateFile := config.PrivValidatorStateFile() - privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile) + privKeyType := config.PrivValidatorKeyType() + privValidator, _ := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile, privKeyType) privValidator.Reset() return privValidator } @@ -803,7 +804,7 @@ func randConsensusNetWithPeers( panic(err) } - privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, _ = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), privval.PrevKeyTypeEd25519) } app := appFunc(path.Join(config.DBDir(), fmt.Sprintf("%s_%d", testName, i))) diff --git a/consensus/metrics.go b/consensus/metrics.go index e315fd1f7..a448f4559 100644 --- a/consensus/metrics.go +++ b/consensus/metrics.go @@ -63,6 +63,24 @@ type Metrics struct { // Number of blockparts transmitted by peer. BlockParts metrics.Counter + + // Number of blocks that are we couldn't receive + MissingProposal metrics.Gauge + + // Number of rounds turned over. + RoundFailures metrics.Histogram + + // Execution time profiling of each step + ProposalCreating metrics.Histogram + ProposalWaiting metrics.Histogram + ProposalVerifying metrics.Histogram + ProposalBlockReceiving metrics.Histogram + PrevoteBlockVerifying metrics.Histogram + PrevoteReceiving metrics.Histogram + PrecommitBlockVerifying metrics.Histogram + PrecommitReceiving metrics.Histogram + CommitBlockVerifying metrics.Histogram + CommitBlockApplying metrics.Histogram } // PrometheusMetrics returns Metrics build using Prometheus client library. @@ -197,6 +215,89 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { Name: "block_parts", Help: "Number of blockparts transmitted by peer.", }, append(labels, "peer_id")).With(labelsAndValues...), + MissingProposal: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "missing_proposal", + Help: "Number of blocks we couldn't receive", + }, labels).With(labelsAndValues...), + RoundFailures: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "round_failures", + Help: "Number of rounds failed on consensus", + Buckets: stdprometheus.LinearBuckets(0, 1, 5), + }, labels).With(labelsAndValues...), + ProposalCreating: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_proposal_creating", + Help: "Duration of creating proposal and block", + Buckets: stdprometheus.LinearBuckets(100, 100, 10), + }, labels).With(labelsAndValues...), + ProposalWaiting: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_proposal_waiting", + Help: "Duration between enterNewRound and receiving proposal", + Buckets: stdprometheus.LinearBuckets(100, 100, 10), + }, labels).With(labelsAndValues...), + ProposalVerifying: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_proposal_verifying", + Help: "Duration of ValidBlock", + Buckets: stdprometheus.LinearBuckets(50, 50, 10), + }, labels).With(labelsAndValues...), + ProposalBlockReceiving: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_proposal_block_receiving", + Help: "Duration of receiving all proposal block parts", + Buckets: stdprometheus.LinearBuckets(100, 100, 10), + }, labels).With(labelsAndValues...), + PrevoteBlockVerifying: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_prevote_block_verifying", + Help: "Duration of ValidBlock in prevote", + Buckets: stdprometheus.LinearBuckets(50, 50, 10), + }, labels).With(labelsAndValues...), + PrevoteReceiving: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_prevote_receiving", + Help: "Duration of receiving 2/3+ prevotes", + Buckets: stdprometheus.LinearBuckets(100, 100, 10), + }, labels).With(labelsAndValues...), + PrecommitBlockVerifying: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_precommit_block_verifying", + Help: "Duration of ValidBlock in precommit", + Buckets: stdprometheus.LinearBuckets(50, 50, 10), + }, labels).With(labelsAndValues...), + PrecommitReceiving: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_precommit_receiving", + Help: "Duration of receiving 2/3+ precommits", + Buckets: stdprometheus.LinearBuckets(100, 100, 10), + }, labels).With(labelsAndValues...), + CommitBlockVerifying: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_commit_block_verifying", + Help: "Duration of ValidBlock in commit", + Buckets: stdprometheus.LinearBuckets(50, 50, 10), + }, labels).With(labelsAndValues...), + CommitBlockApplying: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "duration_commit_block_applying", + Help: "Duration of applying block", + Buckets: stdprometheus.LinearBuckets(100, 100, 10), + }, labels).With(labelsAndValues...), } } @@ -228,5 +329,19 @@ func NopMetrics() *Metrics { CommittedHeight: discard.NewGauge(), FastSyncing: discard.NewGauge(), BlockParts: discard.NewCounter(), + + MissingProposal: discard.NewGauge(), + RoundFailures: discard.NewHistogram(), + + ProposalCreating: discard.NewHistogram(), + ProposalWaiting: discard.NewHistogram(), + ProposalVerifying: discard.NewHistogram(), + ProposalBlockReceiving: discard.NewHistogram(), + PrevoteBlockVerifying: discard.NewHistogram(), + PrevoteReceiving: discard.NewHistogram(), + PrecommitBlockVerifying: discard.NewHistogram(), + PrecommitReceiving: discard.NewHistogram(), + CommitBlockVerifying: discard.NewHistogram(), + CommitBlockApplying: discard.NewHistogram(), } } diff --git a/consensus/state.go b/consensus/state.go index bab97babf..8130e6015 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -9,7 +9,6 @@ import ( "time" "github.com/pkg/errors" - "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" @@ -68,6 +67,48 @@ type evidencePool interface { AddEvidence(types.Evidence) error } +type StepDuration struct { + started bool + start time.Time + end time.Time +} + +func (sd *StepDuration) GetDuration() float64 { + if !sd.started && sd.end.After(sd.start) { + return float64(sd.end.Sub(sd.start).Microseconds()) / 1000 + } + return 0 +} + +func (sd *StepDuration) SetStart() time.Time { + sd.start = tmtime.Now() + sd.started = true + return sd.start +} + +func (sd *StepDuration) SetEnd() time.Time { + if sd.started { + // update only once at first; it will be reset when Start is re-assigned + sd.end = tmtime.Now() + sd.started = false + } + return sd.end +} + +type StepTimes struct { + ProposalCreatedByMyself bool + ProposalCreating StepDuration + ProposalWaiting StepDuration + ProposalVerifying StepDuration + ProposalBlockReceiving StepDuration + PrevoteBlockVerifying StepDuration + PrevoteReceiving StepDuration + PrecommitBlockVerifying StepDuration + PrecommitReceiving StepDuration + CommitBlockVerifying StepDuration + CommitBlockApplying StepDuration +} + // State handles execution of the consensus algorithm. // It processes votes and proposals, and upon reaching agreement, // commits blocks to the chain and executes them against the application. @@ -134,6 +175,9 @@ type State struct { // for reporting metrics metrics *Metrics + + // times of each step + stepTimes StepTimes } // StateOption sets an optional parameter on the State. @@ -828,7 +872,8 @@ func (cs *State) enterNewRound(height int64, round int) { return } - if now := tmtime.Now(); cs.StartTime.After(now) { + now := cs.stepTimes.ProposalWaiting.SetStart() + if cs.StartTime.After(now) { logger.Info("Need to set a buffer and log message here for sanity.", "startTime", cs.StartTime, "now", now) } @@ -944,13 +989,14 @@ func (cs *State) enterPropose(height int64, round int) { "privValidator", cs.privValidator) cs.decideProposal(height, round) + cs.stepTimes.ProposalCreatedByMyself = true } else { logger.Info("enterPropose: Not our turn to propose", "proposer", cs.Proposer.Address, "privValidator", cs.privValidator) - + cs.stepTimes.ProposalCreatedByMyself = false } if !cs.Voters.HasAddress(address) { @@ -974,8 +1020,10 @@ func (cs *State) defaultDecideProposal(height int64, round int) { block, blockParts = cs.ValidBlock, cs.ValidBlockParts } else { // Create a new proposal block from state/txs from the mempool. + cs.stepTimes.ProposalCreating.SetStart() block, blockParts = cs.createProposalBlock(round) if block == nil { // on error + cs.stepTimes.ProposalCreating.SetEnd() return } cs.Logger.Info("Create Block", "Height", height, "Round", round, @@ -990,7 +1038,7 @@ func (cs *State) defaultDecideProposal(height int64, round int) { propBlockID := types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()} proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID) if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal); err == nil { - + cs.stepTimes.ProposalCreating.SetEnd() // send proposal and block parts on internal msg queue cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""}) for i := 0; i < blockParts.Total(); i++ { @@ -1000,6 +1048,7 @@ func (cs *State) defaultDecideProposal(height int64, round int) { cs.Logger.Info("Signed proposal", "height", height, "round", round, "proposal", proposal) cs.Logger.Debug(fmt.Sprintf("Signed proposal block: %v", block)) } else if !cs.replayMode { + cs.stepTimes.ProposalCreating.SetEnd() cs.Logger.Error("enterPropose: Error signing proposal", "height", height, "round", round, "err", err) } } @@ -1106,25 +1155,34 @@ func (cs *State) defaultDoPrevote(height int64, round int) { // If ProposalBlock is nil, prevote nil. if cs.ProposalBlock == nil { + // if it already ends or not starts it will be ignored + cs.stepTimes.ProposalWaiting.SetEnd() + cs.stepTimes.ProposalBlockReceiving.SetEnd() logger.Info("enterPrevote: ProposalBlock is nil") cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{}) + // increase missing proposal by one + cs.metrics.MissingProposal.Add(1) return } + cs.stepTimes.PrevoteBlockVerifying.SetStart() // Validate proposal block err := cs.blockExec.ValidateBlock(cs.state, round, cs.ProposalBlock) if err != nil { // ProposalBlock is invalid, prevote nil. logger.Error("enterPrevote: ProposalBlock is invalid", "err", err) + cs.stepTimes.PrevoteBlockVerifying.SetEnd() cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{}) return } + cs.stepTimes.PrevoteBlockVerifying.SetEnd() // Prevote cs.ProposalBlock // NOTE: the proposal signature is validated when it is received, // and the proposal block parts are validated as they are received (against the merkle hash in the proposal) logger.Info("enterPrevote: ProposalBlock is valid") cs.signAddVote(types.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header()) + cs.stepTimes.PrevoteReceiving.SetStart() } // Enter: any +2/3 prevotes at next round. @@ -1230,6 +1288,7 @@ func (cs *State) enterPrecommit(height int64, round int) { cs.LockedRound = round cs.eventBus.PublishEventRelock(cs.RoundStateEvent()) cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader) + cs.stepTimes.PrecommitReceiving.SetStart() return } @@ -1237,14 +1296,17 @@ func (cs *State) enterPrecommit(height int64, round int) { if cs.ProposalBlock.HashesTo(blockID.Hash) { logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash) // Validate the block. + cs.stepTimes.PrecommitBlockVerifying.SetStart() if err := cs.blockExec.ValidateBlock(cs.state, round, cs.ProposalBlock); err != nil { panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err)) } + cs.stepTimes.PrecommitBlockVerifying.SetEnd() cs.LockedRound = round cs.LockedBlock = cs.ProposalBlock cs.LockedBlockParts = cs.ProposalBlockParts cs.eventBus.PublishEventLock(cs.RoundStateEvent()) cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader) + cs.stepTimes.PrecommitReceiving.SetStart() return } @@ -1399,6 +1461,7 @@ func (cs *State) finalizeCommit(height int64) { blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts + cs.stepTimes.CommitBlockVerifying.SetStart() if !ok { panic("Cannot finalizeCommit, commit does not have two thirds majority") } @@ -1411,6 +1474,7 @@ func (cs *State) finalizeCommit(height int64) { if err := cs.blockExec.ValidateBlock(cs.state, cs.CommitRound, block); err != nil { panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err)) } + cs.stepTimes.CommitBlockVerifying.SetEnd() cs.Logger.Info("Finalizing commit of block with N txs", "height", block.Height, @@ -1461,6 +1525,7 @@ func (cs *State) finalizeCommit(height int64) { // Execute and commit the block, update and save the state, and update the mempool. // NOTE The block.AppHash wont reflect these txs until the next block. + cs.stepTimes.CommitBlockApplying.SetStart() var err error var retainHeight int64 stateCopy, retainHeight, err = cs.blockExec.ApplyBlock( @@ -1473,6 +1538,7 @@ func (cs *State) finalizeCommit(height int64) { } return } + cs.stepTimes.CommitBlockApplying.SetEnd() fail.Fail() // XXX @@ -1545,6 +1611,7 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { commitSize, valSetLen, block.Height, block.LastCommit.Signatures, cs.LastVoters.Voters)) } + selectedAsVoter := false for i, val := range cs.LastVoters.Voters { commitSig := block.LastCommit.Signatures[i] if commitSig.Absent() { @@ -1564,6 +1631,7 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { "validator_address", val.Address.String(), } cs.metrics.VoterPower.With(label...).Set(float64(val.VotingPower)) + selectedAsVoter = true if commitSig.ForBlock() { cs.metrics.VoterLastSignedHeight.With(label...).Set(float64(height)) } else { @@ -1572,6 +1640,19 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { } } } + if !selectedAsVoter { + address := "" + if cs.privValidator != nil { + pubKey, err := cs.privValidator.GetPubKey() + if err == nil && cs.Validators != nil && cs.Validators.HasAddress(pubKey.Address().Bytes()) { + address = pubKey.Address().String() + } + } + label := []string{ + "validator_address", address, + } + cs.metrics.VoterPower.With(label...).Set(float64(0)) + } } cs.metrics.MissingVoters.Set(float64(missingVoters)) cs.metrics.MissingVotersPower.Set(float64(missingVotersPower)) @@ -1598,6 +1679,20 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { cs.metrics.TotalTxs.Add(float64(len(block.Data.Txs))) cs.metrics.BlockSizeBytes.Set(float64(block.Size())) cs.metrics.CommittedHeight.Set(float64(block.Height)) + + cs.metrics.RoundFailures.Observe(float64(cs.Round)) + if cs.stepTimes.ProposalCreatedByMyself { + cs.metrics.ProposalCreating.Observe(cs.stepTimes.ProposalCreating.GetDuration()) + } + cs.metrics.ProposalWaiting.Observe(cs.stepTimes.ProposalWaiting.GetDuration()) + cs.metrics.ProposalVerifying.Observe(cs.stepTimes.ProposalVerifying.GetDuration()) + cs.metrics.ProposalBlockReceiving.Observe(cs.stepTimes.ProposalBlockReceiving.GetDuration()) + cs.metrics.PrevoteBlockVerifying.Observe(cs.stepTimes.PrevoteBlockVerifying.GetDuration()) + cs.metrics.PrevoteReceiving.Observe(cs.stepTimes.PrevoteReceiving.GetDuration()) + cs.metrics.PrecommitBlockVerifying.Observe(cs.stepTimes.PrecommitBlockVerifying.GetDuration()) + cs.metrics.PrecommitReceiving.Observe(cs.stepTimes.PrecommitReceiving.GetDuration()) + cs.metrics.CommitBlockVerifying.Observe(cs.stepTimes.CommitBlockVerifying.GetDuration()) + cs.metrics.CommitBlockApplying.Observe(cs.stepTimes.CommitBlockApplying.GetDuration()) } //----------------------------------------------------------------------------- @@ -1619,14 +1714,17 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { (proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) { return ErrInvalidProposalPOLRound } + cs.stepTimes.ProposalWaiting.SetEnd() + cs.stepTimes.ProposalVerifying.SetStart() // If consensus does not enterNewRound yet, cs.Proposer may be nil or prior proposer, so don't use cs.Proposer proposer := cs.Validators.SelectProposer(cs.state.LastProofHash, proposal.Height, proposal.Round) - // Verify signature if !proposer.PubKey.VerifyBytes(proposal.SignBytes(cs.state.ChainID), proposal.Signature) { + cs.stepTimes.ProposalVerifying.SetEnd() return ErrInvalidProposalSignature } + cs.stepTimes.ProposalVerifying.SetEnd() cs.Proposal = proposal // We don't update cs.ProposalBlockParts if it is already set. @@ -1636,6 +1734,8 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartsHeader) } cs.Logger.Info("Received proposal", "proposal", proposal) + + cs.stepTimes.ProposalBlockReceiving.SetStart() return nil } @@ -1674,6 +1774,8 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (add if err != nil { return added, err } + cs.stepTimes.ProposalBlockReceiving.SetEnd() + // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) cs.eventBus.PublishEventCompleteProposal(cs.CompleteProposalEvent()) @@ -1824,6 +1926,7 @@ func (cs *State) addVote( // If +2/3 prevotes for a block or nil for *any* round: if blockID, ok := prevotes.TwoThirdsMajority(); ok { + cs.stepTimes.PrevoteReceiving.SetEnd() // There was a polka! // If we're locked but this is a recent polka, unlock. @@ -1893,6 +1996,7 @@ func (cs *State) addVote( blockID, ok := precommits.TwoThirdsMajority() if ok { + cs.stepTimes.PrecommitReceiving.SetEnd() // Executed as TwoThirdsMajority could be from a higher round cs.enterNewRound(height, vote.Round) cs.enterPrecommit(height, vote.Round) @@ -1905,6 +2009,7 @@ func (cs *State) addVote( cs.enterPrecommitWait(height, vote.Round) } } else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { + cs.stepTimes.PrecommitReceiving.SetEnd() cs.enterNewRound(height, vote.Round) cs.enterPrecommitWait(height, vote.Round) } diff --git a/consensus/state_test.go b/consensus/state_test.go index 7e45f887a..9358a0ec1 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -1924,8 +1924,7 @@ func proposeBlock(t *testing.T, cs *State, round int, vssMap map[crypto.PubKey]* func TestStateFullRoundWithSelectedVoter(t *testing.T) { cs, vss := randStateWithVoterParams(10, &types.VoterParams{ VoterElectionThreshold: 5, - MaxTolerableByzantinePercentage: 20, - ElectionPrecision: 2}) + MaxTolerableByzantinePercentage: 20}) vss[0].Height = 1 // this is needed because of `incrementHeight(vss[1:]...)` of randStateWithVoterParams() vssMap := makeVssMap(vss) height, round := cs.Height, cs.Round @@ -2014,8 +2013,7 @@ func TestStateBadVoterWithSelectedVoter(t *testing.T) { // making him having 1/3 + 1 voting power of total cs, vss := randStateWithVoterParams(9, &types.VoterParams{ VoterElectionThreshold: 5, - MaxTolerableByzantinePercentage: 20, - ElectionPrecision: 5}) + MaxTolerableByzantinePercentage: 20}) assert.True(t, cs.Voters.Size() >= 4) @@ -2154,8 +2152,7 @@ func TestStateAllVoterToSelectedVoter(t *testing.T) { startValidators := 5 cs, vss := randStateWithVoterParamsWithApp(startValidators, &types.VoterParams{ VoterElectionThreshold: int32(startValidators), - MaxTolerableByzantinePercentage: 20, - ElectionPrecision: 2}, + MaxTolerableByzantinePercentage: 20}, "TestStateAllVoterToSelectedVoter") vss[0].Height = 1 // this is needed because of `incrementHeight(vss[1:]...)` of randStateWithVoterParams() vssMap := makeVssMap(vss) @@ -2208,13 +2205,6 @@ func TestStateAllVoterToSelectedVoter(t *testing.T) { ensureNewRound(newRoundCh, height+1, 0) endHeight := 20 - voterCount := make([]int, endHeight-1) - for i := 0; i < len(voterCount); i++ { - voterCount[i] = int(types.CalNumOfVoterToElect(int64(startValidators+i), 0.2, 0.99)) - if voterCount[i] < startValidators { - voterCount[i] = startValidators - } - } for i := 2; i <= endHeight; i++ { // height 2~10 height = cs.Height privPubKey, _ = cs.privValidator.GetPubKey() @@ -2229,9 +2219,6 @@ func TestStateAllVoterToSelectedVoter(t *testing.T) { voters = cs.Voters voterPrivVals = votersPrivVals(voters, vssMap) - // verify voters count - assert.True(t, voters.Size() == voterCount[i-2]) - signAddVotes(cs, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(types.BlockPartSizeBytes).Header(), voterPrivVals...) diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 422c3f73b..7994ec4fb 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -44,7 +44,11 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { // NOTE: we don't do handshake so need to set state.Version.Consensus.App directly. privValidatorKeyFile := config.PrivValidatorKeyFile() privValidatorStateFile := config.PrivValidatorStateFile() - privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile) + privKeyType := config.PrivValidatorKeyType() + privValidator, err := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile, privKeyType) + if err != nil { + return errors.Wrap(err, "failed to create a private key") + } genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) if err != nil { return errors.Wrap(err, "failed to read genesis file") diff --git a/docker_push.sh b/docker_push.sh new file mode 100644 index 000000000..1971b528d --- /dev/null +++ b/docker_push.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +make build-docker +LINE_VERSION="$(awk -F\" '/LINECoreSemVer =/ {print $2; exit }' < ./version/version.go)" +GIT_COMMIT="$(git rev-parse --short=8 HEAD)" +TAG=docker-registry.linecorp.com/link-network/tendermint:latest +docker tag tendermint/tendermint:latest $TAG + +read -p "==> Do you push docker image to [$TAG]? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]] +then + docker push $TAG +fi + diff --git a/go.mod b/go.mod index 7a8d083bb..c4e86660e 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,8 @@ require ( github.com/golang/protobuf v1.4.0 github.com/gorilla/websocket v1.4.2 github.com/gtank/merlin v0.1.1 - github.com/herumi/bls v0.0.0-20200721070902-5e2af1489a06 - github.com/herumi/bls-eth-go-binary v0.0.0-20200522010937-01d282b5380b + github.com/herumi/bls v0.0.0-20200904110701-e4663751b56f + github.com/herumi/bls-eth-go-binary v0.0.0-20200923072303-32b29e5d8cbf github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.1 github.com/minio/highwayhash v1.0.0 diff --git a/go.sum b/go.sum index dced540f8..6ce98199e 100644 --- a/go.sum +++ b/go.sum @@ -217,8 +217,12 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/herumi/bls v0.0.0-20200721070902-5e2af1489a06 h1:4w5HyoC9FiD1UcO9j6MBkBIeBaLTe8JJOYmSiIFlo8c= github.com/herumi/bls v0.0.0-20200721070902-5e2af1489a06/go.mod h1:i4wRNUUFF1nNmYFHM9UDl13MGoxEQkMVCLAd82qZz4s= +github.com/herumi/bls v0.0.0-20200904110701-e4663751b56f h1:MWNZl9RnZ8aDlChxHjlbNXAqFpwNPdLwoxJpV5LXUmw= +github.com/herumi/bls v0.0.0-20200904110701-e4663751b56f/go.mod h1:i4wRNUUFF1nNmYFHM9UDl13MGoxEQkMVCLAd82qZz4s= github.com/herumi/bls-eth-go-binary v0.0.0-20200522010937-01d282b5380b h1:mu+F5uA3Y68oB6KXZqWlASKMetbNufhQx2stMI+sD+Y= github.com/herumi/bls-eth-go-binary v0.0.0-20200522010937-01d282b5380b/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= +github.com/herumi/bls-eth-go-binary v0.0.0-20200923072303-32b29e5d8cbf h1:Lw7EOMVxu3O+7Ro5bqn9M20a7GwuCqZQsmdXNzmcKE4= +github.com/herumi/bls-eth-go-binary v0.0.0-20200923072303-32b29e5d8cbf/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= diff --git a/libs/cmap/cmap_test.go b/libs/cmap/cmap_test.go index b1ab85c0c..669e50882 100644 --- a/libs/cmap/cmap_test.go +++ b/libs/cmap/cmap_test.go @@ -60,10 +60,10 @@ func TestContains(t *testing.T) { func BenchmarkCMapHas(b *testing.B) { m := NewCMap() for i := 0; i < 1000; i++ { - m.Set(fmt.Sprint(i), i) + m.Set(string(rune(i)), i) } b.ResetTimer() for i := 0; i < b.N; i++ { - m.Has(fmt.Sprint(i)) + m.Has(string(rune(i))) } } diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index 85076f31e..70a003b75 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -383,7 +383,7 @@ func benchmarkNClients(n int, b *testing.B) { s.PublishWithEvents( ctx, "Gamora", - map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {strconv.Itoa(i)}}, + map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {string(rune(i))}}, ) } } diff --git a/libs/rand/sampling.go b/libs/rand/sampling.go index 69e15612c..d0a21c04d 100644 --- a/libs/rand/sampling.go +++ b/libs/rand/sampling.go @@ -10,7 +10,6 @@ import ( type Candidate interface { Priority() uint64 LessThan(other Candidate) bool - SetWinPoint(winPoint int64) } // Select a specified number of candidates randomly from the candidate set based on each priority. This function is @@ -32,7 +31,7 @@ func RandomSamplingWithPriority( thresholds := make([]uint64, sampleSize) for i := 0; i < sampleSize; i++ { // calculating [gross weights] × [(0,1] random number] - thresholds[i] = randomThreshold(&seed, totalPriority) + thresholds[i] = RandomThreshold(&seed, totalPriority) } s.Slice(thresholds, func(i, j int) bool { return thresholds[i] < thresholds[j] }) @@ -65,20 +64,8 @@ func RandomSamplingWithPriority( totalPriority, actualTotalPriority, seed, sampleSize, undrawn, undrawn, thresholds[undrawn], len(candidates))) } -func moveWinnerToLast(candidates []Candidate, winner int) { - winnerCandidate := candidates[winner] - copy(candidates[winner:], candidates[winner+1:]) - candidates[len(candidates)-1] = winnerCandidate -} - const uint64Mask = uint64(0x7FFFFFFFFFFFFFFF) -// precisionForSelection is a value to be corrected to increase precision when calculating voting power as an integer. -const precisionForSelection = uint64(1000) - -// precisionCorrectionForSelection is a value corrected for accuracy of voting power -const precisionCorrectionForSelection = uint64(1000) - var divider *big.Int func init() { @@ -86,7 +73,7 @@ func init() { divider.Add(divider, big.NewInt(1)) } -func randomThreshold(seed *uint64, total uint64) uint64 { +func RandomThreshold(seed *uint64, total uint64) uint64 { totalBig := new(big.Int).SetUint64(total) a := new(big.Int).SetUint64(nextRandom(seed) & uint64Mask) a.Mul(a, totalBig) @@ -94,85 +81,6 @@ func randomThreshold(seed *uint64, total uint64) uint64 { return a.Uint64() } -func RandomSamplingWithoutReplacement( - seed uint64, candidates []Candidate, minSamplingCount int) (winners []Candidate) { - - if len(candidates) < minSamplingCount { - panic(fmt.Sprintf("The number of candidates(%d) cannot be less minSamplingCount %d", - len(candidates), minSamplingCount)) - } - - totalPriority := sumTotalPriority(candidates) - candidates = sort(candidates) - winnersPriority := uint64(0) - losersPriorities := make([]uint64, len(candidates)) - winnerNum := 0 - for winnerNum < minSamplingCount { - if totalPriority-winnersPriority == 0 { - // it's possible if some candidates have zero priority - // if then, we can't elect voter any more; we should holt electing not to fall in infinity loop - break - } - threshold := randomThreshold(&seed, totalPriority-winnersPriority) - cumulativePriority := uint64(0) - found := false - for i, candidate := range candidates[:len(candidates)-winnerNum] { - if threshold < cumulativePriority+candidate.Priority() { - moveWinnerToLast(candidates, i) - winnersPriority += candidate.Priority() - losersPriorities[winnerNum] = totalPriority - winnersPriority - winnerNum++ - found = true - break - } - cumulativePriority += candidate.Priority() - } - - if !found { - panic(fmt.Sprintf("Cannot find random sample. winnerNum=%d, minSamplingCount=%d, "+ - "winnersPriority=%d, totalPriority=%d, threshold=%d", - winnerNum, minSamplingCount, winnersPriority, totalPriority, threshold)) - } - } - correction := new(big.Int).SetUint64(totalPriority) - correction = correction.Mul(correction, new(big.Int).SetUint64(precisionForSelection)) - compensationProportions := make([]big.Int, winnerNum) - for i := winnerNum - 2; i >= 0; i-- { - additionalCompensation := new(big.Int).Div(correction, new(big.Int).SetUint64(losersPriorities[i])) - compensationProportions[i].Add(&compensationProportions[i+1], additionalCompensation) - } - winners = candidates[len(candidates)-winnerNum:] - winPoints := make([]*big.Int, len(winners)) - totalWinPoint := new(big.Int) - for i, winner := range winners { - winPoints[i] = new(big.Int).SetUint64(winner.Priority()) - winPoints[i].Mul(winPoints[i], &compensationProportions[i]) - winPoints[i].Add(winPoints[i], correction) - totalWinPoint.Add(totalWinPoint, winPoints[i]) - } - recalibration := new(big.Int).Div(correction, new(big.Int).SetUint64(precisionCorrectionForSelection)) - for i, winner := range winners { - winPoint := new(big.Int) - winPoint.Mul(recalibration, winPoints[i]) - winPoint.Div(winPoint, totalWinPoint) - winner.SetWinPoint(winPoint.Int64()) - } - return winners -} - -// sumTotalPriority calculate the sum of all candidate's priority(weight) -// and the sum should be less then or equal to MaxUint64 -// TODO We need to check the total weight doesn't over MaxUint64 in somewhere not here. -func sumTotalPriority(candidates []Candidate) (sum uint64) { - for _, candi := range candidates { - sum += candi.Priority() - } - if sum == 0 { - panic("all candidates have zero priority") - } - return -} - // SplitMix64 // http://xoshiro.di.unimi.it/splitmix64.c // diff --git a/libs/rand/sampling_test.go b/libs/rand/sampling_test.go index 0cf544c0b..a6e0733a6 100644 --- a/libs/rand/sampling_test.go +++ b/libs/rand/sampling_test.go @@ -11,9 +11,10 @@ import ( ) type Element struct { - id uint32 - winPoint int64 - weight uint64 + id uint32 + winPoint float64 + weight uint64 + votingPower uint64 } func (e *Element) Priority() uint64 { @@ -28,9 +29,11 @@ func (e *Element) LessThan(other Candidate) bool { return e.id < o.id } -func (e *Element) SetWinPoint(winPoint int64) { - e.winPoint += winPoint +func (e *Element) SetVotingPower(votingPower uint64) { + e.votingPower = votingPower } +func (e *Element) WinPoint() float64 { return e.winPoint } +func (e *Element) VotingPower() uint64 { return e.votingPower } func TestRandomSamplingWithPriority(t *testing.T) { candidates := newCandidates(100, func(i int) uint64 { return uint64(i) }) @@ -92,113 +95,6 @@ func TestRandomSamplingPanicCase(t *testing.T) { } } -func resetWinPoint(candidate []Candidate) { - for _, c := range candidate { - c.(*Element).winPoint = 0 - } -} - -func TestRandomSamplingWithoutReplacement1Candidate(t *testing.T) { - candidates := newCandidates(1, func(i int) uint64 { return uint64(1000 * (i + 1)) }) - - winners := RandomSamplingWithoutReplacement(0, candidates, 1) - assert.True(t, len(winners) == 1) - assert.True(t, candidates[0] == winners[0]) - assert.True(t, uint64(winners[0].(*Element).winPoint) == precisionForSelection) - resetWinPoint(candidates) - - winners2 := RandomSamplingWithoutReplacement(0, candidates, 0) - assert.True(t, len(winners2) == 0) - resetWinPoint(candidates) - - winners4 := RandomSamplingWithoutReplacement(0, candidates, 0) - assert.True(t, len(winners4) == 0) - resetWinPoint(candidates) -} - -// test samplingThreshold -func TestRandomSamplingWithoutReplacementSamplingThreshold(t *testing.T) { - candidates := newCandidates(100, func(i int) uint64 { return uint64(1000 * (i + 1)) }) - - for i := 1; i <= 100; i++ { - winners := RandomSamplingWithoutReplacement(0, candidates, i) - assert.True(t, len(winners) == i) - resetWinPoint(candidates) - } -} - -// test downscale of win point cases -func TestRandomSamplingWithoutReplacementDownscale(t *testing.T) { - candidates := newCandidates(10, func(i int) uint64 { - if i == 0 { - return math.MaxInt64 >> 1 - } - if i == 1 { - return 1 << 55 - } - if i == 3 { - return 1 << 54 - } - if i == 4 { - return 1 << 53 - } - return uint64(i) - }) - RandomSamplingWithoutReplacement(0, candidates, 5) -} - -// test random election should be deterministic -func TestRandomSamplingWithoutReplacementDeterministic(t *testing.T) { - candidates1 := newCandidates(100, func(i int) uint64 { return uint64(i + 1) }) - candidates2 := newCandidates(100, func(i int) uint64 { return uint64(i + 1) }) - for i := 1; i <= 100; i++ { - winners1 := RandomSamplingWithoutReplacement(uint64(i), candidates1, 50) - winners2 := RandomSamplingWithoutReplacement(uint64(i), candidates2, 50) - sameCandidates(winners1, winners2) - resetWinPoint(candidates1) - resetWinPoint(candidates2) - } -} - -func TestRandomSamplingWithoutReplacementIncludingZeroStakingPower(t *testing.T) { - // first candidate's priority is 0 - candidates1 := newCandidates(100, func(i int) uint64 { return uint64(i) }) - winners1 := RandomSamplingWithoutReplacement(0, candidates1, 100) - assert.True(t, len(winners1) == 99) - - candidates2 := newCandidates(100, func(i int) uint64 { - if i < 10 { - return 0 - } - return uint64(i) - }) - winners2 := RandomSamplingWithoutReplacement(0, candidates2, 95) - assert.True(t, len(winners2) == 90) -} - -func TestRandomSamplingWithoutReplacementOverflow(t *testing.T) { - number := 100 - candidates := newCandidates(number, func(i int) uint64 { return math.MaxUint64 / uint64(number) }) - winners := RandomSamplingWithoutReplacement(rand.Uint64(), candidates, 64) - lastWinPoint := int64(math.MaxInt64) - for _, w := range winners { - element := w.(*Element) - assert.True(t, element.winPoint > 0) - assert.True(t, element.winPoint <= lastWinPoint) - lastWinPoint = element.winPoint - } -} - -func accumulateAndResetReward(candidate []Candidate, acc []uint64) uint64 { - totalWinPoint := uint64(0) - for i, c := range candidate { - acc[i] += uint64(c.(*Element).winPoint) - totalWinPoint += uint64(c.(*Element).winPoint) - c.(*Element).winPoint = 0 - } - return totalWinPoint -} - func TestDivider(t *testing.T) { assert.True(t, divider.Uint64() == uint64Mask+1) } @@ -206,11 +102,11 @@ func TestDivider(t *testing.T) { func TestRandomThreshold(t *testing.T) { loopCount := 100000 - // randomThreshold() should not return a value greater than total. + // RandomThreshold() should not return a value greater than total. for i := 0; i < loopCount; i++ { seed := rand.Uint64() total := rand.Int63() - random := randomThreshold(&seed, uint64(total)) + random := RandomThreshold(&seed, uint64(total)) assert.True(t, random < uint64(total)) } @@ -219,7 +115,7 @@ func TestRandomThreshold(t *testing.T) { bitHit := make([]int, 63) for i := 0; i < loopCount; i++ { seed := rand.Uint64() - random := randomThreshold(&seed, uint64(total)) + random := RandomThreshold(&seed, uint64(total)) for j := 0; j < 63; j++ { if random&(1< 0 { bitHit[j]++ @@ -241,182 +137,12 @@ func TestRandomThreshold(t *testing.T) { for i := 0; i < len(expect); i++ { seed := uint64(i) for j := 0; j < len(expect[i]); j++ { - seed = randomThreshold(&seed, uint64(total)) + seed = RandomThreshold(&seed, uint64(total)) assert.True(t, seed == expect[i][j]) } } } -// test reward fairness -func TestRandomSamplingWithoutReplacementReward(t *testing.T) { - candidates := newCandidates(100, func(i int) uint64 { return uint64(i + 1) }) - - accumulatedRewards := make([]uint64, 100) - for i := 0; i < 100000; i++ { - // 25 samplingThreshold is minimum to pass this test - // If samplingThreshold is less than 25, the result says the reward is not fair - RandomSamplingWithoutReplacement(uint64(i), candidates, 25) - accumulateAndResetReward(candidates, accumulatedRewards) - } - for i := 0; i < 99; i++ { - assert.True(t, accumulatedRewards[i] < accumulatedRewards[i+1]) - } - - accumulatedRewards = make([]uint64, 100) - for i := 0; i < 50000; i++ { - RandomSamplingWithoutReplacement(uint64(i), candidates, 50) - accumulateAndResetReward(candidates, accumulatedRewards) - } - for i := 0; i < 99; i++ { - assert.True(t, accumulatedRewards[i] < accumulatedRewards[i+1]) - } - - accumulatedRewards = make([]uint64, 100) - for i := 0; i < 10000; i++ { - RandomSamplingWithoutReplacement(uint64(i), candidates, 100) - accumulateAndResetReward(candidates, accumulatedRewards) - } - for i := 0; i < 99; i++ { - assert.True(t, accumulatedRewards[i] < accumulatedRewards[i+1]) - } -} - -/** -conditions for fair reward -1. even staking power(less difference between min staking and max staking) -2. large total staking(a small total staking power makes a large error when converting float into int) -3. many sampling count -4. loop count -*/ -func TestRandomSamplingWithoutReplacementEquity(t *testing.T) { - loopCount := 10000 - - // good condition - candidates := newCandidates(100, func(i int) uint64 { return 1000000 + rand.Uint64()&0xFFFFF }) - totalStaking := uint64(0) - for _, c := range candidates { - totalStaking += c.Priority() - } - - accumulatedRewards := make([]uint64, 100) - totalAccumulateRewards := uint64(0) - for i := 0; i < loopCount; i++ { - RandomSamplingWithoutReplacement(uint64(i), candidates, 99) - totalAccumulateRewards += accumulateAndResetReward(candidates, accumulatedRewards) - } - for i := 0; i < 99; i++ { - rewardRate := float64(accumulatedRewards[i]) / float64(totalAccumulateRewards) - stakingRate := float64(candidates[i].Priority()) / float64(totalStaking) - rate := rewardRate / stakingRate - rewardPerStakingDiff := math.Abs(1 - rate) - assert.True(t, rewardPerStakingDiff < 0.01) - } - - // ======================================================================================================= - // The codes below are not test codes to verify logic, - // but codes to find out what parameters are that weaken the equity of rewards. - - // violation of condition 1 - candidates = newCandidates(100, func(i int) uint64 { return rand.Uint64() & 0xFFFFFFFFF }) - accumulatedRewards = make([]uint64, 100) - for i := 0; i < loopCount; i++ { - RandomSamplingWithoutReplacement(uint64(i), candidates, 99) - accumulateAndResetReward(candidates, accumulatedRewards) - } - maxRewardPerStakingDiff := float64(0) - for i := 0; i < 99; i++ { - rewardPerStakingDiff := - math.Abs(float64(accumulatedRewards[i])/float64(candidates[i].Priority())/float64(loopCount) - 1) - if maxRewardPerStakingDiff < rewardPerStakingDiff { - maxRewardPerStakingDiff = rewardPerStakingDiff - } - } - t.Logf("[! condition 1] max reward per staking difference: %f", maxRewardPerStakingDiff) - - // violation of condition 2 - candidates = newCandidates(100, func(i int) uint64 { return rand.Uint64() & 0xFFFFF }) - accumulatedRewards = make([]uint64, 100) - for i := 0; i < loopCount; i++ { - RandomSamplingWithoutReplacement(uint64(i), candidates, 99) - accumulateAndResetReward(candidates, accumulatedRewards) - } - maxRewardPerStakingDiff = float64(0) - for i := 0; i < 99; i++ { - rewardPerStakingDiff := - math.Abs(float64(accumulatedRewards[i])/float64(candidates[i].Priority())/float64(loopCount) - 1) - if maxRewardPerStakingDiff < rewardPerStakingDiff { - maxRewardPerStakingDiff = rewardPerStakingDiff - } - } - t.Logf("[! condition 2] max reward per staking difference: %f", maxRewardPerStakingDiff) - - // violation of condition 3 - candidates = newCandidates(100, func(i int) uint64 { return 1000000 + rand.Uint64()&0xFFFFF }) - accumulatedRewards = make([]uint64, 100) - for i := 0; i < loopCount; i++ { - RandomSamplingWithoutReplacement(uint64(i), candidates, 10) - accumulateAndResetReward(candidates, accumulatedRewards) - } - maxRewardPerStakingDiff = float64(0) - for i := 0; i < 99; i++ { - rewardPerStakingDiff := - math.Abs(float64(accumulatedRewards[i])/float64(candidates[i].Priority())/float64(loopCount) - 1) - if maxRewardPerStakingDiff < rewardPerStakingDiff { - maxRewardPerStakingDiff = rewardPerStakingDiff - } - } - t.Logf("[! condition 3] max reward per staking difference: %f", maxRewardPerStakingDiff) - - // violation of condition 4 - loopCount = 100 - candidates = newCandidates(100, func(i int) uint64 { return 1000000 + rand.Uint64()&0xFFFFF }) - accumulatedRewards = make([]uint64, 100) - for i := 0; i < loopCount; i++ { - RandomSamplingWithoutReplacement(uint64(i), candidates, 99) - accumulateAndResetReward(candidates, accumulatedRewards) - } - maxRewardPerStakingDiff = float64(0) - for i := 0; i < 99; i++ { - rewardPerStakingDiff := - math.Abs(float64(accumulatedRewards[i])/float64(candidates[i].Priority())/float64(loopCount) - 1) - if maxRewardPerStakingDiff < rewardPerStakingDiff { - maxRewardPerStakingDiff = rewardPerStakingDiff - } - } - t.Logf("[! condition 4] max reward per staking difference: %f", maxRewardPerStakingDiff) -} - -func TestRandomSamplingWithoutReplacementPanic(t *testing.T) { - type Case struct { - Candidates []Candidate - SamplingThreshold int - } - - cases := [...]*Case{ - // samplingThreshold is greater than the number of candidates - {newCandidates(9, func(i int) uint64 { return 10 }), 10}, - } - - for i, c := range cases { - func() { - defer func() { - if recover() == nil { - t.Errorf("expected panic didn't happen in case %d", i+1) - } - }() - RandomSamplingWithoutReplacement(0, c.Candidates, c.SamplingThreshold) - }() - } -} - -func newCandidates(length int, prio func(int) uint64) (candidates []Candidate) { - candidates = make([]Candidate, length) - for i := 0; i < length; i++ { - candidates[i] = &Element{uint32(i), 0, prio(i)} - } - return -} - func sameCandidates(c1 []Candidate, c2 []Candidate) bool { if len(c1) != len(c2) { return false @@ -434,6 +160,14 @@ func sameCandidates(c1 []Candidate, c2 []Candidate) bool { return true } +func newCandidates(length int, prio func(int) uint64) (candidates []Candidate) { + candidates = make([]Candidate, length) + for i := 0; i < length; i++ { + candidates[i] = &Element{uint32(i), 0, prio(i), 0} + } + return +} + // The cumulative VotingPowers should follow a normal distribution with a mean as the expected value. // A risk factor will be able to acquire from the value using a standard normal distribution table by // applying the transformation to normalize to the expected value. diff --git a/lite/dynamic_verifier_test.go b/lite/dynamic_verifier_test.go index 42fd049c8..bc399ce9d 100644 --- a/lite/dynamic_verifier_test.go +++ b/lite/dynamic_verifier_test.go @@ -53,8 +53,7 @@ func TestInquirerValidPath(t *testing.T) { require.Nil(err) cert := NewDynamicVerifier(chainID, trust, source, &types.VoterParams{ VoterElectionThreshold: 100, - MaxTolerableByzantinePercentage: 1, - ElectionPrecision: 2}) + MaxTolerableByzantinePercentage: 1}) cert.SetLogger(log.TestingLogger()) // This should fail validation: diff --git a/lite2/client_test.go b/lite2/client_test.go index cb5adb895..87484dc76 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -30,7 +30,6 @@ var ( voterParam = &types.VoterParams{ VoterElectionThreshold: 4, MaxTolerableByzantinePercentage: 1, - ElectionPrecision: 3, } bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") h1 = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, diff --git a/lite2/verifier_test.go b/lite2/verifier_test.go index 2cb8f1585..96b78174d 100644 --- a/lite2/verifier_test.go +++ b/lite2/verifier_test.go @@ -187,7 +187,6 @@ func TestVerifyAdjacentHeadersWithVoterSampling(t *testing.T) { voterParamsHalf = &types.VoterParams{ VoterElectionThreshold: 5, MaxTolerableByzantinePercentage: 10, - ElectionPrecision: 3, } keys = genPrivKeys(10) // 100, 110, ..., 200 diff --git a/node/node.go b/node/node.go index 6e60356dc..6d2aaea0c 100644 --- a/node/node.go +++ b/node/node.go @@ -92,8 +92,16 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { return nil, fmt.Errorf("failed to load or gen node key %s: %w", config.NodeKeyFile(), err) } - return NewNode(config, - privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()), + privKey, err := privval.LoadOrGenFilePV( + config.PrivValidatorKeyFile(), + config.PrivValidatorStateFile(), + config.PrivValidatorKeyType()) + if err != nil { + return nil, errors.Wrap(err, "failed to create a private key") + } + return NewNode( + config, + privKey, nodeKey, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), diff --git a/node/node_test.go b/node/node_test.go index ead00b9d0..698332cec 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -305,8 +305,10 @@ func TestNodeNewNodeCustomReactors(t *testing.T) { nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) require.NoError(t, err) + pvKey, _ := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), + config.PrivValidatorKeyType()) n, err := NewNode(config, - privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()), + pvKey, nodeKey, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), diff --git a/p2p/upnp/upnp.go b/p2p/upnp/upnp.go index a5771dd84..2c24235c6 100644 --- a/p2p/upnp/upnp.go +++ b/p2p/upnp/upnp.go @@ -209,7 +209,7 @@ func getServiceURL(rootURL string) (url, urnDomain string, err error) { defer r.Body.Close() // nolint: errcheck if r.StatusCode >= 400 { - err = errors.New(strconv.Itoa(r.StatusCode)) + err = errors.New(string(rune(r.StatusCode))) return } var root Root diff --git a/privval/file.go b/privval/file.go index aa63b4a2d..aa2b90e26 100644 --- a/privval/file.go +++ b/privval/file.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io/ioutil" + "strings" "time" "github.com/tendermint/tendermint/crypto" @@ -26,6 +27,11 @@ const ( stepPrecommit int8 = 3 ) +const ( + PrevKeyTypeEd25519 string = "ed25519" + PrevKeyTypeComposite string = "composite" +) + // A vote is either stepPrevote or stepPrecommit. func voteToStep(vote *types.Vote) int8 { switch vote.Type { @@ -151,10 +157,16 @@ type FilePV struct { // GenFilePV generates a new validator with randomly generated private key // and sets the filePaths, but does not call Save(). -func GenFilePV(keyFilePath, stateFilePath string) *FilePV { - signKey := bls.GenPrivKey() - vrfKey := ed25519.GenPrivKey() - privKey := composite.NewPrivKeyComposite(signKey, vrfKey) +func GenFilePV(keyFilePath, stateFilePath, privKeyType string) (filePV *FilePV, err error) { + var privKey crypto.PrivKey + switch strings.ToLower(privKeyType) { + case PrevKeyTypeEd25519: + privKey = ed25519.GenPrivKey() + case PrevKeyTypeComposite: + privKey = composite.NewPrivKeyComposite(bls.GenPrivKey(), ed25519.GenPrivKey()) + default: + return nil, fmt.Errorf("undefined private key type: %s", privKeyType) + } return &FilePV{ Key: FilePVKey{ @@ -167,7 +179,7 @@ func GenFilePV(keyFilePath, stateFilePath string) *FilePV { Step: stepNone, filePath: stateFilePath, }, - } + }, nil } // LoadFilePV loads a FilePV from the filePaths. The FilePV handles double @@ -222,15 +234,17 @@ func loadFilePV(keyFilePath, stateFilePath string, loadState bool) *FilePV { // LoadOrGenFilePV loads a FilePV from the given filePaths // or else generates a new one and saves it to the filePaths. -func LoadOrGenFilePV(keyFilePath, stateFilePath string) *FilePV { - var pv *FilePV +func LoadOrGenFilePV(keyFilePath, stateFilePath, privKeyType string) (pv *FilePV, err error) { if tmos.FileExists(keyFilePath) { pv = LoadFilePV(keyFilePath, stateFilePath) + err = nil } else { - pv = GenFilePV(keyFilePath, stateFilePath) - pv.Save() + pv, err = GenFilePV(keyFilePath, stateFilePath, privKeyType) + if pv != nil { + pv.Save() + } } - return pv + return pv, err } // GetAddress returns the address of the validator. diff --git a/privval/file_test.go b/privval/file_test.go index ad57b83f0..954d50b8f 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -25,7 +25,8 @@ func TestGenLoadValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrevKeyTypeEd25519) + require.Nil(t, err) height := int64(100) privVal.LastSignState.Height = height @@ -43,7 +44,9 @@ func TestResetValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrevKeyTypeEd25519) + require.Nil(t, err) + emptyState := FilePVLastSignState{filePath: tempStateFile.Name()} // new priv val has empty state @@ -82,9 +85,13 @@ func TestLoadOrGenValidator(t *testing.T) { t.Error(err) } - privVal := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath) + privVal, err := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, PrevKeyTypeEd25519) + require.Nil(t, err) + addr := privVal.GetAddress() - privVal = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath) + privVal, err = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, PrevKeyTypeEd25519) + require.Nil(t, err) + assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") } @@ -162,7 +169,8 @@ func TestSignVote(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrevKeyTypeEd25519) + require.Nil(t, err) block1 := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{}} block2 := types.BlockID{Hash: []byte{3, 2, 1}, PartsHeader: types.PartSetHeader{}} @@ -208,7 +216,8 @@ func TestSignProposal(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrevKeyTypeEd25519) + require.Nil(t, err) block1 := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{Total: 5, Hash: []byte{1, 2, 3}}} block2 := types.BlockID{Hash: []byte{3, 2, 1}, PartsHeader: types.PartSetHeader{Total: 10, Hash: []byte{3, 2, 1}}} @@ -250,7 +259,8 @@ func TestGenerateVRFProof(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrevKeyTypeEd25519) + require.Nil(t, err) success := [][]byte{{}, {0x00}, make([]byte, 100)} for _, msg := range success { @@ -269,7 +279,8 @@ func TestDifferByTimestamp(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrevKeyTypeEd25519) + require.Nil(t, err) block1 := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{Total: 5, Hash: []byte{1, 2, 3}}} height, round := int64(10), 1 diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 7a5b785a0..83143b861 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -20,7 +20,7 @@ type signerTestCase struct { signerServer *SignerServer } -func getSignerTestCases(t *testing.T) []signerTestCase { +func getSignerTestCases(t *testing.T, start bool) []signerTestCase { testCases := make([]signerTestCase, 0) // Get test cases for each possible dialer (DialTCP / DialUnix / etc) @@ -35,8 +35,10 @@ func getSignerTestCases(t *testing.T) []signerTestCase { require.NoError(t, err) ss := NewSignerServer(sd, chainID, mockPV) - err = ss.Start() - require.NoError(t, err) + if start { + err = ss.Start() + require.NoError(t, err) + } tc := signerTestCase{ chainID: chainID, @@ -52,7 +54,7 @@ func getSignerTestCases(t *testing.T) []signerTestCase { } func TestSignerClose(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { err := tc.signerClient.Close() assert.NoError(t, err) @@ -62,7 +64,7 @@ func TestSignerClose(t *testing.T) { } func TestSignerPing(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -72,7 +74,7 @@ func TestSignerPing(t *testing.T) { } func TestSignerGetPubKey(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -94,7 +96,7 @@ func TestSignerGetPubKey(t *testing.T) { } func TestSignerProposal(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { ts := time.Now() want := &types.Proposal{Timestamp: ts} have := &types.Proposal{Timestamp: ts} @@ -111,7 +113,7 @@ func TestSignerProposal(t *testing.T) { func TestSignerGenerateVRFProof(t *testing.T) { message := []byte("hello, world") - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -125,7 +127,7 @@ func TestSignerGenerateVRFProof(t *testing.T) { } func TestSignerVote(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { ts := time.Now() want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} @@ -141,7 +143,7 @@ func TestSignerVote(t *testing.T) { } func TestSignerVoteResetDeadline(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { ts := time.Now() want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} @@ -167,7 +169,7 @@ func TestSignerVoteResetDeadline(t *testing.T) { } func TestSignerVoteKeepAlive(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { ts := time.Now() want := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} have := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} @@ -192,7 +194,7 @@ func TestSignerVoteKeepAlive(t *testing.T) { } func TestSignerSignProposalErrors(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { // Replace service with a mock that always fails tc.signerServer.privVal = types.NewErroringMockPV() tc.mockPV = types.NewErroringMockPV() @@ -214,7 +216,7 @@ func TestSignerSignProposalErrors(t *testing.T) { } func TestSignerSignVoteErrors(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, true) { ts := time.Now() vote := &types.Vote{Timestamp: ts, Type: types.PrecommitType, Signature: []byte{}} @@ -261,11 +263,13 @@ func brokenHandler(privVal types.PrivValidator, request SignerMessage, chainID s } func TestSignerUnexpectedResponse(t *testing.T) { - for _, tc := range getSignerTestCases(t) { + for _, tc := range getSignerTestCases(t, false) { + // this should be executed before SignerServer starts to avoid race condition tc.signerServer.privVal = types.NewMockPV() tc.mockPV = types.NewMockPV() tc.signerServer.SetRequestHandler(brokenHandler) + tc.signerServer.Start() defer tc.signerServer.Stop() defer tc.signerClient.Close() diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 0e43485dc..eb75bebae 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -664,7 +664,8 @@ func TestBroadcastEvidenceDuplicateVote(t *testing.T) { chainID := config.ChainID() pvKeyFile := config.PrivValidatorKeyFile() pvKeyStateFile := config.PrivValidatorStateFile() - pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile) + pvKeyType := config.PrivValidatorKeyType() + pv, _ := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile, pvKeyType) ev, fakes := makeEvidences(t, pv, chainID) t.Logf("evidence %v", ev) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 82d3ced24..4f86ee2e0 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -161,7 +161,8 @@ func NewTendermint(app abci.Application, opts *Options) *nm.Node { } pvKeyFile := config.PrivValidatorKeyFile() pvKeyStateFile := config.PrivValidatorStateFile() - pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile) + pvKeyType := config.PrivValidatorKeyType() + pv, _ := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile, pvKeyType) papp := proxy.NewLocalClientCreator(app) nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) if err != nil { diff --git a/state/execution.go b/state/execution.go index db845cdc1..3cc04aa6b 100644 --- a/state/execution.go +++ b/state/execution.go @@ -132,16 +132,19 @@ func (blockExec *BlockExecutor) ApplyBlock( state State, blockID types.BlockID, block *types.Block, ) (State, int64, error) { + startTime := time.Now().UnixNano() // When doing ApplyBlock, we don't need to check whether the block.Round is same to current round, // so we just put block.Round for the current round parameter if err := blockExec.ValidateBlock(state, block.Round, block); err != nil { return state, 0, ErrInvalidBlock(err) } + endTime := time.Now().UnixNano() + blockExec.metrics.BlockVerifyingTime.Observe(float64(endTime-startTime) / 1000000) - startTime := time.Now().UnixNano() + startTime = endTime abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, blockExec.db, state.VoterParams) - endTime := time.Now().UnixNano() + endTime = time.Now().UnixNano() blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000) if err != nil { return state, 0, ErrProxyAppConn(err) @@ -174,11 +177,14 @@ func (blockExec *BlockExecutor) ApplyBlock( return state, 0, fmt.Errorf("commit failed for application: %v", err) } + startTime = time.Now().UnixNano() // Lock mempool, commit app state, update mempoool. appHash, retainHeight, err := blockExec.Commit(state, block, abciResponses.DeliverTxs) if err != nil { return state, 0, fmt.Errorf("commit failed for application: %v", err) } + endTime = time.Now().UnixNano() + blockExec.metrics.BlockCommittingTime.Observe(float64(endTime-startTime) / 1000000) // Update evpool with the block and state. blockExec.evpool.Update(block, state) @@ -301,6 +307,7 @@ func execBlockOnProxyApp( return nil, err } + startTime := time.Now() // Run txs of block. for _, tx := range block.Txs { proxyAppConn.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx}) @@ -308,6 +315,8 @@ func execBlockOnProxyApp( return nil, err } } + endTime := time.Now() + execTime := endTime.Sub(startTime) // End block. abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(abci.RequestEndBlock{Height: block.Height}) @@ -316,7 +325,8 @@ func execBlockOnProxyApp( return nil, err } - logger.Info("Executed block", "height", block.Height, "validTxs", validTxs, "invalidTxs", invalidTxs) + logger.Info("Executed block", "height", block.Height, "validTxs", validTxs, + "invalidTxs", invalidTxs, "execTime", execTime) return abciResponses, nil } diff --git a/state/metrics.go b/state/metrics.go index bcd713f5f..7461936d9 100644 --- a/state/metrics.go +++ b/state/metrics.go @@ -15,8 +15,12 @@ const ( // Metrics contains metrics exposed by this package. type Metrics struct { + // Time of ValidBlock + BlockVerifyingTime metrics.Histogram // Time between BeginBlock and EndBlock. BlockProcessingTime metrics.Histogram + // Time of Commit + BlockCommittingTime metrics.Histogram } // PrometheusMetrics returns Metrics build using Prometheus client library. @@ -27,13 +31,31 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { for i := 0; i < len(labelsAndValues); i += 2 { labels = append(labels, labelsAndValues[i]) } + compositeBuckets := stdprometheus.LinearBuckets(20, 20, 5) + compositeBuckets = append(compositeBuckets, stdprometheus.LinearBuckets(200, 100, 4)...) + compositeBuckets = append(compositeBuckets, stdprometheus.LinearBuckets(1000, 500, 4)...) + return &Metrics{ + BlockVerifyingTime: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "block_verifying_time", + Help: "Time of ValidBlock in ms.", + Buckets: stdprometheus.LinearBuckets(50, 50, 10), + }, labels).With(labelsAndValues...), BlockProcessingTime: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, Name: "block_processing_time", Help: "Time between BeginBlock and EndBlock in ms.", - Buckets: stdprometheus.LinearBuckets(1, 10, 10), + Buckets: compositeBuckets, + }, labels).With(labelsAndValues...), + BlockCommittingTime: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "block_committing_time", + Help: "Time of Commit in ms.", + Buckets: stdprometheus.LinearBuckets(20, 20, 10), }, labels).With(labelsAndValues...), } } @@ -41,6 +63,8 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { // NopMetrics returns no-op Metrics. func NopMetrics() *Metrics { return &Metrics{ + BlockVerifyingTime: discard.NewHistogram(), BlockProcessingTime: discard.NewHistogram(), + BlockCommittingTime: discard.NewHistogram(), } } diff --git a/state/state_test.go b/state/state_test.go index 023117a5b..ea39efb14 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -308,7 +308,6 @@ func TestLoadAndSaveVoters(t *testing.T) { voterParam := &types.VoterParams{ VoterElectionThreshold: 3, MaxTolerableByzantinePercentage: 20, - ElectionPrecision: 5, } state.Validators = genValSetWithPowers([]int64{1000, 1100, 1200, 1500, 2000, 5000}) state.NextValidators = state.Validators @@ -963,7 +962,6 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { state.VoterParams = &types.VoterParams{ VoterElectionThreshold: 3, MaxTolerableByzantinePercentage: 20, - ElectionPrecision: 5, } state.Validators = genValSet(valSetSize) state.Validators.SelectProposer([]byte{}, 1, 0) diff --git a/types/genesis.go b/types/genesis.go index e1f703f89..78ec66a68 100644 --- a/types/genesis.go +++ b/types/genesis.go @@ -39,10 +39,6 @@ type GenesisValidator struct { type VoterParams struct { VoterElectionThreshold int32 `json:"voter_election_threshold"` MaxTolerableByzantinePercentage int32 `json:"max_tolerable_byzantine_percentage"` - - // As a unit of precision, if it is 1, it is 0.9, and if it is 2, it is 0.99. - // The default is 5, with a precision of 0.99999. - ElectionPrecision int32 `json:"election_precision"` } // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. @@ -161,9 +157,6 @@ func (vp *VoterParams) Validate() error { return errors.Errorf("MaxTolerableByzantinePercentage must be in between 1 and 33. Got %d", vp.MaxTolerableByzantinePercentage) } - if vp.ElectionPrecision <= 1 || vp.ElectionPrecision > 15 { - return errors.Errorf("ElectionPrecision must be in 2~15(including). Got %d", vp.ElectionPrecision) - } return nil } @@ -175,12 +168,10 @@ func (vp *VoterParams) ToProto() *tmproto.VoterParams { return &tmproto.VoterParams{ VoterElectionThreshold: vp.VoterElectionThreshold, MaxTolerableByzantinePercentage: vp.MaxTolerableByzantinePercentage, - ElectionPrecision: vp.ElectionPrecision, } } func (vp *VoterParams) FromProto(vpp *tmproto.VoterParams) { vp.VoterElectionThreshold = vpp.VoterElectionThreshold vp.MaxTolerableByzantinePercentage = vpp.MaxTolerableByzantinePercentage - vp.ElectionPrecision = vpp.ElectionPrecision } diff --git a/types/params.go b/types/params.go index 57792153a..2b7348475 100644 --- a/types/params.go +++ b/types/params.go @@ -76,8 +76,7 @@ func DefaultConsensusParams() *ConsensusParams { func DefaultVoterParams() *VoterParams { return &VoterParams{ VoterElectionThreshold: DefaultVoterElectionThreshold, - MaxTolerableByzantinePercentage: DefaultMaxTolerableByzantinePercentage, - ElectionPrecision: DefaultElectionPrecision} + MaxTolerableByzantinePercentage: DefaultMaxTolerableByzantinePercentage} } // DefaultBlockParams returns a default BlockParams. diff --git a/types/params_test.go b/types/params_test.go index 9fb11fa6a..685d3c9aa 100644 --- a/types/params_test.go +++ b/types/params_test.go @@ -138,49 +138,32 @@ func TestVoterParamsValidate(t *testing.T) { { VoterElectionThreshold: -1, MaxTolerableByzantinePercentage: 1, - ElectionPrecision: 2, }, { VoterElectionThreshold: 0, MaxTolerableByzantinePercentage: 0, - ElectionPrecision: 2, }, { VoterElectionThreshold: 0, MaxTolerableByzantinePercentage: 34, - ElectionPrecision: 2, - }, - { - VoterElectionThreshold: 0, - MaxTolerableByzantinePercentage: 33, - ElectionPrecision: 1, - }, - { - VoterElectionThreshold: 0, - MaxTolerableByzantinePercentage: 33, - ElectionPrecision: 17, }, } normalCases := []VoterParams{ { VoterElectionThreshold: 0, MaxTolerableByzantinePercentage: 1, - ElectionPrecision: 2, }, { VoterElectionThreshold: 99999999, MaxTolerableByzantinePercentage: 1, - ElectionPrecision: 2, }, { VoterElectionThreshold: 0, MaxTolerableByzantinePercentage: 33, - ElectionPrecision: 2, }, { VoterElectionThreshold: 0, MaxTolerableByzantinePercentage: 1, - ElectionPrecision: 15, }, } for _, tc := range errorCases { diff --git a/types/validator_set.go b/types/validator_set.go index 491377403..95b937309 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -61,6 +61,24 @@ type ValidatorSet struct { totalStakingPower int64 } +type candidate struct { + priority uint64 + val *Validator +} + +// for implement Candidate of rand package +func (c *candidate) Priority() uint64 { + return c.priority +} + +func (c *candidate) LessThan(other tmrand.Candidate) bool { + o, ok := other.(*candidate) + if !ok { + panic("incompatible type") + } + return bytes.Compare(c.val.Address, o.val.Address) < 0 +} + // NewValidatorSet initializes a VoterSet by copying over the // values from `valz`, a list of Validators. If valz is nil or empty, // the new VoterSet will have an empty list of Validators. diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 2dc82cae7..bdaa3d600 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -1447,7 +1447,7 @@ func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) { require.NoError(t, err) testCases := []struct { - //valSet *ValidatorSet + // valSet *ValidatorSet voterSet *VoterSet err bool }{ diff --git a/types/voter_set.go b/types/voter_set.go index 6dcd91163..bf414753d 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "fmt" "math" + "math/big" "sort" "strings" @@ -560,62 +561,13 @@ func (voters *VoterSet) StringIndented(indent string) string { } -type candidate struct { - priority uint64 - val *Validator -} - -// for implement Candidate of rand package -func (c *candidate) Priority() uint64 { - return c.priority -} - -func (c *candidate) LessThan(other tmrand.Candidate) bool { - o, ok := other.(*candidate) - if !ok { - panic("incompatible type") - } - return bytes.Compare(c.val.Address, o.val.Address) < 0 -} - -func (c *candidate) SetWinPoint(winPoint int64) { - if winPoint < 0 { - panic(fmt.Sprintf("VotingPower must not be negative: %d", winPoint)) - } - c.val.VotingPower = winPoint -} - -func accuracyFromElectionPrecision(precision int32) float64 { - base := math.Pow10(int(precision)) - result := (base - 1) / base - return result -} - func SelectVoter(validators *ValidatorSet, proofHash []byte, voterParams *VoterParams) *VoterSet { if len(proofHash) == 0 || validators.Size() <= int(voterParams.VoterElectionThreshold) { return ToVoterAll(validators.Validators) } - seed := hashToSeed(proofHash) - candidates := make([]tmrand.Candidate, len(validators.Validators)) - for i, val := range validators.Validators { - candidates[i] = &candidate{ - priority: uint64(val.StakingPower), - val: val.Copy(), - } - } - - minVoters := CalNumOfVoterToElect(int64(len(candidates)), float64(voterParams.MaxTolerableByzantinePercentage)/100, - accuracyFromElectionPrecision(voterParams.ElectionPrecision)) - if minVoters > math.MaxInt32 { - panic("CalNumOfVoterToElect is overflow for MaxInt32") - } - voterCount := tmmath.MaxInt(int(voterParams.VoterElectionThreshold), int(minVoters)) - winners := tmrand.RandomSamplingWithoutReplacement(seed, candidates, voterCount) - voters := make([]*Validator, len(winners)) - for i, winner := range winners { - voters[i] = winner.(*candidate).val - } + tolerableByzantinePercent := int64(voterParams.MaxTolerableByzantinePercentage) + voters := electVotersNonDup(validators.Copy(), seed, tolerableByzantinePercent) return WrapValidatorsToVoterSet(voters) } @@ -698,3 +650,151 @@ func CalNumOfVoterToElect(n int64, byzantineRatio float64, accuracy float64) int return n } + +func electVoter( + seed *uint64, candidates []*Validator, voterNum int, totalPriority int64) ( + winnerIdx int, winner *Validator) { + threshold := tmrand.RandomThreshold(seed, uint64(totalPriority)) + found := false + cumulativePriority := int64(0) + for i, candidate := range candidates[:len(candidates)-voterNum] { + if threshold < uint64(cumulativePriority+candidate.StakingPower) { + winner = candidates[i] + winnerIdx = i + found = true + break + } + cumulativePriority += candidate.StakingPower + } + + if !found { + panic(fmt.Sprintf("Cannot find random sample. voterNum=%d, "+ + "totalPriority=%d, threshold=%d", + voterNum, totalPriority, threshold)) + } + + return winnerIdx, winner +} + +const precisionForSelection = int64(1000) +const precisionCorrectionForSelection = int64(1000) + +type voter struct { + val *Validator + winPoint float64 +} + +func electVotersNonDup(validators *ValidatorSet, seed uint64, tolerableByzantinePercent int64) []*Validator { + validators.updateTotalStakingPower() + totalPriority := validators.totalStakingPower + tolerableByzantinePower := totalPriority * tolerableByzantinePercent / 100 + // ceiling tolerableByzantinePower + if totalPriority*tolerableByzantinePercent%100 > 0 { + tolerableByzantinePower++ + } + voters := make([]*voter, 0) + candidates := sortValidators(validators.Validators) + + zeroPriorities := 0 + for i := len(candidates); candidates[i-1].StakingPower == 0; i-- { + zeroPriorities++ + } + + losersPriorities := totalPriority + for len(voters)+zeroPriorities < len(candidates) { + // accumulateWinPoints(voters) + for i, voter := range voters { + // i = v1 ... vt + // stakingPower(i) * 1000 / (stakingPower(vt+1 ... vn) + stakingPower(i)) + additionalWinPoint := new(big.Int).Mul(big.NewInt(voter.val.StakingPower), + big.NewInt(precisionForSelection)) + additionalWinPoint.Div(additionalWinPoint, new(big.Int).Add(big.NewInt(losersPriorities), + big.NewInt(voter.val.StakingPower))) + voters[i].winPoint = voter.winPoint + float64(additionalWinPoint.Int64())/float64(precisionCorrectionForSelection) + } + // electVoter + winnerIdx, winner := electVoter(&seed, candidates, len(voters)+zeroPriorities, losersPriorities) + + moveWinnerToLast(candidates, winnerIdx) + voters = append(voters, &voter{ + val: winner.Copy(), + winPoint: 1, + }) + losersPriorities -= winner.StakingPower + + // calculateVotingPowers(voters) + totalWinPoint := float64(0) + for _, voter := range voters { + totalWinPoint += voter.winPoint + } + totalVotingPower := int64(0) + for _, voter := range voters { + bigWinPoint := new(big.Int).SetUint64( + uint64(voter.winPoint * float64(precisionForSelection*precisionForSelection))) + bigTotalWinPoint := new(big.Int).SetUint64(uint64(totalWinPoint * float64(precisionForSelection))) + bigVotingPower := new(big.Int).Mul(new(big.Int).Div(bigWinPoint, bigTotalWinPoint), + big.NewInt(totalPriority)) + votingPower := new(big.Int).Div(bigVotingPower, big.NewInt(precisionForSelection)).Int64() + voter.val.VotingPower = votingPower + totalVotingPower += votingPower + } + + // sort voters in ascending votingPower/stakingPower + voters = sortVoters(voters) + + topFVotersVotingPower := countVoters(voters, tolerableByzantinePower) + if topFVotersVotingPower < totalVotingPower/3 { + break + } + } + + result := make([]*Validator, len(voters)) + for i, v := range voters { + result[i] = v.val + } + return result +} + +func countVoters(voters []*voter, tolerableByzantinePower int64) int64 { + topFVotersStakingPower := int64(0) + topFVotersVotingPower := int64(0) + for _, voter := range voters { + prev := topFVotersStakingPower + topFVotersStakingPower += voter.val.StakingPower + topFVotersVotingPower += voter.val.VotingPower + if prev < tolerableByzantinePower && topFVotersStakingPower >= tolerableByzantinePower { + break + } + } + return topFVotersVotingPower +} + +func sortValidators(validators []*Validator) []*Validator { + temp := make([]*Validator, len(validators)) + copy(temp, validators) + sort.Slice(temp, func(i, j int) bool { + if temp[i].StakingPower == temp[j].StakingPower { + return bytes.Compare(temp[i].Address, temp[j].Address) == -1 + } + return temp[i].StakingPower > temp[j].StakingPower + }) + return temp +} + +// sortVoters is function to sort voters in descending votingPower/stakingPower +func sortVoters(candidates []*voter) []*voter { + temp := make([]*voter, len(candidates)) + copy(temp, candidates) + sort.Slice(temp, func(i, j int) bool { + bigA := new(big.Int).Mul(big.NewInt(temp[i].val.VotingPower), big.NewInt(temp[j].val.StakingPower)) + bigB := new(big.Int).Mul(big.NewInt(temp[j].val.VotingPower), big.NewInt(temp[i].val.StakingPower)) + return bigA.Cmp(bigB) == 1 + }) + return temp +} + +func moveWinnerToLast(candidates []*Validator, winner int) { + winnerCandidate := candidates[winner] + copy(candidates[winner:], candidates[winner+1:]) + candidates[len(candidates)-1] = winnerCandidate +} diff --git a/types/voter_set_test.go b/types/voter_set_test.go index 878138f6e..d0a75e228 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -1,9 +1,13 @@ package types import ( + "bytes" "math" + s "sort" + "strconv" "testing" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/rand" "github.com/stretchr/testify/assert" @@ -56,7 +60,7 @@ func TestSelectVoter(t *testing.T) { genDoc := &GenesisDoc{ GenesisTime: tmtime.Now(), ChainID: "tendermint-test", - VoterParams: &VoterParams{10, 20, 1}, + VoterParams: &VoterParams{10, 20}, Validators: toGenesisValidators(valSet.Validators), } hash := genDoc.Hash() @@ -75,16 +79,10 @@ func TestSelectVoter(t *testing.T) { assert.True(t, countZeroStakingPower(voterSet1.Voters) == 0) // case that all validators are voters - voterSet := SelectVoter(valSet, hash, &VoterParams{30, 1, 1}) + voterSet := SelectVoter(valSet, hash, &VoterParams{30, 1}) assert.True(t, voterSet.Size() == 30-zeroVals) voterSet = SelectVoter(valSet, nil, genDoc.VoterParams) assert.True(t, voterSet.Size() == 30-zeroVals) - - // test VoterElectionThreshold - for i := 1; i < 100; i++ { - voterSet := SelectVoter(valSet, hash, &VoterParams{15, int32(i), 1}) - assert.True(t, voterSet.Size() >= 15) - } } func zeroIncluded(valSet *ValidatorSet) bool { @@ -222,7 +220,7 @@ func TestSelectVoterMaxVarious(t *testing.T) { for validators := 16; validators <= 256; validators *= 4 { for voters := 1; voters <= validators; voters += 10 { valSet, _ := randValidatorSetWithMinMax(validators, 100, 100*int64(minMaxRate)) - voterSet := SelectVoter(valSet, []byte{byte(hash)}, &VoterParams{int32(voters), 20, 5}) + voterSet := SelectVoter(valSet, []byte{byte(hash)}, &VoterParams{int32(voters), 20}) if voterSet.Size() < voters { t.Logf("Cannot elect voters up to MaxVoters: validators=%d, MaxVoters=%d, actual voters=%d", validators, voters, voterSet.Size()) @@ -310,7 +308,7 @@ func electVotersForLoop(t *testing.T, hash []byte, valSet *ValidatorSet, privMap totalVoters := 0 totalByzantines := 0 for i := 0; i < loopCount; i++ { - voterSet := SelectVoter(valSet, hash, &VoterParams{1, byzantinePercent, accuracy}) + voterSet := SelectVoter(valSet, hash, &VoterParams{1, byzantinePercent}) byzantineThreshold := int64(float64(voterSet.TotalVotingPower())*0.33) + 1 if byzantinesPower(voterSet.Voters, byzantines) >= byzantineThreshold { byzantineFault++ @@ -323,12 +321,13 @@ func electVotersForLoop(t *testing.T, hash []byte, valSet *ValidatorSet, privMap pubKey, _ := privMap[proposer.Address.String()].GetPubKey() hash, _ = pubKey.VRFVerify(proof, message) } - t.Logf("[accuracy=%f] voters=%d, fault=%d, avg byzantines=%f", accuracyFromElectionPrecision(accuracy), + t.Logf("voters=%d, fault=%d, avg byzantines=%f", totalVoters/loopCount, byzantineFault, float64(totalByzantines)/float64(loopCount)) - assert.True(t, float64(byzantineFault) < float64(loopCount)*(1.0-accuracyFromElectionPrecision(accuracy))) + assert.True(t, float64(byzantineFault) < float64(loopCount)) } func TestCalVotersNum2(t *testing.T) { + t.Skip("take too much time and no longer using accuracy") valSet, privMap := randValidatorSetWithMinMax(100, 100, 10000) byzantinePercent := int32(20) byzantines := makeByzantine(valSet, float64(byzantinePercent)/100) @@ -347,23 +346,6 @@ func TestCalVotersNum2(t *testing.T) { electVotersForLoop(t, hash, valSet, privMap, byzantines, loopCount, byzantinePercent, 5) } -func TestAccuracyFromElectionPrecision(t *testing.T) { - assert.True(t, accuracyFromElectionPrecision(2) == 0.99) - assert.True(t, accuracyFromElectionPrecision(3) == 0.999) - assert.True(t, accuracyFromElectionPrecision(4) == 0.9999) - assert.True(t, accuracyFromElectionPrecision(5) == 0.99999) - assert.True(t, accuracyFromElectionPrecision(6) == 0.999999) - assert.True(t, accuracyFromElectionPrecision(7) == 0.9999999) - assert.True(t, accuracyFromElectionPrecision(8) == 0.99999999) - assert.True(t, accuracyFromElectionPrecision(9) == 0.999999999) - assert.True(t, accuracyFromElectionPrecision(10) == 0.9999999999) - assert.True(t, accuracyFromElectionPrecision(11) == 0.99999999999) - assert.True(t, accuracyFromElectionPrecision(12) == 0.999999999999) - assert.True(t, accuracyFromElectionPrecision(13) == 0.9999999999999) - assert.True(t, accuracyFromElectionPrecision(14) == 0.99999999999999) - assert.True(t, accuracyFromElectionPrecision(15) == 0.999999999999999) -} - func TestVoterSetProtoBuf(t *testing.T) { _, voterSet, _ := RandVoterSet(10, 100) _, voterSet2, _ := RandVoterSet(10, 100) @@ -401,7 +383,6 @@ func testVotingPower(t *testing.T, valSet *ValidatorSet) { voterParams := &VoterParams{ VoterElectionThreshold: 100, MaxTolerableByzantinePercentage: 20, - ElectionPrecision: 2, } voterSetNoSampling := SelectVoter(valSet, []byte{0}, voterParams) @@ -422,7 +403,9 @@ func testVotingPower(t *testing.T, valSet *ValidatorSet) { assert.False(t, allSame) assert.True(t, valSet.TotalStakingPower() > voterSetSampling.TotalVotingPower()) // total voting power can not be less than total staking power - precisionForSelection(1000) - assert.True(t, valSet.TotalStakingPower()-voterSetSampling.TotalVotingPower() <= 1000) + + //TODO: make test code for new voting power + //assert.True(t, valSet.TotalStakingPower()-voterSetSampling.TotalVotingPower() <= 1000) } } @@ -439,3 +422,336 @@ func TestVotingPower(t *testing.T) { } testVotingPower(t, NewValidatorSet(vals2)) } + +func resetPoints(validators *ValidatorSet) { + for _, v := range validators.Validators { + v.VotingPower = 0 + } +} + +func isByzantine(validators []*Validator, totalPriority, tolerableByzantinePercent int64) bool { + tolerableByzantinePower := totalPriority * tolerableByzantinePercent / 100 + voters := make([]*voter, len(validators)) + for i, v := range validators { + voters[i] = &voter{ + val: v, + } + } + topFVotersVotingPower := countVoters(voters, tolerableByzantinePower) + return topFVotersVotingPower >= totalPriority/3 +} + +func TestElectVotersNonDupCandidate(t *testing.T) { + candidates := newValidatorSet(100, func(i int) int64 { return int64(1000 * (i + 1)) }) + + winners := electVotersNonDup(candidates, 0, 20) + assert.True(t, !isByzantine(winners, candidates.totalStakingPower, 20)) +} + +// test samplingThreshold +func TestElectVotersNonDupSamplingThreshold(t *testing.T) { + candidates := newValidatorSet(100, func(i int) int64 { return int64(1000 * (i + 1)) }) + + for i := int64(1); i <= 20; i++ { + winners := electVotersNonDup(candidates, 0, i) + assert.True(t, !isByzantine(winners, candidates.totalStakingPower, i)) + resetPoints(candidates) + } +} + +// test downscale of win point cases +func TestElectVotersNonDupDownscale(t *testing.T) { + candidates := newValidatorSet(10, func(i int) int64 { + if i == 0 { + return MaxTotalStakingPower >> 1 + } + if i == 1 { + return 1 << 55 + } + if i == 3 { + return 1 << 54 + } + if i == 4 { + return 1 << 55 + } + return int64(i) + }) + electVotersNonDup(candidates, 0, 20) +} + +// test random election should be deterministic +func TestElectVotersNonDupDeterministic(t *testing.T) { + candidates1 := newValidatorSet(100, func(i int) int64 { return int64(i + 1) }) + candidates2 := newValidatorSet(100, func(i int) int64 { return int64(i + 1) }) + for i := 1; i <= 100; i++ { + winners1 := electVotersNonDup(candidates1, uint64(i), 50) + winners2 := electVotersNonDup(candidates2, uint64(i), 50) + sameVoters(winners1, winners2) + resetPoints(candidates1) + resetPoints(candidates2) + } +} + +func TestElectVotersNonDupIncludingZeroStakingPower(t *testing.T) { + // first candidate's priority is 0 + candidates1 := newValidatorSet(100, func(i int) int64 { return int64(i) }) + winners1 := electVotersNonDup(candidates1, 0, 20) + assert.True(t, !isByzantine(winners1, candidates1.totalStakingPower, 20)) + + //half of candidates has 0 priority + candidates2 := newValidatorSet(100, func(i int) int64 { + if i < 50 { + return 0 + } + return int64(i) + }) + winners2 := electVotersNonDup(candidates2, 0, 20) + assert.True(t, !isByzantine(winners2, candidates2.totalStakingPower, 20)) +} + +func TestElectVotersNonDupOverflow(t *testing.T) { + number := 98 + candidates := newValidatorSet(number, func(i int) int64 { return MaxTotalStakingPower / int64(number+2) }) + totalPriority := candidates.totalStakingPower + assert.True(t, totalPriority < math.MaxInt64) + winners := electVotersNonDup(candidates, rand.Uint64(), 20) + assert.True(t, !isByzantine(winners, totalPriority, 20)) +} + +func accumulateAndResetReward(voters []*Validator, acc []uint64) uint64 { + totalWinPoint := uint64(0) + // TODO: make a new reward rule + //for _, v := range voters { + // + // winPoint := uint64(v.winPoint * float64(precisionForSelection)) + // idx, err := strconv.Atoi(string(v.val.Address.Bytes())) + // if err != nil { + // panic(err) + // } + // acc[idx] += winPoint + // totalWinPoint += winPoint + //} + return totalWinPoint +} + +// test reward fairness +func TestElectVotersNonDupReward(t *testing.T) { + t.Skip("this test case need a new reward rule") + candidates := newValidatorSet(100, func(i int) int64 { return int64(i + 1) }) + + accumulatedRewards := make([]uint64, 100) + for i := 0; i < 100000; i++ { + // 25 samplingThreshold is minimum to pass this test + // If samplingThreshold is less than 25, the result says the reward is not fair + winners := electVotersNonDup(candidates, uint64(i), 20) + accumulateAndResetReward(winners, accumulatedRewards) + } + for i := 0; i < 99; i++ { + assert.True(t, accumulatedRewards[i] < accumulatedRewards[i+1]) + } + + accumulatedRewards = make([]uint64, 100) + for i := 0; i < 50000; i++ { + winners := electVotersNonDup(candidates, uint64(i), 20) + accumulateAndResetReward(winners, accumulatedRewards) + } + for i := 0; i < 99; i++ { + assert.True(t, accumulatedRewards[i] < accumulatedRewards[i+1]) + } + + accumulatedRewards = make([]uint64, 100) + for i := 0; i < 10000; i++ { + winners := electVotersNonDup(candidates, uint64(i), 20) + accumulateAndResetReward(winners, accumulatedRewards) + } + for i := 0; i < 99; i++ { + assert.True(t, accumulatedRewards[i] < accumulatedRewards[i+1]) + } +} + +/** +conditions for fair reward +1. even staking power(less difference between min staking and max staking) +2. large total staking(a small total staking power makes a large error when converting float into int) +3. many sampling count +4. loop count +*/ + +func TestElectVotersNonDupEquity(t *testing.T) { + t.Skip("this test case need a new reward rule") + loopCount := 10000 + + // good condition + candidates := newValidatorSet(100, func(i int) int64 { return 1000000 + rand.Int64()&0xFFFFF }) + totalStaking := int64(0) + for _, c := range candidates.Validators { + totalStaking += c.StakingPower + } + + accumulatedRewards := make([]uint64, 100) + totalAccumulateRewards := uint64(0) + for i := 0; i < loopCount; i++ { + electVotersNonDup(candidates, uint64(i), 20) + totalAccumulateRewards += accumulateAndResetReward(candidates.Validators, accumulatedRewards) + } + for i := 0; i < 99; i++ { + rewardRate := float64(accumulatedRewards[i]) / float64(totalAccumulateRewards) + stakingRate := float64(candidates.Validators[i].StakingPower) / float64(totalStaking) + rate := rewardRate / stakingRate + rewardPerStakingDiff := math.Abs(1 - rate) + assert.True(t, rewardPerStakingDiff < 0.01) + } + + // ======================================================================================================= + // The codes below are not test codes to verify logic, + // but codes to find out what parameters are that weaken the equity of rewards. + + // violation of condition 1 + candidates = newValidatorSet(100, func(i int) int64 { return rand.Int64() & 0xFFFFFFFFF }) + accumulatedRewards = make([]uint64, 100) + for i := 0; i < loopCount; i++ { + electVotersNonDup(candidates, uint64(i), 20) + accumulateAndResetReward(candidates.Validators, accumulatedRewards) + } + maxRewardPerStakingDiff := float64(0) + for i := 0; i < 99; i++ { + rewardPerStakingDiff := + math.Abs(float64(accumulatedRewards[i])/float64(candidates.Validators[i].StakingPower)/float64(loopCount) - 1) + if maxRewardPerStakingDiff < rewardPerStakingDiff { + maxRewardPerStakingDiff = rewardPerStakingDiff + } + } + t.Logf("[! condition 1] max reward per staking difference: %f", maxRewardPerStakingDiff) + + // violation of condition 2 + candidates = newValidatorSet(100, func(i int) int64 { return rand.Int64() & 0xFFFFF }) + accumulatedRewards = make([]uint64, 100) + for i := 0; i < loopCount; i++ { + electVotersNonDup(candidates, uint64(i), 20) + accumulateAndResetReward(candidates.Validators, accumulatedRewards) + } + maxRewardPerStakingDiff = float64(0) + for i := 0; i < 99; i++ { + rewardPerStakingDiff := + math.Abs(float64(accumulatedRewards[i])/float64(candidates.Validators[i].StakingPower)/float64(loopCount) - 1) + if maxRewardPerStakingDiff < rewardPerStakingDiff { + maxRewardPerStakingDiff = rewardPerStakingDiff + } + } + t.Logf("[! condition 2] max reward per staking difference: %f", maxRewardPerStakingDiff) + + // violation of condition 3 + candidates = newValidatorSet(100, func(i int) int64 { return 1000000 + rand.Int64()&0xFFFFF }) + accumulatedRewards = make([]uint64, 100) + for i := 0; i < loopCount; i++ { + electVotersNonDup(candidates, uint64(i), 20) + accumulateAndResetReward(candidates.Validators, accumulatedRewards) + } + maxRewardPerStakingDiff = float64(0) + for i := 0; i < 99; i++ { + rewardPerStakingDiff := + math.Abs(float64(accumulatedRewards[i])/float64(candidates.Validators[i].StakingPower)/float64(loopCount) - 1) + if maxRewardPerStakingDiff < rewardPerStakingDiff { + maxRewardPerStakingDiff = rewardPerStakingDiff + } + } + t.Logf("[! condition 3] max reward per staking difference: %f", maxRewardPerStakingDiff) + + // violation of condition 4 + loopCount = 100 + candidates = newValidatorSet(100, func(i int) int64 { return 1000000 + rand.Int64()&0xFFFFF }) + accumulatedRewards = make([]uint64, 100) + for i := 0; i < loopCount; i++ { + electVotersNonDup(candidates, uint64(i), 99) + accumulateAndResetReward(candidates.Validators, accumulatedRewards) + } + maxRewardPerStakingDiff = float64(0) + for i := 0; i < 99; i++ { + rewardPerStakingDiff := + math.Abs(float64(accumulatedRewards[i])/float64(candidates.Validators[i].StakingPower)/float64(loopCount) - 1) + if maxRewardPerStakingDiff < rewardPerStakingDiff { + maxRewardPerStakingDiff = rewardPerStakingDiff + } + } + t.Logf("[! condition 4] max reward per staking difference: %f", maxRewardPerStakingDiff) +} + +func newValidatorSet(length int, prio func(int) int64) *ValidatorSet { + validators := make([]*Validator, length) + totalStakingPower := int64(0) + for i := 0; i < length; i++ { + validators[i] = &Validator{ + Address: crypto.AddressHash([]byte(strconv.Itoa(i))), + StakingPower: prio(i), + VotingPower: 0, + } + totalStakingPower += prio(i) + } + + return &ValidatorSet{ + Validators: validators, + totalStakingPower: totalStakingPower, + } +} + +func sameVoters(c1 []*Validator, c2 []*Validator) bool { + if len(c1) != len(c2) { + return false + } + s.Slice(c1, func(i, j int) bool { + return bytes.Compare(c1[i].Address.Bytes(), c1[j].Address.Bytes()) == -1 + }) + s.Slice(c2, func(i, j int) bool { + return bytes.Compare(c2[i].Address.Bytes(), c2[j].Address.Bytes()) == -1 + }) + for i := 0; i < len(c1); i++ { + if bytes.Compare(c1[i].Address.Bytes(), c2[i].Address.Bytes()) == 1 { + return false + } + if c1[i].StakingPower != c2[i].StakingPower { + return false + } + if c1[i].VotingPower != c2[i].VotingPower { + return false + } + } + return true +} + +func TestElectVotersNonDup(t *testing.T) { + candidates := newValidatorSet(5, func(i int) int64 { return 10 }) + expectedVotingPower := []int64{ + 13, + 11, + 10, + 8, + 5, + } + + byzantinePercent := int64(10) + voters := electVotersNonDup(candidates, 0, byzantinePercent) + assert.True(t, !isByzantine(voters, candidates.totalStakingPower, 10)) + + for i, voter := range voters { + assert.True(t, expectedVotingPower[i] == voter.VotingPower) + } + +} + +func TestElectVoter(t *testing.T) { + validators := newValidatorSet(10, func(i int) int64 { return int64(i + 1) }) + total := int64(0) + for _, val := range validators.Validators { + total += val.StakingPower + } + seed := uint64(0) + + candidates := validators.Validators + + //if fail to voting, panic + for i := range validators.Validators { + idx, winner := electVoter(&seed, candidates, i, total) + total -= winner.StakingPower + moveWinnerToLast(candidates, idx) + } +} From 87944e959972d7b2d297d56fa6b0c91093ce8698 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Thu, 5 Nov 2020 08:55:12 +0900 Subject: [PATCH 32/36] fix: merge typo --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 0c5d1c6d5..879a74fd8 100644 --- a/Makefile +++ b/Makefile @@ -246,7 +246,6 @@ build-linux: # Build Linux binary $(DOCKER_CMD) ${DOCKER_IMG} /bin/sh -c "$(BUILD_CMD)" ->>>>>>> 6f8f870e494229f7b5a3b97a6f8b433e1ae1ef86 .PHONY: build-linux build-docker-localnode: From 6f6355623121d48dd5c2f10a1c6291e2142034b5 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 10 Nov 2020 13:36:00 +0900 Subject: [PATCH 33/36] remove unused import --- libs/pubsub/pubsub_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index 70a003b75..9ef21adee 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "runtime/debug" - "strconv" "testing" "time" From 45fd355db54b9307633497015df27b72c5f92c90 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 10 Nov 2020 13:36:00 +0900 Subject: [PATCH 34/36] fix a few trivials --- crypto/composite/composite_test.go | 12 ++++++------ libs/pubsub/pubsub_test.go | 1 - types/block.go | 7 ++++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index c61aeaebc..3629feb46 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -214,9 +214,9 @@ func TestEnvironmentalCompatibility(t *testing.T) { if err != nil { t.Fatal(err) } - expected, err := hex.DecodeString("b0896e20be15f757686cec647bf86c97bc73f6607b730abb80828df110226498ba1c6" + - "ba9ccc645a7ee4a08b7db07e6d103699b40369475df592343b77d7e798f659b1f85eb9e237f870a1c98fa5e70e685e82ccf6de" + - "c4355e61450b5fa4ddb9b") + expected, err := hex.DecodeString("b6bf1dbc3a0d67f19a64d8be04bac7b6bb0963698e1711b3955593f23bf325902298a4" + + "9132ce4ebf452302f21af95da307186574106a5cbdc630a43d3269db99e5ecbf99d4798d8dc681dac1d60f6e43868860ae247e9" + + "e66287774df12e79569") if err != nil { t.Fatal(err) } @@ -272,9 +272,9 @@ func TestEnvironmentalCompatibility(t *testing.T) { if err != nil { t.Fatal(err) } - expectedSig, err := hex.DecodeString("9427431821a7db58542bf52289cfa1ffb087673541df3558f5a5d69633d27bb733" + - "7846befc581536d48b9a805309c9b811eecb92336b290e52ee5be83e081f788ee4c66a154c8a954f5330744058c44aaa95d810" + - "3fcddab0dfdda3b5a8551944") + expectedSig, err := hex.DecodeString("a00a8a8143fff615e3df98e9b4a493b0ffc5cf1cee14c55d4c667c34651392331c4d" + + "5a1bf0a15d018262d61f74a59cc80775217b81363796e50aac7ce7542424a2eb84fbaf787f7a1c00229682ac4bb0a45f67cdf43f" + + "b21b091f25a0a8bd51ae") if err != nil { t.Fatal(err) } diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index 70a003b75..9ef21adee 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "runtime/debug" - "strconv" "testing" "time" diff --git a/types/block.go b/types/block.go index 4a99f3a41..bc23bbe42 100644 --- a/types/block.go +++ b/types/block.go @@ -839,15 +839,16 @@ func (commit *Commit) ValidateBasic() error { omittedSignatures++ } } - if commit.AggregatedSignature == nil { + switch { + case commit.AggregatedSignature == nil: if omittedSignatures > 0 { return fmt.Errorf("%d erased signatures are present, but no aggregate signature exist in commit", omittedSignatures) } - } else if omittedSignatures == 0 { + case omittedSignatures == 0: return fmt.Errorf("erased signatures are not present, but aggregated signature exist in commit: %x", commit.AggregatedSignature) - } else if len(commit.AggregatedSignature) > MaxSignatureSize { + case len(commit.AggregatedSignature) > MaxSignatureSize: return fmt.Errorf("signature is too big %d (max: %d)", len(commit.AggregatedSignature), MaxSignatureSize) } From 2be6328e4dcb01c18b8c7f584a734adbacaa1340 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 10 Nov 2020 15:40:49 +0900 Subject: [PATCH 35/36] prohibit adding a Vote to confirmed VoteSet --- types/block.go | 2 +- types/vote_set.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/types/block.go b/types/block.go index bc23bbe42..f5142da99 100644 --- a/types/block.go +++ b/types/block.go @@ -721,7 +721,6 @@ func NewCommitWithAggregatedSignature( // Inverse of VoteSet.MakeCommit(). func CommitToVoteSet(chainID string, commit *Commit, voters *VoterSet) *VoteSet { voteSet := NewVoteSet(chainID, commit.Height, commit.Round, PrecommitType, voters) - voteSet.aggregatedSignature = commit.AggregatedSignature for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some precommits can be missing. @@ -731,6 +730,7 @@ func CommitToVoteSet(chainID string, commit *Commit, voters *VoterSet) *VoteSet panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)) } } + voteSet.aggregatedSignature = commit.AggregatedSignature return voteSet } diff --git a/types/vote_set.go b/types/vote_set.go index 05d3230f5..605940fbe 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -213,6 +213,12 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { return false, errors.Wrapf(err, "Failed to verify vote with ChainID %s and PubKey %s", voteSet.chainID, voter.PubKey) } + // To make state deterministic, it prohibits to add a new Vote to the VoteSet that is restored from an existing + // Commit. + if voteSet.aggregatedSignature != nil { + return false, errors.Errorf("This VoteSet is already done Commit and no new Vote can be added.") + } + // Add vote and get conflicting vote if any. added, conflicting := voteSet.addVerifiedVote(vote, blockKey, voter.VotingPower) if conflicting != nil { From dd4293d43efde28ec3e85336b7c050cfda370340 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Tue, 10 Nov 2020 19:15:56 +0900 Subject: [PATCH 36/36] fix a few trivials --- config/config.go | 1 + consensus/state.go | 1 + types/block.go | 42 +++--------------------------------------- types/voter_set.go | 10 +++++----- 4 files changed, 10 insertions(+), 44 deletions(-) diff --git a/config/config.go b/config/config.go index b4feb5b25..441e62d33 100644 --- a/config/config.go +++ b/config/config.go @@ -8,6 +8,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/privval" ) diff --git a/consensus/state.go b/consensus/state.go index 8130e6015..54abe79a8 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -9,6 +9,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" diff --git a/types/block.go b/types/block.go index f5142da99..84c8160a3 100644 --- a/types/block.go +++ b/types/block.go @@ -922,44 +922,8 @@ func (commit *Commit) ToProto() *tmproto.Commit { return c } -// VerifyAndPackSignatures validates the signatures in this commit and then reduces the size of the commit by -// aggregating the signatures that can be aggregated. -func (commit *Commit) VerifyAndPackSignatures(chainID string, vals []*Validator) error { - if err := commit.verifySignatures(chainID, vals); err != nil { - return err - } - - // aggregate BLS-scheme signatures - /* - for i := range commit.Signatures { - commitSig := &commit.Signatures[i] - if commitSig.Absent() { - continue // OK, some signatures can be absent. - } - - // BLS signature aggregation for all signatures that have been verified to be cleared. - // Signatures that fail to validate are rejected and be Absent. - if commitSig.Signature != nil && len(commitSig.Signature) == bls.SignatureSize { - if commit.AggregatedSignature == nil { - commit.AggregatedSignature = commitSig.Signature - commitSig.Signature = nil - } else { - aggrSig, err := bls.AddSignature(commit.AggregatedSignature, commitSig.Signature) - if err == nil { - commit.AggregatedSignature = aggrSig - commitSig.Signature = nil - } - // The BLS signature that fail to aggregate is remained intact. - } - fmt.Printf("*** Commit.VerifyAndPackSignatures(): signature aggregated\n") - } - } - */ - - return nil -} - -func (commit *Commit) verifySignatures(chainID string, vals []*Validator) error { +// VerifySignatures validates the signatures in this commit. +func (commit *Commit) VerifySignatures(chainID string, vals []*Validator) error { blsPubKeys := make([]bls.PubKeyBLS12, 0, len(commit.Signatures)) messages := make([][]byte, 0, len(commit.Signatures)) for idx, commitSig := range commit.Signatures { @@ -1298,7 +1262,7 @@ func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) { return blockID, blockID.ValidateBasic() } -// GetBLSPubKey is a utility function for referencing a specified public key as a BLS key for signature. +// GetSignatureKey is a utility function for referencing a specified public key as a BLS key for signature. // If the key is not BLS, return nil func GetSignatureKey(pubKey crypto.PubKey) *bls.PubKeyBLS12 { for { diff --git a/types/voter_set.go b/types/voter_set.go index bf414753d..c7414e60d 100644 --- a/types/voter_set.go +++ b/types/voter_set.go @@ -168,7 +168,7 @@ func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, } // Validate signature. - if err := commit.VerifyAndPackSignatures(chainID, voters.Voters); err != nil { + if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { return err } @@ -220,7 +220,7 @@ func (voters *VoterSet) VerifyCommitLight(chainID string, blockID BlockID, // return as soon as +2/3 of the signatures are verified if talliedVotingPower > votingPowerNeeded { // Validate signature. - if err := commit.VerifyAndPackSignatures(chainID, voters.Voters); err != nil { + if err := commit.VerifySignatures(chainID, voters.Voters); err != nil { return err } return nil @@ -298,7 +298,7 @@ func (voters *VoterSet) VerifyFutureCommit(newSet *VoterSet, chainID string, } // Validate signature. - if err := commit.VerifyAndPackSignatures(chainID, vals); err != nil { + if err := commit.VerifySignatures(chainID, vals); err != nil { return err } @@ -370,7 +370,7 @@ func (voters *VoterSet) VerifyCommitTrusting(chainID string, blockID BlockID, } // Validate signature. - if err := commit.VerifyAndPackSignatures(chainID, vals); err != nil { + if err := commit.VerifySignatures(chainID, vals); err != nil { return err } @@ -439,7 +439,7 @@ func (voters *VoterSet) VerifyCommitLightTrusting(chainID string, blockID BlockI _, val := voters.GetByAddress(cs.ValidatorAddress) vals = append(vals, val) } - if err := commit.VerifyAndPackSignatures(chainID, vals); err != nil { + if err := commit.VerifySignatures(chainID, vals); err != nil { return err } return nil