From 0b13059216b179d99d4544ca9b0242481e3ca6cc Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 23 Jun 2020 11:10:57 +0400 Subject: [PATCH] types: fix evidence timestamp calculation (#5032) depending on the votes order in DuplicateVoteEvidence is arbitrary. we should always choose the latest timestamp. Closes #5030 --- types/evidence.go | 26 +++++++++++++++++++------- types/evidence_test.go | 16 +++++++++++----- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/types/evidence.go b/types/evidence.go index 54446a7c7..9b9cadbea 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -310,9 +310,9 @@ func (dve *DuplicateVoteEvidence) Height() int64 { return dve.VoteA.Height } -// Time returns the time the evidence was created. +// Time returns time of the latest vote. func (dve *DuplicateVoteEvidence) Time() time.Time { - return dve.VoteA.Timestamp + return maxTime(dve.VoteA.Timestamp, dve.VoteB.Timestamp) } // Address returns the address of the validator. @@ -677,8 +677,10 @@ OUTER_LOOP: func (ev ConflictingHeadersEvidence) Height() int64 { return ev.H1.Height } -// XXX: this is not the time of equivocation -func (ev ConflictingHeadersEvidence) Time() time.Time { return ev.H1.Time } +// Time returns time of the latest header. +func (ev ConflictingHeadersEvidence) Time() time.Time { + return maxTime(ev.H1.Time, ev.H2.Time) +} func (ev ConflictingHeadersEvidence) Address() []byte { panic("use ConflictingHeadersEvidence#Split to split evidence into individual pieces") @@ -833,6 +835,7 @@ func (e PhantomValidatorEvidence) Height() int64 { return e.Vote.Height } +// Time returns the Vote's timestamp. func (e PhantomValidatorEvidence) Time() time.Time { return e.Vote.Timestamp } @@ -954,8 +957,9 @@ func (e LunaticValidatorEvidence) Height() int64 { return e.Header.Height } +// Time returns the maximum between the header's time and vote's time. func (e LunaticValidatorEvidence) Time() time.Time { - return e.Header.Time + return maxTime(e.Header.Time, e.Vote.Timestamp) } func (e LunaticValidatorEvidence) Address() []byte { @@ -1145,8 +1149,9 @@ func (e PotentialAmnesiaEvidence) Height() int64 { return e.VoteA.Height } +// Time returns time of the latest vote. func (e PotentialAmnesiaEvidence) Time() time.Time { - return e.VoteA.Timestamp + return maxTime(e.VoteA.Timestamp, e.VoteB.Timestamp) } func (e PotentialAmnesiaEvidence) Address() []byte { @@ -1340,7 +1345,7 @@ func (e ProofOfLockChange) Height() int64 { return e.Votes[0].Height } -// returns the time of the last vote +// Time returns time of the latest vote. func (e ProofOfLockChange) Time() time.Time { latest := e.Votes[0].Timestamp for _, vote := range e.Votes { @@ -1770,3 +1775,10 @@ func NewMockPOLC(height int64, time time.Time, pubKey crypto.PubKey) ProofOfLock PubKey: pubKey, } } + +func maxTime(t1 time.Time, t2 time.Time) time.Time { + if t1.After(t2) { + return t1 + } + return t2 +} diff --git a/types/evidence_test.go b/types/evidence_test.go index 542865a49..3ead30731 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -80,6 +80,12 @@ func TestDuplicatedVoteEvidence(t *testing.T) { assert.True(t, ev.Equal(ev)) assert.False(t, ev.Equal(&DuplicateVoteEvidence{})) + + maxTime := ev.VoteB.Timestamp + if ev.VoteA.Timestamp.After(ev.VoteB.Timestamp) { + maxTime = ev.VoteA.Timestamp + } + assert.Equal(t, maxTime, ev.Time(), "expected time of the latest vote") } func TestEvidenceList(t *testing.T) { @@ -152,7 +158,7 @@ func randomDuplicatedVoteEvidence(t *testing.T) *DuplicateVoteEvidence { const chainID = "mychain" return &DuplicateVoteEvidence{ VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime), - VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime), + VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime.Add(1*time.Minute)), } } @@ -223,7 +229,7 @@ func TestLunaticValidatorEvidence(t *testing.T) { } assert.Equal(t, header.Height, ev.Height()) - assert.Equal(t, bTime, ev.Time()) + assert.Equal(t, defaultVoteTime, ev.Time()) assert.EqualValues(t, vote.ValidatorAddress, ev.Address()) assert.NotEmpty(t, ev.Hash()) assert.NotEmpty(t, ev.Bytes()) @@ -330,7 +336,7 @@ func TestConflictingHeadersEvidence(t *testing.T) { }) assert.Equal(t, height, ev.Height()) - // assert.Equal(t, bTime, ev.Time()) + assert.Equal(t, ev.H2.Time, ev.Time()) assert.NotEmpty(t, ev.Hash()) assert.NotEmpty(t, ev.Bytes()) assert.NoError(t, ev.VerifyComposite(header1, valSet)) @@ -350,7 +356,7 @@ func TestPotentialAmnesiaEvidence(t *testing.T) { blockID = makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) blockID2 = makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) vote1 = makeVote(t, val, chainID, 0, height, 0, 2, blockID, defaultVoteTime) - vote2 = makeVote(t, val, chainID, 0, height, 1, 2, blockID2, defaultVoteTime) + vote2 = makeVote(t, val, chainID, 0, height, 1, 2, blockID2, defaultVoteTime.Add(1*time.Minute)) ) ev := &PotentialAmnesiaEvidence{ @@ -359,7 +365,7 @@ func TestPotentialAmnesiaEvidence(t *testing.T) { } assert.Equal(t, height, ev.Height()) - // assert.Equal(t, bTime, ev.Time()) + assert.Equal(t, vote2.Timestamp, ev.Time()) assert.EqualValues(t, vote1.ValidatorAddress, ev.Address()) assert.NotEmpty(t, ev.Hash()) assert.NotEmpty(t, ev.Bytes())