evidence: change evidence time to block time (#5219)

adds blockstore interface to evidence and adds fix to byzantine test
This commit is contained in:
Callum Waters
2020-08-11 14:39:07 +02:00
committed by GitHub
parent f66b7a8e32
commit 312c4f8fe1
22 changed files with 527 additions and 213 deletions

View File

@@ -644,7 +644,8 @@ func TestBlockProtoBuf(t *testing.T) {
b2 := MakeBlock(h, []Tx{Tx([]byte{1})}, c1, []Evidence{})
b2.ProposerAddress = tmrand.Bytes(crypto.AddressSize)
evi := NewMockDuplicateVoteEvidence(h, time.Now(), "block-test-chain")
evidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
evi := NewMockDuplicateVoteEvidence(h, evidenceTime, "block-test-chain")
b2.Evidence = EvidenceData{Evidence: EvidenceList{evi}}
b2.EvidenceHash = b2.Evidence.Hash()
@@ -714,7 +715,7 @@ func TestEvidenceDataProtoBuf(t *testing.T) {
const chainID = "mychain"
v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, time.Now())
v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, time.Now())
ev := NewDuplicateVoteEvidence(v2, v)
ev := NewDuplicateVoteEvidence(v2, v, v2.Timestamp)
data := &EvidenceData{Evidence: EvidenceList{ev}}
_ = data.Hash()
testCases := []struct {

View File

@@ -181,13 +181,15 @@ func init() {
type DuplicateVoteEvidence struct {
VoteA *Vote `json:"vote_a"`
VoteB *Vote `json:"vote_b"`
Timestamp time.Time `json:"timestamp"`
}
var _ Evidence = &DuplicateVoteEvidence{}
// NewDuplicateVoteEvidence creates DuplicateVoteEvidence with right ordering given
// two conflicting votes. If one of the votes is nil, evidence returned is nil as well
func NewDuplicateVoteEvidence(vote1 *Vote, vote2 *Vote) *DuplicateVoteEvidence {
func NewDuplicateVoteEvidence(vote1, vote2 *Vote, time time.Time) *DuplicateVoteEvidence {
var voteA, voteB *Vote
if vote1 == nil || vote2 == nil {
return nil
@@ -202,13 +204,14 @@ func NewDuplicateVoteEvidence(vote1 *Vote, vote2 *Vote) *DuplicateVoteEvidence {
return &DuplicateVoteEvidence{
VoteA: voteA,
VoteB: voteB,
Timestamp: time,
}
}
// String returns a string representation of the evidence.
func (dve *DuplicateVoteEvidence) String() string {
return fmt.Sprintf("DuplicateVoteEvidence{VoteA: %v, VoteB: %v}", dve.VoteA, dve.VoteB)
return fmt.Sprintf("DuplicateVoteEvidence{VoteA: %v, VoteB: %v, Time: %v}", dve.VoteA, dve.VoteB, dve.Timestamp)
}
// Height returns the height this evidence refers to.
@@ -218,7 +221,7 @@ func (dve *DuplicateVoteEvidence) Height() int64 {
// Time returns time of the latest vote.
func (dve *DuplicateVoteEvidence) Time() time.Time {
return maxTime(dve.VoteA.Timestamp, dve.VoteB.Timestamp)
return dve.Timestamp
}
// Address returns the address of the validator.
@@ -358,8 +361,9 @@ func (dve *DuplicateVoteEvidence) ToProto() *tmproto.DuplicateVoteEvidence {
voteB := dve.VoteB.ToProto()
voteA := dve.VoteA.ToProto()
tp := tmproto.DuplicateVoteEvidence{
VoteA: voteA,
VoteB: voteB,
VoteA: voteA,
VoteB: voteB,
Timestamp: dve.Timestamp,
}
return &tp
}
@@ -379,10 +383,7 @@ func DuplicateVoteEvidenceFromProto(pb *tmproto.DuplicateVoteEvidence) (*Duplica
return nil, err
}
dve := new(DuplicateVoteEvidence)
dve.VoteA = vA
dve.VoteB = vB
dve := NewDuplicateVoteEvidence(vA, vB, pb.Timestamp)
return dve, dve.ValidateBasic()
}
@@ -441,11 +442,12 @@ func (ev *ConflictingHeadersEvidence) Split(committedHeader *Header, valSet *Val
if sig.Absent() {
continue
}
evList = append(evList, &LunaticValidatorEvidence{
Header: alternativeHeader.Header,
Vote: alternativeHeader.Commit.GetVote(int32(i)),
InvalidHeaderField: invalidField,
})
evList = append(evList, NewLunaticValidatorEvidence(
alternativeHeader.Header,
alternativeHeader.Commit.GetVote(int32(i)),
invalidField,
committedHeader.Time, //take the time of our own trusted header
))
}
return evList
}
@@ -483,16 +485,17 @@ OUTER_LOOP:
// messages in both commits, then it is an equivocation misbehavior =>
// immediately slashable (#F1).
if ev.H1.Commit.Round == ev.H2.Commit.Round {
evList = append(evList, &DuplicateVoteEvidence{
VoteA: ev.H1.Commit.GetVote(int32(i)),
VoteB: ev.H2.Commit.GetVote(int32(j)),
})
evList = append(evList, NewDuplicateVoteEvidence(
ev.H1.Commit.GetVote(int32(i)),
ev.H2.Commit.GetVote(int32(j)),
ev.H1.Time,
))
} else {
// if H1.Round != H2.Round we need to run full detection procedure => not
// immediately slashable.
firstVote := ev.H1.Commit.GetVote(int32(i))
secondVote := ev.H2.Commit.GetVote(int32(j))
newEv := NewPotentialAmnesiaEvidence(firstVote, secondVote)
newEv := NewPotentialAmnesiaEvidence(firstVote, secondVote, committedHeader.Time)
// has the validator incorrectly voted for a previous round
if newEv.VoteA.Round > newEv.VoteB.Round {
@@ -671,16 +674,21 @@ type LunaticValidatorEvidence struct {
Header *Header `json:"header"`
Vote *Vote `json:"vote"`
InvalidHeaderField string `json:"invalid_header_field"`
Timestamp time.Time `json:"timestamp"`
}
var _ Evidence = &LunaticValidatorEvidence{}
// NewLunaticValidatorEvidence creates a new instance of the respective evidence
func NewLunaticValidatorEvidence(header *Header, vote *Vote, invalidHeaderField string) *LunaticValidatorEvidence {
func NewLunaticValidatorEvidence(header *Header,
vote *Vote, invalidHeaderField string, time time.Time) *LunaticValidatorEvidence {
return &LunaticValidatorEvidence{
Header: header,
Vote: vote,
InvalidHeaderField: invalidHeaderField,
Timestamp: time,
}
}
@@ -690,7 +698,7 @@ func (e *LunaticValidatorEvidence) Height() int64 {
// Time returns the maximum between the header's time and vote's time.
func (e *LunaticValidatorEvidence) Time() time.Time {
return maxTime(e.Header.Time, e.Vote.Timestamp)
return e.Timestamp
}
func (e *LunaticValidatorEvidence) Address() []byte {
@@ -839,6 +847,7 @@ func (e *LunaticValidatorEvidence) ToProto() *tmproto.LunaticValidatorEvidence {
Header: h,
Vote: v,
InvalidHeaderField: e.InvalidHeaderField,
Timestamp: e.Timestamp,
}
return tp
@@ -863,6 +872,7 @@ func LunaticValidatorEvidenceFromProto(pb *tmproto.LunaticValidatorEvidence) (*L
Header: &h,
Vote: v,
InvalidHeaderField: pb.InvalidHeaderField,
Timestamp: pb.Timestamp,
}
return &tp, tp.ValidateBasic()
@@ -880,20 +890,21 @@ type PotentialAmnesiaEvidence struct {
VoteB *Vote `json:"vote_b"`
HeightStamp int64
Timestamp time.Time `json:"timestamp"`
}
var _ Evidence = &PotentialAmnesiaEvidence{}
// NewPotentialAmnesiaEvidence creates a new instance of the evidence and orders the votes correctly
func NewPotentialAmnesiaEvidence(voteA *Vote, voteB *Vote) *PotentialAmnesiaEvidence {
func NewPotentialAmnesiaEvidence(voteA, voteB *Vote, time time.Time) *PotentialAmnesiaEvidence {
if voteA == nil || voteB == nil {
return nil
}
if voteA.Timestamp.Before(voteB.Timestamp) {
return &PotentialAmnesiaEvidence{VoteA: voteA, VoteB: voteB}
return &PotentialAmnesiaEvidence{VoteA: voteA, VoteB: voteB, Timestamp: time}
}
return &PotentialAmnesiaEvidence{VoteA: voteB, VoteB: voteA}
return &PotentialAmnesiaEvidence{VoteA: voteB, VoteB: voteA, Timestamp: time}
}
func (e *PotentialAmnesiaEvidence) Height() int64 {
@@ -901,7 +912,7 @@ func (e *PotentialAmnesiaEvidence) Height() int64 {
}
func (e *PotentialAmnesiaEvidence) Time() time.Time {
return e.VoteB.Timestamp
return e.Timestamp
}
func (e *PotentialAmnesiaEvidence) Address() []byte {
@@ -1057,6 +1068,7 @@ func (e *PotentialAmnesiaEvidence) ToProto() *tmproto.PotentialAmnesiaEvidence {
VoteA: voteA,
VoteB: voteB,
HeightStamp: e.HeightStamp,
Timestamp: e.Timestamp,
}
return tp
@@ -1451,6 +1463,7 @@ func PotentialAmnesiaEvidenceFromProto(pb *tmproto.PotentialAmnesiaEvidence) (*P
VoteA: voteA,
VoteB: voteB,
HeightStamp: pb.GetHeightStamp(),
Timestamp: pb.Timestamp,
}
return &tp, tp.ValidateBasic()
@@ -1534,8 +1547,7 @@ func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time,
vB := voteB.ToProto()
_ = pv.SignVote(chainID, vB)
voteB.Signature = vB.Signature
return NewDuplicateVoteEvidence(voteA, voteB)
return NewDuplicateVoteEvidence(voteA, voteB, time)
}
func makeMockVote(height int64, round, index int32, addr Address,

View File

@@ -23,7 +23,7 @@ type voteData struct {
var defaultVoteTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
func TestEvidence(t *testing.T) {
func TestDuplicateVoteEvidence(t *testing.T) {
val := NewMockPV()
val2 := NewMockPV()
@@ -67,6 +67,8 @@ func TestEvidence(t *testing.T) {
ev := &DuplicateVoteEvidence{
VoteA: c.vote1,
VoteB: c.vote2,
Timestamp: defaultVoteTime,
}
if c.valid {
assert.Nil(t, ev.Verify(chainID, pubKey), "evidence should be valid")
@@ -74,19 +76,12 @@ func TestEvidence(t *testing.T) {
assert.NotNil(t, ev.Verify(chainID, pubKey), "evidence should be invalid")
}
}
}
func TestDuplicatedVoteEvidence(t *testing.T) {
ev := randomDuplicatedVoteEvidence(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) {
@@ -187,7 +182,7 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) {
t.Run(tc.testName, func(t *testing.T) {
vote1 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID, defaultVoteTime)
vote2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID2, defaultVoteTime)
ev := NewDuplicateVoteEvidence(vote1, vote2)
ev := NewDuplicateVoteEvidence(vote1, vote2, vote1.Timestamp)
tc.malleateEvidence(ev)
assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
@@ -220,11 +215,11 @@ func TestLunaticValidatorEvidence(t *testing.T) {
vote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, blockID, defaultVoteTime)
ev := NewLunaticValidatorEvidence(header, vote, "AppHash")
ev := NewLunaticValidatorEvidence(header, vote, "AppHash", bTime)
//happy path
assert.Equal(t, header.Height, ev.Height())
assert.Equal(t, defaultVoteTime, ev.Time())
assert.Equal(t, bTime, ev.Time())
assert.EqualValues(t, vote.ValidatorAddress, ev.Address())
assert.NotEmpty(t, ev.Hash())
assert.NotEmpty(t, ev.Bytes())
@@ -248,12 +243,12 @@ func TestLunaticValidatorEvidence(t *testing.T) {
emptyBlockVote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, BlockID{}, defaultVoteTime)
invalidLunaticEvidence := []*LunaticValidatorEvidence{
NewLunaticValidatorEvidence(header, invalidVote, "AppHash"),
NewLunaticValidatorEvidence(header, invalidHeightVote, "AppHash"),
NewLunaticValidatorEvidence(nil, vote, "AppHash"),
NewLunaticValidatorEvidence(header, nil, "AppHash"),
NewLunaticValidatorEvidence(header, vote, "other"),
NewLunaticValidatorEvidence(header, emptyBlockVote, "AppHash"),
NewLunaticValidatorEvidence(header, invalidVote, "AppHash", header.Time),
NewLunaticValidatorEvidence(header, invalidHeightVote, "AppHash", header.Time),
NewLunaticValidatorEvidence(nil, vote, "AppHash", vote.Timestamp),
NewLunaticValidatorEvidence(header, nil, "AppHash", header.Time),
NewLunaticValidatorEvidence(header, vote, "other", header.Time),
NewLunaticValidatorEvidence(header, emptyBlockVote, "AppHash", header.Time),
}
for idx, ev := range invalidLunaticEvidence {
@@ -348,10 +343,10 @@ func TestPotentialAmnesiaEvidence(t *testing.T) {
vote3 = makeVote(t, val, chainID, 0, height, 2, 2, blockID, defaultVoteTime)
)
ev := NewPotentialAmnesiaEvidence(vote1, vote2)
ev := NewPotentialAmnesiaEvidence(vote1, vote2, vote1.Timestamp)
assert.Equal(t, height, ev.Height())
assert.Equal(t, vote2.Timestamp, ev.Time())
assert.Equal(t, vote1.Timestamp, ev.Time())
assert.EqualValues(t, vote1.ValidatorAddress, ev.Address())
assert.NotEmpty(t, ev.Hash())
assert.NotEmpty(t, ev.Bytes())
@@ -375,7 +370,7 @@ func TestPotentialAmnesiaEvidence(t *testing.T) {
assert.True(t, ev.Equal(ev2))
assert.Equal(t, ev.Hash(), ev2.Hash())
ev3 := NewPotentialAmnesiaEvidence(vote2, vote1)
ev3 := NewPotentialAmnesiaEvidence(vote2, vote1, vote1.Timestamp)
assert.True(t, ev3.Equal(ev))
ev4 := &PotentialAmnesiaEvidence{

View File

@@ -29,16 +29,18 @@ var (
)
type ErrVoteConflictingVotes struct {
*DuplicateVoteEvidence
VoteA *Vote
VoteB *Vote
}
func (err *ErrVoteConflictingVotes) Error() string {
return fmt.Sprintf("conflicting votes from validator %X", err.VoteA.ValidatorAddress)
}
func NewConflictingVoteError(val *Validator, vote1, vote2 *Vote) *ErrVoteConflictingVotes {
func NewConflictingVoteError(vote1, vote2 *Vote) *ErrVoteConflictingVotes {
return &ErrVoteConflictingVotes{
NewDuplicateVoteEvidence(vote1, vote2),
VoteA: vote1,
VoteB: vote2,
}
}

View File

@@ -207,7 +207,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
// Add vote and get conflicting vote if any.
added, conflicting := voteSet.addVerifiedVote(vote, blockKey, val.VotingPower)
if conflicting != nil {
return added, NewConflictingVoteError(val, conflicting, vote)
return added, NewConflictingVoteError(conflicting, vote)
}
if !added {
panic("Expected to add non-conflicting vote")