github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/types/evidence_test.go (about)

     1  package types
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/tendermint/tendermint/crypto"
    12  	"github.com/tendermint/tendermint/crypto/ed25519"
    13  	"github.com/tendermint/tendermint/crypto/tmhash"
    14  	tmrand "github.com/tendermint/tendermint/libs/rand"
    15  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    16  )
    17  
    18  type voteData struct {
    19  	vote1 *Vote
    20  	vote2 *Vote
    21  	valid bool
    22  }
    23  
    24  var defaultVoteTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
    25  
    26  func TestEvidence(t *testing.T) {
    27  	val := NewMockPV()
    28  	val2 := NewMockPV()
    29  
    30  	blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
    31  	blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash"))
    32  	blockID3 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash"))
    33  	blockID4 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash2"))
    34  
    35  	const chainID = "mychain"
    36  
    37  	vote1 := makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime)
    38  	v1 := vote1.ToProto()
    39  	err := val.SignVote(chainID, v1)
    40  	require.NoError(t, err)
    41  	badVote := makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime)
    42  	bv := badVote.ToProto()
    43  	err = val2.SignVote(chainID, bv)
    44  	require.NoError(t, err)
    45  
    46  	vote1.Signature = v1.Signature
    47  	badVote.Signature = bv.Signature
    48  
    49  	cases := []voteData{
    50  		{vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime), true}, // different block ids
    51  		{vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID3, defaultVoteTime), true},
    52  		{vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID4, defaultVoteTime), true},
    53  		{vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime), false},     // wrong block id
    54  		{vote1, makeVote(t, val, "mychain2", 0, 10, 2, 1, blockID2, defaultVoteTime), false}, // wrong chain id
    55  		{vote1, makeVote(t, val, chainID, 1, 10, 2, 1, blockID2, defaultVoteTime), false},    // wrong val index
    56  		{vote1, makeVote(t, val, chainID, 0, 11, 2, 1, blockID2, defaultVoteTime), false},    // wrong height
    57  		{vote1, makeVote(t, val, chainID, 0, 10, 3, 1, blockID2, defaultVoteTime), false},    // wrong round
    58  		{vote1, makeVote(t, val, chainID, 0, 10, 2, 2, blockID2, defaultVoteTime), false},    // wrong step
    59  		{vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID, defaultVoteTime), false},    // wrong validator
    60  		{vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID, time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)), false},
    61  		{vote1, badVote, false}, // signed by wrong key
    62  	}
    63  
    64  	pubKey, err := val.GetPubKey()
    65  	require.NoError(t, err)
    66  	for _, c := range cases {
    67  		ev := &DuplicateVoteEvidence{
    68  			VoteA: c.vote1,
    69  			VoteB: c.vote2,
    70  		}
    71  		if c.valid {
    72  			assert.Nil(t, ev.Verify(chainID, pubKey), "evidence should be valid")
    73  		} else {
    74  			assert.NotNil(t, ev.Verify(chainID, pubKey), "evidence should be invalid")
    75  		}
    76  	}
    77  }
    78  
    79  func TestDuplicatedVoteEvidence(t *testing.T) {
    80  	ev := randomDuplicatedVoteEvidence(t)
    81  
    82  	assert.True(t, ev.Equal(ev))
    83  	assert.False(t, ev.Equal(&DuplicateVoteEvidence{}))
    84  
    85  	maxTime := ev.VoteB.Timestamp
    86  	if ev.VoteA.Timestamp.After(ev.VoteB.Timestamp) {
    87  		maxTime = ev.VoteA.Timestamp
    88  	}
    89  	assert.Equal(t, maxTime, ev.Time(), "expected time of the latest vote")
    90  }
    91  
    92  func TestEvidenceList(t *testing.T) {
    93  	ev := randomDuplicatedVoteEvidence(t)
    94  	evl := EvidenceList([]Evidence{ev})
    95  
    96  	assert.NotNil(t, evl.Hash())
    97  	assert.True(t, evl.Has(ev))
    98  	assert.False(t, evl.Has(&DuplicateVoteEvidence{}))
    99  }
   100  
   101  func TestMaxEvidenceBytes(t *testing.T) {
   102  	val := NewMockPV()
   103  	blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   104  	blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   105  	maxTime := time.Date(9999, 0, 0, 0, 0, 0, 0, time.UTC)
   106  	const chainID = "mychain"
   107  	ev := &DuplicateVoteEvidence{
   108  		VoteA: makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, math.MaxInt64, blockID, maxTime),
   109  		VoteB: makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, math.MaxInt64, blockID2, maxTime),
   110  	}
   111  
   112  	//TODO: Add other types of evidence to test and set MaxEvidenceBytes accordingly
   113  
   114  	// evl := &LunaticValidatorEvidence{
   115  	// Header: makeHeaderRandom(),
   116  	// Vote:   makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2),
   117  
   118  	// 	InvalidHeaderField: "",
   119  	// }
   120  
   121  	// evp := &PhantomValidatorEvidence{
   122  	// 	Header: makeHeaderRandom(),
   123  	// 	Vote:   makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2),
   124  
   125  	// 	LastHeightValidatorWasInSet: math.MaxInt64,
   126  	// }
   127  
   128  	// signedHeader := SignedHeader{Header: makeHeaderRandom(), Commit: randCommit(time.Now())}
   129  	// evc := &ConflictingHeadersEvidence{
   130  	// 	H1: &signedHeader,
   131  	// 	H2: &signedHeader,
   132  	// }
   133  
   134  	testCases := []struct {
   135  		testName string
   136  		evidence Evidence
   137  	}{
   138  		{"DuplicateVote", ev},
   139  		// {"LunaticValidatorEvidence", evl},
   140  		// {"PhantomValidatorEvidence", evp},
   141  		// {"ConflictingHeadersEvidence", evc},
   142  	}
   143  
   144  	for _, tt := range testCases {
   145  		pb, err := EvidenceToProto(tt.evidence)
   146  		require.NoError(t, err, tt.testName)
   147  		bz, err := pb.Marshal()
   148  		require.NoError(t, err, tt.testName)
   149  
   150  		assert.LessOrEqual(t, int64(len(bz)), MaxEvidenceBytes, tt.testName)
   151  	}
   152  
   153  }
   154  
   155  func randomDuplicatedVoteEvidence(t *testing.T) *DuplicateVoteEvidence {
   156  	val := NewMockPV()
   157  	blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
   158  	blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash"))
   159  	const chainID = "mychain"
   160  	return &DuplicateVoteEvidence{
   161  		VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime),
   162  		VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime.Add(1*time.Minute)),
   163  	}
   164  }
   165  
   166  func TestDuplicateVoteEvidenceValidation(t *testing.T) {
   167  	val := NewMockPV()
   168  	blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   169  	blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   170  	const chainID = "mychain"
   171  
   172  	testCases := []struct {
   173  		testName         string
   174  		malleateEvidence func(*DuplicateVoteEvidence)
   175  		expectErr        bool
   176  	}{
   177  		{"Good DuplicateVoteEvidence", func(ev *DuplicateVoteEvidence) {}, false},
   178  		{"Nil vote A", func(ev *DuplicateVoteEvidence) { ev.VoteA = nil }, true},
   179  		{"Nil vote B", func(ev *DuplicateVoteEvidence) { ev.VoteB = nil }, true},
   180  		{"Nil votes", func(ev *DuplicateVoteEvidence) {
   181  			ev.VoteA = nil
   182  			ev.VoteB = nil
   183  		}, true},
   184  		{"Invalid vote type", func(ev *DuplicateVoteEvidence) {
   185  			ev.VoteA = makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0, blockID2, defaultVoteTime)
   186  		}, true},
   187  		{"Invalid vote order", func(ev *DuplicateVoteEvidence) {
   188  			swap := ev.VoteA.Copy()
   189  			ev.VoteA = ev.VoteB.Copy()
   190  			ev.VoteB = swap
   191  		}, true},
   192  	}
   193  	for _, tc := range testCases {
   194  		tc := tc
   195  		t.Run(tc.testName, func(t *testing.T) {
   196  			vote1 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID, defaultVoteTime)
   197  			vote2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID2, defaultVoteTime)
   198  			ev := NewDuplicateVoteEvidence(vote1, vote2)
   199  			tc.malleateEvidence(ev)
   200  			assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result")
   201  		})
   202  	}
   203  }
   204  
   205  func TestMockEvidenceValidateBasic(t *testing.T) {
   206  	goodEvidence := NewMockDuplicateVoteEvidence(int64(1), time.Now(), "mock-chain-id")
   207  	assert.Nil(t, goodEvidence.ValidateBasic())
   208  }
   209  
   210  func TestLunaticValidatorEvidence(t *testing.T) {
   211  	var (
   212  		invalidBlockID = makeBlockIDRandom()
   213  		header         = makeHeaderRandom()
   214  		altHeader      = makeHeaderRandom()
   215  		bTime, _       = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
   216  		val            = NewMockPV()
   217  	)
   218  
   219  	header.Time = bTime
   220  
   221  	blockID := BlockID{
   222  		Hash: header.Hash(),
   223  		PartSetHeader: PartSetHeader{
   224  			Total: 100,
   225  			Hash:  crypto.CRandBytes(tmhash.Size),
   226  		},
   227  	}
   228  
   229  	vote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, blockID, defaultVoteTime)
   230  
   231  	ev := NewLunaticValidatorEvidence(header, vote, "AppHash")
   232  
   233  	//happy path
   234  	assert.Equal(t, header.Height, ev.Height())
   235  	assert.Equal(t, defaultVoteTime, ev.Time())
   236  	assert.EqualValues(t, vote.ValidatorAddress, ev.Address())
   237  	assert.NotEmpty(t, ev.Hash())
   238  	assert.NotEmpty(t, ev.Bytes())
   239  	assert.True(t, ev.Equal(ev))
   240  	pubKey, err := val.GetPubKey()
   241  	require.NoError(t, err)
   242  	assert.NoError(t, ev.Verify(header.ChainID, pubKey))
   243  	assert.NoError(t, ev.ValidateBasic())
   244  	assert.NotEmpty(t, ev.String())
   245  	assert.NoError(t, ev.VerifyHeader(altHeader))
   246  
   247  	// invalid evidence
   248  	assert.Error(t, ev.Verify("other", pubKey))
   249  	privKey2 := ed25519.GenPrivKey()
   250  	pubKey2 := privKey2.PubKey()
   251  	assert.Error(t, ev.Verify(header.ChainID, pubKey2))
   252  	assert.Error(t, ev.VerifyHeader(header))
   253  
   254  	invalidVote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, invalidBlockID, defaultVoteTime)
   255  	invalidHeightVote := makeVote(t, val, header.ChainID, 0, header.Height+1, 0, 2, blockID, defaultVoteTime)
   256  	emptyBlockVote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, BlockID{}, defaultVoteTime)
   257  
   258  	invalidLunaticEvidence := []*LunaticValidatorEvidence{
   259  		NewLunaticValidatorEvidence(header, invalidVote, "AppHash"),
   260  		NewLunaticValidatorEvidence(header, invalidHeightVote, "AppHash"),
   261  		NewLunaticValidatorEvidence(nil, vote, "AppHash"),
   262  		NewLunaticValidatorEvidence(header, nil, "AppHash"),
   263  		NewLunaticValidatorEvidence(header, vote, "other"),
   264  		NewLunaticValidatorEvidence(header, emptyBlockVote, "AppHash"),
   265  	}
   266  
   267  	for idx, ev := range invalidLunaticEvidence {
   268  		assert.Error(t, ev.ValidateBasic(), "#%d", idx)
   269  	}
   270  
   271  }
   272  
   273  func TestPhantomValidatorEvidence(t *testing.T) {
   274  	var (
   275  		blockID = makeBlockIDRandom()
   276  		header  = makeHeaderRandom()
   277  		val     = NewMockPV()
   278  		vote    = makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, blockID, defaultVoteTime)
   279  	)
   280  
   281  	ev := NewPhantomValidatorEvidence(vote, header.Height-1)
   282  
   283  	assert.Equal(t, header.Height, ev.Height())
   284  	assert.Equal(t, defaultVoteTime, ev.Time())
   285  	assert.EqualValues(t, vote.ValidatorAddress, ev.Address())
   286  	assert.NotEmpty(t, ev.Hash())
   287  	assert.NotEmpty(t, ev.Bytes())
   288  	pubKey, err := val.GetPubKey()
   289  	require.NoError(t, err)
   290  	assert.NoError(t, ev.Verify(header.ChainID, pubKey))
   291  	assert.Error(t, ev.Verify("other", pubKey))
   292  	privKey2 := ed25519.GenPrivKey()
   293  	pubKey2 := privKey2.PubKey()
   294  	assert.Error(t, ev.Verify("other", pubKey2))
   295  	assert.True(t, ev.Equal(ev))
   296  	assert.NoError(t, ev.ValidateBasic())
   297  	assert.NotEmpty(t, ev.String())
   298  }
   299  
   300  func TestConflictingHeadersEvidence(t *testing.T) {
   301  	const (
   302  		chainID       = "TestConflictingHeadersEvidence"
   303  		height  int64 = 37
   304  	)
   305  
   306  	var (
   307  		blockID = makeBlockIDRandom()
   308  		header1 = makeHeaderRandom()
   309  		header2 = makeHeaderRandom()
   310  	)
   311  
   312  	header1.Height = height
   313  	header1.LastBlockID = blockID
   314  	header1.ChainID = chainID
   315  
   316  	header2.Height = height
   317  	header2.LastBlockID = blockID
   318  	header2.ChainID = chainID
   319  
   320  	voteSet1, valSet, vals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1)
   321  	voteSet2 := NewVoteSet(chainID, height, 1, tmproto.PrecommitType, valSet)
   322  
   323  	commit1, err := MakeCommit(BlockID{
   324  		Hash: header1.Hash(),
   325  		PartSetHeader: PartSetHeader{
   326  			Total: 100,
   327  			Hash:  crypto.CRandBytes(tmhash.Size),
   328  		},
   329  	}, height, 1, voteSet1, vals, time.Now())
   330  	require.NoError(t, err)
   331  	commit2, err := MakeCommit(BlockID{
   332  		Hash: header2.Hash(),
   333  		PartSetHeader: PartSetHeader{
   334  			Total: 100,
   335  			Hash:  crypto.CRandBytes(tmhash.Size),
   336  		},
   337  	}, height, 1, voteSet2, vals, time.Now())
   338  	require.NoError(t, err)
   339  
   340  	h1 := &SignedHeader{
   341  		Header: header1,
   342  		Commit: commit1,
   343  	}
   344  	h2 := &SignedHeader{
   345  		Header: header2,
   346  		Commit: commit2,
   347  	}
   348  
   349  	ev := NewConflictingHeadersEvidence(h1, h2)
   350  
   351  	assert.Panics(t, func() {
   352  		ev.Address()
   353  	})
   354  
   355  	assert.Panics(t, func() {
   356  		pubKey, _ := vals[0].GetPubKey()
   357  		ev.Verify(chainID, pubKey)
   358  	})
   359  
   360  	assert.Equal(t, height, ev.Height())
   361  	assert.Equal(t, ev.H2.Time, ev.Time())
   362  	assert.NotEmpty(t, ev.Hash())
   363  	assert.NotEmpty(t, ev.Bytes())
   364  	assert.NoError(t, ev.VerifyComposite(header1, valSet))
   365  	assert.True(t, ev.Equal(ev))
   366  	assert.NoError(t, ev.ValidateBasic())
   367  	assert.NotEmpty(t, ev.String())
   368  }
   369  
   370  func TestPotentialAmnesiaEvidence(t *testing.T) {
   371  	const (
   372  		chainID       = "TestPotentialAmnesiaEvidence"
   373  		height  int64 = 37
   374  	)
   375  
   376  	var (
   377  		val      = NewMockPV()
   378  		blockID  = makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   379  		blockID2 = makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   380  		vote1    = makeVote(t, val, chainID, 0, height, 0, 2, blockID, defaultVoteTime)
   381  		vote2    = makeVote(t, val, chainID, 0, height, 1, 2, blockID2, defaultVoteTime.Add(1*time.Second))
   382  		vote3    = makeVote(t, val, chainID, 0, height, 2, 2, blockID, defaultVoteTime)
   383  	)
   384  
   385  	ev := NewPotentialAmnesiaEvidence(vote1, vote2)
   386  
   387  	assert.Equal(t, height, ev.Height())
   388  	assert.Equal(t, vote2.Timestamp, ev.Time())
   389  	assert.EqualValues(t, vote1.ValidatorAddress, ev.Address())
   390  	assert.NotEmpty(t, ev.Hash())
   391  	assert.NotEmpty(t, ev.Bytes())
   392  	pubKey, err := val.GetPubKey()
   393  	require.NoError(t, err)
   394  	assert.NoError(t, ev.Verify(chainID, pubKey))
   395  	assert.Error(t, ev.Verify("other", pubKey))
   396  	privKey2 := ed25519.GenPrivKey()
   397  	pubKey2 := privKey2.PubKey()
   398  	assert.Error(t, ev.Verify("other", pubKey2))
   399  	assert.True(t, ev.Equal(ev))
   400  	assert.NoError(t, ev.ValidateBasic())
   401  	assert.NotEmpty(t, ev.String())
   402  
   403  	ev2 := &PotentialAmnesiaEvidence{
   404  		VoteA:       vote1,
   405  		VoteB:       vote2,
   406  		HeightStamp: 5,
   407  	}
   408  
   409  	assert.True(t, ev.Equal(ev2))
   410  	assert.Equal(t, ev.Hash(), ev2.Hash())
   411  
   412  	ev3 := &PotentialAmnesiaEvidence{
   413  		VoteA: vote2,
   414  		VoteB: vote1,
   415  	}
   416  
   417  	assert.Error(t, ev3.ValidateBasic())
   418  
   419  	ev3 = NewPotentialAmnesiaEvidence(vote2, vote1)
   420  	assert.True(t, ev3.Equal(ev))
   421  
   422  	ev4 := &PotentialAmnesiaEvidence{
   423  		VoteA: vote3,
   424  		VoteB: vote2,
   425  	}
   426  
   427  	assert.NoError(t, ev4.ValidateBasic())
   428  	assert.NotEqual(t, ev.Hash(), ev4.Hash())
   429  	assert.False(t, ev.Equal(ev4))
   430  
   431  }
   432  
   433  func TestProofOfLockChange(t *testing.T) {
   434  	const (
   435  		chainID       = "test_chain_id"
   436  		height  int64 = 37
   437  	)
   438  	// 1: valid POLC - nothing should fail
   439  	voteSet, valSet, privValidators, blockID := buildVoteSet(height, 1, 3, 7, 0, tmproto.PrecommitType)
   440  	pubKey, err := privValidators[7].GetPubKey()
   441  	require.NoError(t, err)
   442  	polc, err := NewPOLCFromVoteSet(voteSet, pubKey, blockID)
   443  	assert.NoError(t, err)
   444  
   445  	assert.Equal(t, height, polc.Height())
   446  	assert.NoError(t, polc.ValidateBasic())
   447  	assert.NoError(t, polc.ValidateVotes(valSet, chainID))
   448  	assert.NotEmpty(t, polc.String())
   449  
   450  	// tamper with one of the votes
   451  	polc.Votes[0].Timestamp = time.Now().Add(1 * time.Second)
   452  	err = polc.ValidateVotes(valSet, chainID)
   453  	t.Log(err)
   454  	assert.Error(t, err)
   455  
   456  	// remove a vote such that majority wasn't reached
   457  	polc.Votes = polc.Votes[1:]
   458  	err = polc.ValidateVotes(valSet, chainID)
   459  	t.Log(err)
   460  	assert.Error(t, err)
   461  
   462  	// test validate basic on a set of bad cases
   463  	var badPOLCs []*ProofOfLockChange
   464  	// 2: node has already voted in next round
   465  	pubKey, err = privValidators[0].GetPubKey()
   466  	require.NoError(t, err)
   467  	polc2 := newPOLCFromVoteSet(voteSet, pubKey, blockID)
   468  	badPOLCs = append(badPOLCs, polc2)
   469  	// 3: one vote was from a different round
   470  	voteSet, _, privValidators, blockID = buildVoteSet(height, 1, 3, 7, 0, tmproto.PrecommitType)
   471  	pubKey, err = privValidators[7].GetPubKey()
   472  	require.NoError(t, err)
   473  	polc = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   474  	badVote := makeVote(t, privValidators[8], chainID, 8, height, 2, 2, blockID, defaultVoteTime)
   475  	polc.Votes = append(polc.Votes, badVote)
   476  	badPOLCs = append(badPOLCs, polc)
   477  	// 4: one vote was from a different height
   478  	polc = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   479  	badVote = makeVote(t, privValidators[8], chainID, 8, height+1, 1, 2, blockID, defaultVoteTime)
   480  	polc.Votes = append(polc.Votes, badVote)
   481  	badPOLCs = append(badPOLCs, polc)
   482  	// 5: one vote was from a different vote type
   483  	polc = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   484  	badVote = makeVote(t, privValidators[8], chainID, 8, height, 1, 1, blockID, defaultVoteTime)
   485  	polc.Votes = append(polc.Votes, badVote)
   486  	badPOLCs = append(badPOLCs, polc)
   487  	// 5: one of the votes was for a nil block
   488  	polc = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   489  	badVote = makeVote(t, privValidators[8], chainID, 8, height, 1, 2, BlockID{}, defaultVoteTime)
   490  	polc.Votes = append(polc.Votes, badVote)
   491  	badPOLCs = append(badPOLCs, polc)
   492  
   493  	for idx, polc := range badPOLCs {
   494  		err := polc.ValidateBasic()
   495  		t.Logf("case: %d: %v", idx+2, err)
   496  		assert.Error(t, err)
   497  		if err == nil {
   498  			t.Errorf("test no. %d failed", idx+2)
   499  		}
   500  	}
   501  
   502  }
   503  
   504  func TestAmnesiaEvidence(t *testing.T) {
   505  	const (
   506  		chainID       = "test_chain_id"
   507  		height  int64 = 37
   508  	)
   509  
   510  	voteSet, valSet, privValidators, blockID := buildVoteSet(height, 1, 2, 7, 0, tmproto.PrecommitType)
   511  
   512  	var (
   513  		val       = privValidators[7]
   514  		pubKey, _ = val.GetPubKey()
   515  		blockID2  = makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   516  		vote1     = makeVote(t, val, chainID, 7, height, 0, 2, blockID2, time.Now())
   517  		vote2     = makeVote(t, val, chainID, 7, height, 1, 2, blockID,
   518  			time.Now().Add(time.Second))
   519  		vote3 = makeVote(t, val, chainID, 7, height, 2, 2, blockID2, time.Now())
   520  		polc  = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   521  	)
   522  
   523  	require.False(t, polc.IsAbsent())
   524  
   525  	pe := &PotentialAmnesiaEvidence{
   526  		VoteA: vote1,
   527  		VoteB: vote2,
   528  	}
   529  
   530  	emptyAmnesiaEvidence := NewAmnesiaEvidence(pe, NewEmptyPOLC())
   531  
   532  	assert.NoError(t, emptyAmnesiaEvidence.ValidateBasic())
   533  	violated, reason := emptyAmnesiaEvidence.ViolatedConsensus()
   534  	if assert.True(t, violated) {
   535  		assert.Equal(t, reason, "no proof of lock was provided")
   536  	}
   537  	assert.NoError(t, emptyAmnesiaEvidence.Verify(chainID, pubKey))
   538  
   539  	completeAmnesiaEvidence := NewAmnesiaEvidence(pe, polc)
   540  
   541  	assert.NoError(t, completeAmnesiaEvidence.ValidateBasic())
   542  	violated, reason = completeAmnesiaEvidence.ViolatedConsensus()
   543  	if !assert.False(t, violated) {
   544  		t.Log(reason)
   545  	}
   546  	assert.NoError(t, completeAmnesiaEvidence.Verify(chainID, pubKey))
   547  	assert.NoError(t, completeAmnesiaEvidence.Polc.ValidateVotes(valSet, chainID))
   548  
   549  	assert.True(t, completeAmnesiaEvidence.Equal(emptyAmnesiaEvidence))
   550  	assert.Equal(t, completeAmnesiaEvidence.Hash(), emptyAmnesiaEvidence.Hash())
   551  	assert.NotEmpty(t, completeAmnesiaEvidence.Hash())
   552  	assert.NotEmpty(t, completeAmnesiaEvidence.Bytes())
   553  
   554  	pe2 := &PotentialAmnesiaEvidence{
   555  		VoteA: vote3,
   556  		VoteB: vote2,
   557  	}
   558  
   559  	// validator has incorrectly voted for a previous round after voting for a later round
   560  	ae := NewAmnesiaEvidence(pe2, NewEmptyPOLC())
   561  	assert.NoError(t, ae.ValidateBasic())
   562  	violated, reason = ae.ViolatedConsensus()
   563  	if assert.True(t, violated) {
   564  		assert.Equal(t, reason, "validator went back and voted on a previous round")
   565  	}
   566  
   567  	var badAE []*AmnesiaEvidence
   568  	// 1) Polc is at an incorrect height
   569  	voteSet, _, _ = buildVoteSetForBlock(height+1, 1, 2, 7, 0, tmproto.PrecommitType, blockID)
   570  	polc = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   571  	badAE = append(badAE, NewAmnesiaEvidence(pe, polc))
   572  	// 2) Polc is of a later round
   573  	voteSet, _, _ = buildVoteSetForBlock(height, 2, 2, 7, 0, tmproto.PrecommitType, blockID)
   574  	polc = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   575  	badAE = append(badAE, NewAmnesiaEvidence(pe, polc))
   576  	// 3) Polc has a different public key
   577  	voteSet, _, privValidators = buildVoteSetForBlock(height, 1, 2, 7, 0, tmproto.PrecommitType, blockID)
   578  	pubKey2, _ := privValidators[7].GetPubKey()
   579  	polc = newPOLCFromVoteSet(voteSet, pubKey2, blockID)
   580  	badAE = append(badAE, NewAmnesiaEvidence(pe, polc))
   581  	// 4) Polc has a different block ID
   582  	voteSet, _, _, blockID = buildVoteSet(height, 1, 2, 7, 0, tmproto.PrecommitType)
   583  	polc = newPOLCFromVoteSet(voteSet, pubKey, blockID)
   584  	badAE = append(badAE, NewAmnesiaEvidence(pe, polc))
   585  
   586  	for idx, ae := range badAE {
   587  		t.Log(ae.ValidateBasic())
   588  		if !assert.Error(t, ae.ValidateBasic()) {
   589  			t.Errorf("test no. %d failed", idx+1)
   590  		}
   591  	}
   592  
   593  }
   594  
   595  func makeVote(
   596  	t *testing.T, val PrivValidator, chainID string, valIndex int32, height int64, round int32, step int, blockID BlockID,
   597  	time time.Time) *Vote {
   598  	pubKey, err := val.GetPubKey()
   599  	require.NoError(t, err)
   600  	v := &Vote{
   601  		ValidatorAddress: pubKey.Address(),
   602  		ValidatorIndex:   valIndex,
   603  		Height:           height,
   604  		Round:            round,
   605  		Type:             tmproto.SignedMsgType(step),
   606  		BlockID:          blockID,
   607  		Timestamp:        time,
   608  	}
   609  
   610  	vpb := v.ToProto()
   611  	err = val.SignVote(chainID, vpb)
   612  	if err != nil {
   613  		panic(err)
   614  	}
   615  	v.Signature = vpb.Signature
   616  	return v
   617  }
   618  
   619  func makeHeaderRandom() *Header {
   620  	return &Header{
   621  		ChainID:            tmrand.Str(12),
   622  		Height:             int64(tmrand.Uint16()) + 1,
   623  		Time:               time.Now(),
   624  		LastBlockID:        makeBlockIDRandom(),
   625  		LastCommitHash:     crypto.CRandBytes(tmhash.Size),
   626  		DataHash:           crypto.CRandBytes(tmhash.Size),
   627  		ValidatorsHash:     crypto.CRandBytes(tmhash.Size),
   628  		NextValidatorsHash: crypto.CRandBytes(tmhash.Size),
   629  		ConsensusHash:      crypto.CRandBytes(tmhash.Size),
   630  		AppHash:            crypto.CRandBytes(tmhash.Size),
   631  		LastResultsHash:    crypto.CRandBytes(tmhash.Size),
   632  		EvidenceHash:       crypto.CRandBytes(tmhash.Size),
   633  		ProposerAddress:    crypto.CRandBytes(crypto.AddressSize),
   634  	}
   635  }
   636  
   637  func TestEvidenceProto(t *testing.T) {
   638  	// -------- Votes --------
   639  	val := NewMockPV()
   640  	blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   641  	blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   642  	const chainID = "mychain"
   643  	v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime)
   644  	v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, defaultVoteTime)
   645  
   646  	// -------- SignedHeaders --------
   647  	const height int64 = 37
   648  
   649  	var (
   650  		header1 = makeHeaderRandom()
   651  		header2 = makeHeaderRandom()
   652  	)
   653  
   654  	header1.Height = height
   655  	header1.LastBlockID = blockID
   656  	header1.ChainID = chainID
   657  
   658  	header2.Height = height
   659  	header2.LastBlockID = blockID
   660  	header2.ChainID = chainID
   661  
   662  	voteSet1, valSet, vals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1)
   663  	voteSet2 := NewVoteSet(chainID, height, 1, tmproto.PrecommitType, valSet)
   664  
   665  	commit1, err := MakeCommit(BlockID{
   666  		Hash: header1.Hash(),
   667  		PartSetHeader: PartSetHeader{
   668  			Total: 100,
   669  			Hash:  crypto.CRandBytes(tmhash.Size),
   670  		},
   671  	}, height, 1, voteSet1, vals, time.Now())
   672  	require.NoError(t, err)
   673  	commit2, err := MakeCommit(BlockID{
   674  		Hash: header2.Hash(),
   675  		PartSetHeader: PartSetHeader{
   676  			Total: 100,
   677  			Hash:  crypto.CRandBytes(tmhash.Size),
   678  		},
   679  	}, height, 1, voteSet2, vals, time.Now())
   680  	require.NoError(t, err)
   681  
   682  	h1 := &SignedHeader{
   683  		Header: header1,
   684  		Commit: commit1,
   685  	}
   686  	h2 := &SignedHeader{
   687  		Header: header2,
   688  		Commit: commit2,
   689  	}
   690  
   691  	tests := []struct {
   692  		testName     string
   693  		evidence     Evidence
   694  		toProtoErr   bool
   695  		fromProtoErr bool
   696  	}{
   697  		{"nil fail", nil, true, true},
   698  		{"DuplicateVoteEvidence empty fail", &DuplicateVoteEvidence{}, false, true},
   699  		{"DuplicateVoteEvidence nil voteB", &DuplicateVoteEvidence{VoteA: v, VoteB: nil}, false, true},
   700  		{"DuplicateVoteEvidence nil voteA", &DuplicateVoteEvidence{VoteA: nil, VoteB: v}, false, true},
   701  		{"DuplicateVoteEvidence success", &DuplicateVoteEvidence{VoteA: v2, VoteB: v}, false, false},
   702  		{"ConflictingHeadersEvidence empty fail", &ConflictingHeadersEvidence{}, false, true},
   703  		{"ConflictingHeadersEvidence nil H2", &ConflictingHeadersEvidence{H1: h1, H2: nil}, false, true},
   704  		{"ConflictingHeadersEvidence nil H1", &ConflictingHeadersEvidence{H1: nil, H2: h2}, false, true},
   705  		{"ConflictingHeadersEvidence success", &ConflictingHeadersEvidence{H1: h1, H2: h2}, false, false},
   706  		{"LunaticValidatorEvidence success", &LunaticValidatorEvidence{Header: header1,
   707  			Vote: v, InvalidHeaderField: "ValidatorsHash"}, false, true},
   708  		{"&LunaticValidatorEvidence empty fail", &LunaticValidatorEvidence{}, false, true},
   709  		{"LunaticValidatorEvidence only header fail", &LunaticValidatorEvidence{Header: header1}, false, true},
   710  		{"LunaticValidatorEvidence only vote fail", &LunaticValidatorEvidence{Vote: v}, false, true},
   711  		{"LunaticValidatorEvidence header & vote fail", &LunaticValidatorEvidence{Header: header1, Vote: v}, false, true},
   712  		{"LunaticValidatorEvidence empty fail", &LunaticValidatorEvidence{}, false, true},
   713  		{"PotentialAmnesiaEvidence empty fail", &PotentialAmnesiaEvidence{}, false, true},
   714  		{"PotentialAmnesiaEvidence nil VoteB", &PotentialAmnesiaEvidence{VoteA: v, VoteB: nil}, false, true},
   715  		{"PotentialAmnesiaEvidence nil VoteA", &PotentialAmnesiaEvidence{VoteA: nil, VoteB: v2}, false, true},
   716  		{"PotentialAmnesiaEvidence success", &PotentialAmnesiaEvidence{VoteA: v2, VoteB: v}, false, false},
   717  		{"PhantomValidatorEvidence empty fail", &PhantomValidatorEvidence{}, false, true},
   718  		{"PhantomValidatorEvidence nil LastHeightValidatorWasInSet", &PhantomValidatorEvidence{Vote: v}, false, true},
   719  		{"PhantomValidatorEvidence nil Vote", &PhantomValidatorEvidence{LastHeightValidatorWasInSet: 2}, false, true},
   720  		{"PhantomValidatorEvidence success", &PhantomValidatorEvidence{Vote: v2, LastHeightValidatorWasInSet: 2},
   721  			false, false},
   722  		{"AmnesiaEvidence nil ProofOfLockChange", &AmnesiaEvidence{PotentialAmnesiaEvidence: &PotentialAmnesiaEvidence{},
   723  			Polc: NewEmptyPOLC()}, false, true},
   724  		{"AmnesiaEvidence nil Polc",
   725  			&AmnesiaEvidence{PotentialAmnesiaEvidence: &PotentialAmnesiaEvidence{VoteA: v2, VoteB: v},
   726  				Polc: &ProofOfLockChange{}}, false, false},
   727  		{"AmnesiaEvidence success", &AmnesiaEvidence{PotentialAmnesiaEvidence: &PotentialAmnesiaEvidence{VoteA: v2, VoteB: v},
   728  			Polc: NewEmptyPOLC()}, false, false},
   729  	}
   730  	for _, tt := range tests {
   731  		tt := tt
   732  		t.Run(tt.testName, func(t *testing.T) {
   733  			pb, err := EvidenceToProto(tt.evidence)
   734  			if tt.toProtoErr {
   735  				assert.Error(t, err, tt.testName)
   736  				return
   737  			}
   738  			assert.NoError(t, err, tt.testName)
   739  
   740  			evi, err := EvidenceFromProto(pb)
   741  			if tt.fromProtoErr {
   742  				assert.Error(t, err, tt.testName)
   743  				return
   744  			}
   745  			require.Equal(t, tt.evidence, evi, tt.testName)
   746  		})
   747  	}
   748  }
   749  
   750  func TestProofOfLockChangeProtoBuf(t *testing.T) {
   751  	// -------- Votes --------
   752  	val := NewMockPV()
   753  	val2 := NewMockPV()
   754  	val3 := NewMockPV()
   755  	blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
   756  	const chainID = "mychain"
   757  	v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime)
   758  	v2 := makeVote(t, val2, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime)
   759  
   760  	testCases := []struct {
   761  		msg          string
   762  		polc         *ProofOfLockChange
   763  		toProtoErr   bool
   764  		fromProtoErr bool
   765  	}{
   766  		{"failure, empty key", &ProofOfLockChange{Votes: []*Vote{v, v2}, PubKey: nil}, true, false},
   767  		{"failure, empty votes", &ProofOfLockChange{PubKey: val3.PrivKey.PubKey()}, true, false},
   768  		{"success empty ProofOfLockChange", NewEmptyPOLC(), false, false},
   769  		{"success", &ProofOfLockChange{Votes: []*Vote{v, v2}, PubKey: val3.PrivKey.PubKey()}, false, false},
   770  	}
   771  	for _, tc := range testCases {
   772  		tc := tc
   773  		pbpolc, err := tc.polc.ToProto()
   774  		if tc.toProtoErr {
   775  			assert.Error(t, err, tc.msg)
   776  		} else {
   777  			assert.NoError(t, err, tc.msg)
   778  		}
   779  
   780  		c, err := ProofOfLockChangeFromProto(pbpolc)
   781  		if !tc.fromProtoErr {
   782  			assert.NoError(t, err, tc.msg)
   783  			if !tc.toProtoErr {
   784  				assert.Equal(t, tc.polc, c, tc.msg)
   785  			}
   786  		} else {
   787  			assert.Error(t, err, tc.msg)
   788  		}
   789  	}
   790  }