diff --git a/crypto/composite/composite.go b/crypto/composite/composite.go index 60994ba0f..e32f2bae1 100644 --- a/crypto/composite/composite.go +++ b/crypto/composite/composite.go @@ -35,6 +35,17 @@ type PubKey struct { VrfKey crypto.PubKey `json:"vrf"` } +func PubKeyFromBytes(bz []byte) PubKey { + if len(bz) != bls.PubKeySize+ed25519.PubKeySize { + panic(fmt.Sprintf("Wrong PubKey bytes size: %d", len(bz))) + } + sign := bls.PubKey{} + copy(sign[:], bz[:bls.PubKeySize]) + vrf := ed25519.PubKey(make([]byte, ed25519.PubKeySize)) + copy(vrf, bz[bls.PubKeySize:]) + return PubKey{SignKey: sign, VrfKey: vrf} +} + func (pk *PubKey) Identity() crypto.PubKey { return pk.VrfKey } @@ -44,9 +55,9 @@ func (pk PubKey) Address() crypto.Address { } func (pk PubKey) Bytes() []byte { - msg := bytes.NewBuffer(pk.SignKey.Bytes()) - msg.Write(pk.VrfKey.Bytes()) - return msg.Bytes() + bz := bytes.NewBuffer(pk.SignKey.Bytes()) + bz.Write(pk.VrfKey.Bytes()) + return bz.Bytes() } func (pk PubKey) VerifySignature(msg []byte, sig []byte) bool { @@ -80,12 +91,27 @@ func NewPrivKeyComposite(sign crypto.PrivKey, vrf crypto.PrivKey) *PrivKey { return &PrivKey{SignKey: sign, VrfKey: vrf} } +// PrivKeyFromBytes depends on PrivKey.Bytes +// See PrivKey.Bytes +func PrivKeyFromBytes(bz []byte) *PrivKey { + if len(bz) != bls.PrivKeySize+ed25519.PrivateKeySize { + panic(fmt.Sprintf("Wrong PrivKey bytes size: %d", len(bz))) + } + sign := bls.PrivKey{} + copy(sign[:], bz[:bls.PrivKeySize]) + vrf := ed25519.PrivKey(make([]byte, ed25519.PrivateKeySize)) + copy(vrf, bz[bls.PrivKeySize:]) + return &PrivKey{SignKey: sign, VrfKey: vrf} +} + func (sk PrivKey) Identity() crypto.PrivKey { return sk.VrfKey } func (sk PrivKey) Bytes() []byte { - return sk.Identity().Bytes() + bz := bytes.NewBuffer(sk.SignKey.Bytes()) + bz.Write(sk.VrfKey.Bytes()) + return bz.Bytes() } func (sk PrivKey) Sign(msg []byte) ([]byte, error) { diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 4ebd19fbd..3abcac202 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -7,6 +7,8 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + tmjson "github.com/line/ostracon/libs/json" "github.com/line/ostracon/crypto/bls" @@ -14,11 +16,24 @@ import ( "github.com/line/ostracon/crypto/ed25519" ) +func TestGenPrivKey(t *testing.T) { + sk := composite.GenPrivKey() + sign := sk.SignKey + vrf := sk.VrfKey + compositeKey := composite.NewPrivKeyComposite(sign, vrf) + assert.Equal(t, sk, compositeKey) + bz := compositeKey.Bytes() + assert.Equal(t, sk, composite.PrivKeyFromBytes(bz)) +} + func TestPrivKeyComposite_Bytes(t *testing.T) { sign := bls.GenPrivKey() vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) - sk.Bytes() + compositeKey := composite.PrivKeyFromBytes(sk.Bytes()) + assert.Equal(t, sign, compositeKey.SignKey) + assert.Equal(t, vrf, compositeKey.VrfKey) + assert.Equal(t, sk, compositeKey) } func TestPrivKeyComposite_Equals(t *testing.T) { @@ -56,7 +71,11 @@ func TestPrivKeyComposite_PubKey(t *testing.T) { sign := bls.GenPrivKey() vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) - sk.PubKey() + pk := sk.PubKey() + compositeKey := composite.PubKeyFromBytes(pk.Bytes()) + assert.Equal(t, sign.PubKey(), compositeKey.SignKey) + assert.Equal(t, vrf.PubKey(), compositeKey.VrfKey) + assert.Equal(t, pk, compositeKey) } func TestPrivKeyComposite_Sign(t *testing.T) { @@ -105,7 +124,10 @@ func TestPubKeyComposite_Bytes(t *testing.T) { vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) pk := sk.PubKey().(composite.PubKey) - pk.Bytes() + compositeKey := composite.PubKeyFromBytes(pk.Bytes()) + assert.Equal(t, sign.PubKey(), compositeKey.SignKey) + assert.Equal(t, vrf.PubKey(), compositeKey.VrfKey) + assert.Equal(t, pk, compositeKey) } func TestPubKeyComposite_Equals(t *testing.T) { @@ -135,7 +157,7 @@ func TestPubKeyComposite_Identity(t *testing.T) { vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) pk := sk.PubKey().(composite.PubKey) - pk.Identity() + assert.Equal(t, vrf.PubKey(), pk.Identity()) } func TestPubKeyComposite_VerifyBytes(t *testing.T) { diff --git a/crypto/encoding/codec.go b/crypto/encoding/codec.go index a86b9dcce..991cac0cb 100644 --- a/crypto/encoding/codec.go +++ b/crypto/encoding/codec.go @@ -16,6 +16,8 @@ func init() { json.RegisterType((*pc.PublicKey)(nil), "ostracon.crypto.PublicKey") json.RegisterType((*pc.PublicKey_Ed25519)(nil), "ostracon.crypto.PublicKey_Ed25519") json.RegisterType((*pc.PublicKey_Secp256K1)(nil), "ostracon.crypto.PublicKey_Secp256K1") + json.RegisterType((*pc.PublicKey_Composite)(nil), "ostracon.crypto.PublicKey_Composite") + json.RegisterType((*pc.PublicKey_Bls12)(nil), "ostracon.crypto.PublicKey_Bls12") } // PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey @@ -81,14 +83,6 @@ func PubKeyFromProto(k *pc.PublicKey) (crypto.PubKey, error) { VrfKey: vrf, } return pk, nil - case *pc.PublicKey_Ed25519: - if len(k.Ed25519) != ed25519.PubKeySize { - return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d", - len(k.Ed25519), ed25519.PubKeySize) - } - pk := make(ed25519.PubKey, ed25519.PubKeySize) - copy(pk, k.Ed25519) - return pk, nil case *pc.PublicKey_Bls12: if len(k.Bls12) != bls.PubKeySize { return nil, fmt.Errorf("invalid size for PubKeyBls12. Got %d, expected %d", @@ -97,6 +91,14 @@ func PubKeyFromProto(k *pc.PublicKey) (crypto.PubKey, error) { pk := bls.PubKey{} copy(pk[:], k.Bls12) return pk, nil + case *pc.PublicKey_Ed25519: + if len(k.Ed25519) != ed25519.PubKeySize { + return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d", + len(k.Ed25519), ed25519.PubKeySize) + } + pk := make(ed25519.PubKey, ed25519.PubKeySize) + copy(pk, k.Ed25519) + return pk, nil case *pc.PublicKey_Secp256K1: if len(k.Secp256K1) != secp256k1.PubKeySize { return nil, fmt.Errorf("invalid size for PubKeySecp256k1. Got %d, expected %d", diff --git a/privval/file_test.go b/privval/file_test.go index f7d39edfa..95bf2082f 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -1,7 +1,6 @@ package privval import ( - "encoding/base64" "encoding/hex" "fmt" "io/ioutil" @@ -10,6 +9,9 @@ import ( "testing" "time" + "github.com/line/ostracon/crypto" + tmjson "github.com/line/ostracon/libs/json" + "github.com/line/ostracon/crypto/composite" "github.com/stretchr/testify/assert" @@ -17,26 +19,52 @@ import ( "github.com/line/ostracon/crypto/ed25519" "github.com/line/ostracon/crypto/tmhash" - tmjson "github.com/line/ostracon/libs/json" tmrand "github.com/line/ostracon/libs/rand" tmproto "github.com/line/ostracon/proto/ostracon/types" "github.com/line/ostracon/types" tmtime "github.com/line/ostracon/types/time" ) +var testingKeyTypes = []struct { + keyType string + privKey crypto.PrivKey + pubKey crypto.PubKey +}{ + { + keyType: PrivKeyTypeEd25519, + privKey: ed25519.GenPrivKey(), + pubKey: ed25519.PubKey{}, + }, + { + keyType: PrivKeyTypeComposite, + privKey: *composite.GenPrivKey(), + pubKey: composite.PubKey{}, + }, +} + +var jsonKey = func(t *testing.T, addr crypto.Address, privKey crypto.PrivKey, pubKey crypto.PubKey) string { + privKeyBytes, err := tmjson.MarshalIndent(privKey, " ", " ") + assert.Nil(t, err) + pubKeyBytes, err := tmjson.MarshalIndent(pubKey, " ", " ") + assert.Nil(t, err) + return fmt.Sprintf(`{ + "address": "%s", + "pub_key": %s, + "priv_key": %s +}`, addr, pubKeyBytes, privKeyBytes) +} + func TestGenFilePV(t *testing.T) { tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privValEd25519, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) - require.EqualValues(t, reflect.TypeOf(ed25519.PubKey{}), reflect.TypeOf(privValEd25519.Key.PubKey)) - - privValComposite, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeComposite) - require.Nil(t, err) - require.EqualValues(t, reflect.TypeOf(composite.PubKey{}), reflect.TypeOf(privValComposite.Key.PubKey)) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) + require.EqualValues(t, reflect.TypeOf(testing.pubKey), reflect.TypeOf(privVal.Key.PubKey)) + } privValUndefinedPrivKeyType, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "") require.NotNil(t, err) @@ -51,17 +79,19 @@ func TestGenLoadValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - height := int64(100) - privVal.LastSignState.Height = height - privVal.Save() - addr := privVal.GetAddress() + height := int64(100) + privVal.LastSignState.Height = height + privVal.Save() + addr := privVal.GetAddress() - privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name()) - assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") - assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved") + privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name()) + assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") + assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved") + } } func TestResetValidator(t *testing.T) { @@ -70,29 +100,31 @@ func TestResetValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - emptyState := FilePVLastSignState{filePath: tempStateFile.Name()} + emptyState := FilePVLastSignState{filePath: tempStateFile.Name()} - // new priv val has empty state - assert.Equal(t, privVal.LastSignState, emptyState) + // new priv val has empty state + assert.Equal(t, privVal.LastSignState, emptyState) - // test vote - height, round := int64(10), int32(1) - voteType := tmproto.PrevoteType - randBytes := tmrand.Bytes(tmhash.Size) - blockID := types.BlockID{Hash: randBytes, PartSetHeader: types.PartSetHeader{}} - vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) - err = privVal.SignVote("mychainid", vote.ToProto()) - assert.NoError(t, err, "expected no error signing vote") + // test vote + height, round := int64(10), int32(1) + voteType := tmproto.PrevoteType + randBytes := tmrand.Bytes(tmhash.Size) + blockID := types.BlockID{Hash: randBytes, PartSetHeader: types.PartSetHeader{}} + vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) + err = privVal.SignVote("mychainid", vote.ToProto()) + assert.NoError(t, err, "expected no error signing vote") - // priv val after signing is not same as empty - assert.NotEqual(t, privVal.LastSignState, emptyState) + // priv val after signing is not same as empty + assert.NotEqual(t, privVal.LastSignState, emptyState) - // priv val after AcceptNewConnection is same as empty - privVal.Reset() - assert.Equal(t, privVal.LastSignState, emptyState) + // priv val after AcceptNewConnection is same as empty + privVal.Reset() + assert.Equal(t, privVal.LastSignState, emptyState) + } } func TestLoadOrGenValidator(t *testing.T) { @@ -103,23 +135,25 @@ func TestLoadOrGenValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - tempKeyFilePath := tempKeyFile.Name() - if err := os.Remove(tempKeyFilePath); err != nil { - t.Error(err) - } - tempStateFilePath := tempStateFile.Name() - if err := os.Remove(tempStateFilePath); err != nil { - t.Error(err) - } - - privVal, err := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + tempKeyFilePath := tempKeyFile.Name() + if err := os.Remove(tempKeyFilePath); err != nil { + t.Error(err) + } + tempStateFilePath := tempStateFile.Name() + if err := os.Remove(tempStateFilePath); err != nil { + t.Error(err) + } + + privVal, err := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, testing.keyType) + require.Nil(t, err) - addr := privVal.GetAddress() - privVal, err = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, PrivKeyTypeEd25519) - require.Nil(t, err) + addr := privVal.GetAddress() + privVal, err = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, testing.keyType) + require.Nil(t, err) - assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") + assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same", "keyType", testing.keyType) + } } func TestUnmarshalValidatorState(t *testing.T) { @@ -150,40 +184,28 @@ func TestUnmarshalValidatorState(t *testing.T) { func TestUnmarshalValidatorKey(t *testing.T) { assert, require := assert.New(t), require.New(t) - // create some fixed values - privKey := ed25519.GenPrivKey() - pubKey := privKey.PubKey() - addr := pubKey.Address() - pubBytes := pubKey.Bytes() - privBytes := privKey.Bytes() - pubB64 := base64.StdEncoding.EncodeToString(pubBytes) - privB64 := base64.StdEncoding.EncodeToString(privBytes) - - serialized := fmt.Sprintf(`{ - "address": "%s", - "pub_key": { - "type": "ostracon/PubKeyEd25519", - "value": "%s" - }, - "priv_key": { - "type": "ostracon/PrivKeyEd25519", - "value": "%s" - } -}`, addr, pubB64, privB64) - - val := FilePVKey{} - err := tmjson.Unmarshal([]byte(serialized), &val) - require.Nil(err, "%+v", err) + for _, testing := range testingKeyTypes { + // create some fixed values + privKey := testing.privKey + pubKey := privKey.PubKey() + addr := pubKey.Address() - // make sure the values match - assert.EqualValues(addr, val.Address) - assert.EqualValues(pubKey, val.PubKey) - assert.EqualValues(privKey, val.PrivKey) + serialized := jsonKey(t, addr, privKey, pubKey) - // export it and make sure it is the same - out, err := tmjson.Marshal(val) - require.Nil(err, "%+v", err) - assert.JSONEq(serialized, string(out)) + val := FilePVKey{} + err := tmjson.Unmarshal([]byte(serialized), &val) + require.Nil(err, "%+v", err) + + // make sure the values match + assert.EqualValues(addr, val.Address) + assert.EqualValues(pubKey, val.PubKey) + assert.EqualValues(privKey, val.PrivKey) + + // export it and make sure it is the same + out, err := tmjson.Marshal(val) + require.Nil(err, "%+v", err) + assert.JSONEq(serialized, string(out)) + } } func TestSignVote(t *testing.T) { @@ -194,50 +216,52 @@ func TestSignVote(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - randbytes := tmrand.Bytes(tmhash.Size) - randbytes2 := tmrand.Bytes(tmhash.Size) - - block1 := types.BlockID{Hash: randbytes, - PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} - block2 := types.BlockID{Hash: randbytes2, - PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} - - height, round := int64(10), int32(1) - voteType := tmproto.PrevoteType - - // sign a vote for first time - vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1) - v := vote.ToProto() - err = privVal.SignVote("mychainid", v) - assert.NoError(err, "expected no error signing vote") - - // try to sign the same vote again; should be fine - err = privVal.SignVote("mychainid", v) - assert.NoError(err, "expected no error on signing same vote") - - // now try some bad votes - cases := []*types.Vote{ - newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression - newVote(privVal.Key.Address, 0, height-1, round, voteType, block1), // height regression - newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round - newVote(privVal.Key.Address, 0, height, round, voteType, block2), // different block - } + randbytes := tmrand.Bytes(tmhash.Size) + randbytes2 := tmrand.Bytes(tmhash.Size) - for _, c := range cases { - cpb := c.ToProto() - err = privVal.SignVote("mychainid", cpb) - assert.Error(err, "expected error on signing conflicting vote") - } + block1 := types.BlockID{Hash: randbytes, + PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} + block2 := types.BlockID{Hash: randbytes2, + PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} - // try signing a vote with a different time stamp - sig := vote.Signature - vote.Timestamp = vote.Timestamp.Add(time.Duration(1000)) - err = privVal.SignVote("mychainid", v) - assert.NoError(err) - assert.Equal(sig, vote.Signature) + height, round := int64(10), int32(1) + voteType := tmproto.PrevoteType + + // sign a vote for first time + vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1) + v := vote.ToProto() + err = privVal.SignVote("mychainid", v) + assert.NoError(err, "expected no error signing vote") + + // try to sign the same vote again; should be fine + err = privVal.SignVote("mychainid", v) + assert.NoError(err, "expected no error on signing same vote") + + // now try some bad votes + cases := []*types.Vote{ + newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression + newVote(privVal.Key.Address, 0, height-1, round, voteType, block1), // height regression + newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round + newVote(privVal.Key.Address, 0, height, round, voteType, block2), // different block + } + + for _, c := range cases { + cpb := c.ToProto() + err = privVal.SignVote("mychainid", cpb) + assert.Error(err, "expected error on signing conflicting vote") + } + + // try signing a vote with a different time stamp + sig := vote.Signature + vote.Timestamp = vote.Timestamp.Add(time.Duration(1000)) + err = privVal.SignVote("mychainid", v) + assert.NoError(err) + assert.Equal(sig, vote.Signature) + } } func TestSignProposal(t *testing.T) { @@ -248,47 +272,49 @@ func TestSignProposal(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - randbytes := tmrand.Bytes(tmhash.Size) - randbytes2 := tmrand.Bytes(tmhash.Size) - - block1 := types.BlockID{Hash: randbytes, - PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} - block2 := types.BlockID{Hash: randbytes2, - PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} - height, round := int64(10), int32(1) - - // sign a proposal for first time - proposal := newProposal(height, round, block1) - pbp := proposal.ToProto() - err = privVal.SignProposal("mychainid", pbp) - assert.NoError(err, "expected no error signing proposal") - - // try to sign the same proposal again; should be fine - err = privVal.SignProposal("mychainid", pbp) - assert.NoError(err, "expected no error on signing same proposal") - - // now try some bad Proposals - cases := []*types.Proposal{ - newProposal(height, round-1, block1), // round regression - newProposal(height-1, round, block1), // height regression - newProposal(height-2, round+4, block1), // height regression and different round - newProposal(height, round, block2), // different block - } + randbytes := tmrand.Bytes(tmhash.Size) + randbytes2 := tmrand.Bytes(tmhash.Size) - for _, c := range cases { - err = privVal.SignProposal("mychainid", c.ToProto()) - assert.Error(err, "expected error on signing conflicting proposal") - } + block1 := types.BlockID{Hash: randbytes, + PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} + block2 := types.BlockID{Hash: randbytes2, + PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} + height, round := int64(10), int32(1) - // try signing a proposal with a different time stamp - sig := proposal.Signature - proposal.Timestamp = proposal.Timestamp.Add(time.Duration(1000)) - err = privVal.SignProposal("mychainid", pbp) - assert.NoError(err) - assert.Equal(sig, proposal.Signature) + // sign a proposal for first time + proposal := newProposal(height, round, block1) + pbp := proposal.ToProto() + err = privVal.SignProposal("mychainid", pbp) + assert.NoError(err, "expected no error signing proposal") + + // try to sign the same proposal again; should be fine + err = privVal.SignProposal("mychainid", pbp) + assert.NoError(err, "expected no error on signing same proposal") + + // now try some bad Proposals + cases := []*types.Proposal{ + newProposal(height, round-1, block1), // round regression + newProposal(height-1, round, block1), // height regression + newProposal(height-2, round+4, block1), // height regression and different round + newProposal(height, round, block2), // different block + } + + for _, c := range cases { + err = privVal.SignProposal("mychainid", c.ToProto()) + assert.Error(err, "expected error on signing conflicting proposal") + } + + // try signing a proposal with a different time stamp + sig := proposal.Signature + proposal.Timestamp = proposal.Timestamp.Add(time.Duration(1000)) + err = privVal.SignProposal("mychainid", pbp) + assert.NoError(err) + assert.Equal(sig, proposal.Signature) + } } func TestGenerateVRFProof(t *testing.T) { @@ -297,19 +323,21 @@ func TestGenerateVRFProof(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) - - success := [][]byte{{}, {0x00}, make([]byte, 100)} - for _, msg := range success { - proof, err := privVal.GenerateVRFProof(msg) - require.Nil(t, err) - t.Log(" Message : ", hex.EncodeToString(msg), " -> ", hex.EncodeToString(proof[:])) - pubKey, err := privVal.GetPubKey() - require.NoError(t, err) - output, err := pubKey.VRFVerify(proof, msg) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) require.Nil(t, err) - require.NotNil(t, output) + + success := [][]byte{{}, {0x00}, make([]byte, 100)} + for _, msg := range success { + proof, err := privVal.GenerateVRFProof(msg) + require.Nil(t, err) + t.Log(" Message : ", hex.EncodeToString(msg), " -> ", hex.EncodeToString(proof[:])) + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + output, err := pubKey.VRFVerify(proof, msg) + require.Nil(t, err) + require.NotNil(t, output) + } } } @@ -319,59 +347,61 @@ func TestDifferByTimestamp(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) - randbytes := tmrand.Bytes(tmhash.Size) - block1 := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} - height, round := int64(10), int32(1) - chainID := "mychainid" - - // test proposal - { - proposal := newProposal(height, round, block1) - pb := proposal.ToProto() - err := privVal.SignProposal(chainID, pb) - assert.NoError(t, err, "expected no error signing proposal") - signBytes := types.ProposalSignBytes(chainID, pb) - - sig := proposal.Signature - timeStamp := proposal.Timestamp - - // manipulate the timestamp. should get changed back - pb.Timestamp = pb.Timestamp.Add(time.Millisecond) - var emptySig []byte - proposal.Signature = emptySig - err = privVal.SignProposal("mychainid", pb) - assert.NoError(t, err, "expected no error on signing same proposal") - - assert.Equal(t, timeStamp, pb.Timestamp) - assert.Equal(t, signBytes, types.ProposalSignBytes(chainID, pb)) - assert.Equal(t, sig, proposal.Signature) - } - - // test vote - { - voteType := tmproto.PrevoteType - blockID := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{}} - vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) - v := vote.ToProto() - err := privVal.SignVote("mychainid", v) - assert.NoError(t, err, "expected no error signing vote") - - signBytes := types.VoteSignBytes(chainID, v) - sig := v.Signature - timeStamp := vote.Timestamp - - // manipulate the timestamp. should get changed back - v.Timestamp = v.Timestamp.Add(time.Millisecond) - var emptySig []byte - v.Signature = emptySig - err = privVal.SignVote("mychainid", v) - assert.NoError(t, err, "expected no error on signing same vote") - - assert.Equal(t, timeStamp, v.Timestamp) - assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v)) - assert.Equal(t, sig, v.Signature) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) + randbytes := tmrand.Bytes(tmhash.Size) + block1 := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} + height, round := int64(10), int32(1) + chainID := "mychainid" + + // test proposal + { + proposal := newProposal(height, round, block1) + pb := proposal.ToProto() + err := privVal.SignProposal(chainID, pb) + assert.NoError(t, err, "expected no error signing proposal") + signBytes := types.ProposalSignBytes(chainID, pb) + + sig := proposal.Signature + timeStamp := proposal.Timestamp + + // manipulate the timestamp. should get changed back + pb.Timestamp = pb.Timestamp.Add(time.Millisecond) + var emptySig []byte + proposal.Signature = emptySig + err = privVal.SignProposal("mychainid", pb) + assert.NoError(t, err, "expected no error on signing same proposal") + + assert.Equal(t, timeStamp, pb.Timestamp) + assert.Equal(t, signBytes, types.ProposalSignBytes(chainID, pb)) + assert.Equal(t, sig, proposal.Signature) + } + + // test vote + { + voteType := tmproto.PrevoteType + blockID := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{}} + vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) + v := vote.ToProto() + err := privVal.SignVote("mychainid", v) + assert.NoError(t, err, "expected no error signing vote") + + signBytes := types.VoteSignBytes(chainID, v) + sig := v.Signature + timeStamp := vote.Timestamp + + // manipulate the timestamp. should get changed back + v.Timestamp = v.Timestamp.Add(time.Millisecond) + var emptySig []byte + v.Signature = emptySig + err = privVal.SignVote("mychainid", v) + assert.NoError(t, err, "expected no error on signing same vote") + + assert.Equal(t, timeStamp, v.Timestamp) + assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v)) + assert.Equal(t, sig, v.Signature) + } } } diff --git a/types/block.go b/types/block.go index ef1fa23c5..03442b8ac 100644 --- a/types/block.go +++ b/types/block.go @@ -888,7 +888,11 @@ func CommitToVoteSet(chainID string, commit *Commit, voters *VoterSet) *VoteSet panic(fmt.Sprintf("Cannot find voter %d in voterSet of size %d", vote.ValidatorIndex, voters.Size())) } msg := VoteSignBytes(chainID, vote.ToProto()) - blsPubKeys = append(blsPubKeys, voter.PubKey.(composite.PubKey).SignKey.(bls.PubKey)) + blsPubKey := GetSignatureKey(voter.PubKey) + if blsPubKey == nil { + panic(fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx)) + } + blsPubKeys = append(blsPubKeys, *blsPubKey) msgs = append(msgs, msg) } }