diff --git a/consensus/common_test.go b/consensus/common_test.go index 948812225..60bb9eed5 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -74,6 +74,7 @@ type validatorStub struct { Round int32 types.PrivValidator VotingPower int64 + lastVote *types.Vote } var testMinPower int64 = 10 @@ -106,8 +107,18 @@ func (vs *validatorStub) signVote( BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, } v := vote.ToProto() - err = vs.PrivValidator.SignVote(config.ChainID(), v) + if err := vs.PrivValidator.SignVote(config.ChainID(), v); err != nil { + return nil, fmt.Errorf("sign vote failed: %w", err) + } + + // ref: signVote in FilePV, the vote should use the privious vote info when the sign data is the same. + if signDataIsEqual(vs.lastVote, v) { + v.Signature = vs.lastVote.Signature + v.Timestamp = vs.lastVote.Timestamp + } + vote.Signature = v.Signature + vote.Timestamp = v.Timestamp return vote, err } @@ -118,6 +129,9 @@ func signVote(vs *validatorStub, voteType tmproto.SignedMsgType, hash []byte, he if err != nil { panic(fmt.Errorf("failed to sign vote: %v", err)) } + + vs.lastVote = v + return v } @@ -866,3 +880,16 @@ func newPersistentKVStore() abci.Application { func newPersistentKVStoreWithPath(dbDir string) abci.Application { return kvstore.NewPersistentKVStoreApplication(dbDir) } + +func signDataIsEqual(v1 *types.Vote, v2 *tmproto.Vote) bool { + if v1 == nil || v2 == nil { + return false + } + + return v1.Type == v2.Type && + bytes.Equal(v1.BlockID.Hash, v2.BlockID.GetHash()) && + v1.Height == v2.GetHeight() && + v1.Round == v2.Round && + bytes.Equal(v1.ValidatorAddress.Bytes(), v2.GetValidatorAddress()) && + v1.ValidatorIndex == v2.GetValidatorIndex() +} diff --git a/consensus/state.go b/consensus/state.go index 98aabd355..dd19f9ab3 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -2180,6 +2180,7 @@ func (cs *State) signVote( v := vote.ToProto() err := cs.privValidator.SignVote(cs.state.ChainID, v) vote.Signature = v.Signature + vote.Timestamp = v.Timestamp return vote, err } diff --git a/consensus/state_test.go b/consensus/state_test.go index fa2aafb56..1e3887fd9 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -1877,6 +1877,26 @@ func TestStateOutputVoteStats(t *testing.T) { } +func TestSignSameVoteTwice(t *testing.T) { + _, vss := randState(2) + + randBytes := tmrand.Bytes(tmhash.Size) + + vote := signVote(vss[1], + tmproto.PrecommitType, + randBytes, + types.PartSetHeader{Total: 10, Hash: randBytes}, + ) + + vote2 := signVote(vss[1], + tmproto.PrecommitType, + randBytes, + types.PartSetHeader{Total: 10, Hash: randBytes}, + ) + + require.Equal(t, vote, vote2) +} + // subscribe subscribes test client to the given query and returns a channel with cap = 1. func subscribe(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Message { sub, err := eventBus.Subscribe(context.Background(), testSubscriber, q)