mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-06 05:25:35 +00:00
evidence: remove ConflictingHeaders type (#5317)
## Description Remove ConflictingHeaders & compositeEvidence types Ref #5288
This commit is contained in:
@@ -87,12 +87,7 @@ func (b *Block) ValidateBasic() error {
|
||||
|
||||
// NOTE: b.Evidence.Evidence may be nil, but we're just looping.
|
||||
for i, ev := range b.Evidence.Evidence {
|
||||
switch ev.(type) {
|
||||
case *ConflictingHeadersEvidence:
|
||||
// ConflictingHeadersEvidence must be broken up in pieces and never
|
||||
// committed as a single piece.
|
||||
return fmt.Errorf("found ConflictingHeadersEvidence (#%d)", i)
|
||||
case *PotentialAmnesiaEvidence:
|
||||
if _, ok := ev.(*PotentialAmnesiaEvidence); ok {
|
||||
// PotentialAmnesiaEvidence does not contribute to anything on its own, so
|
||||
// reject it as well.
|
||||
return fmt.Errorf("found PotentialAmnesiaEvidence (#%d)", i)
|
||||
|
||||
@@ -86,9 +86,6 @@ func TestBlockValidateBasic(t *testing.T) {
|
||||
{"Tampered EvidenceHash", func(blk *Block) {
|
||||
blk.EvidenceHash = []byte("something else")
|
||||
}, true},
|
||||
{"ConflictingHeadersEvidence", func(blk *Block) {
|
||||
blk.Evidence = EvidenceData{Evidence: []Evidence{&ConflictingHeadersEvidence{}}}
|
||||
}, true},
|
||||
{"PotentialAmnesiaEvidence", func(blk *Block) {
|
||||
blk.Evidence = EvidenceData{Evidence: []Evidence{&PotentialAmnesiaEvidence{}}}
|
||||
}, true},
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
@@ -31,11 +30,6 @@ type Evidence interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type CompositeEvidence interface {
|
||||
VerifyComposite(committedHeader *Header, valSet *ValidatorSet) error
|
||||
Split(committedHeader *Header, valSet *ValidatorSet) []Evidence
|
||||
}
|
||||
|
||||
const (
|
||||
// MaxEvidenceBytes is a maximum size of any evidence (including amino overhead).
|
||||
MaxEvidenceBytes int64 = 444
|
||||
@@ -98,16 +92,6 @@ func EvidenceToProto(evidence Evidence) (*tmproto.Evidence, error) {
|
||||
}
|
||||
return tp, nil
|
||||
|
||||
case *ConflictingHeadersEvidence:
|
||||
pbevi := evi.ToProto()
|
||||
|
||||
tp := &tmproto.Evidence{
|
||||
Sum: &tmproto.Evidence_ConflictingHeadersEvidence{
|
||||
ConflictingHeadersEvidence: pbevi,
|
||||
},
|
||||
}
|
||||
|
||||
return tp, nil
|
||||
case *LunaticValidatorEvidence:
|
||||
pbevi := evi.ToProto()
|
||||
|
||||
@@ -153,8 +137,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) {
|
||||
switch evi := evidence.Sum.(type) {
|
||||
case *tmproto.Evidence_DuplicateVoteEvidence:
|
||||
return DuplicateVoteEvidenceFromProto(evi.DuplicateVoteEvidence)
|
||||
case *tmproto.Evidence_ConflictingHeadersEvidence:
|
||||
return ConflictingHeadersEvidenceFromProto(evi.ConflictingHeadersEvidence)
|
||||
case *tmproto.Evidence_LunaticValidatorEvidence:
|
||||
return LunaticValidatorEvidenceFromProto(evi.LunaticValidatorEvidence)
|
||||
case *tmproto.Evidence_PotentialAmnesiaEvidence:
|
||||
@@ -168,7 +150,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) {
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(&DuplicateVoteEvidence{}, "tendermint/DuplicateVoteEvidence")
|
||||
tmjson.RegisterType(&ConflictingHeadersEvidence{}, "tendermint/ConflictingHeadersEvidence")
|
||||
tmjson.RegisterType(&LunaticValidatorEvidence{}, "tendermint/LunaticValidatorEvidence")
|
||||
tmjson.RegisterType(&PotentialAmnesiaEvidence{}, "tendermint/PotentialAmnesiaEvidence")
|
||||
tmjson.RegisterType(&AmnesiaEvidence{}, "tendermint/AmnesiaEvidence")
|
||||
@@ -379,286 +360,6 @@ func DuplicateVoteEvidenceFromProto(pb *tmproto.DuplicateVoteEvidence) (*Duplica
|
||||
return dve, dve.ValidateBasic()
|
||||
}
|
||||
|
||||
// ConflictingHeadersEvidence is primarily used by the light client when it
|
||||
// observes two conflicting headers, both having 1/3+ of the voting power of
|
||||
// the currently trusted validator set.
|
||||
type ConflictingHeadersEvidence struct {
|
||||
H1 *SignedHeader `json:"h_1"`
|
||||
H2 *SignedHeader `json:"h_2"`
|
||||
}
|
||||
|
||||
var _ Evidence = &ConflictingHeadersEvidence{}
|
||||
var _ CompositeEvidence = &ConflictingHeadersEvidence{}
|
||||
|
||||
// NewConflictingHeadersEvidence creates a new instance of the respective evidence
|
||||
func NewConflictingHeadersEvidence(h1, h2 *SignedHeader) *ConflictingHeadersEvidence {
|
||||
return &ConflictingHeadersEvidence{H1: h1, H2: h2}
|
||||
}
|
||||
|
||||
// Split breaks up evidence into smaller chunks of evidence:
|
||||
// LunaticValidatorEvidence, DuplicateVoteEvidence and
|
||||
// PotentialAmnesiaEvidence.
|
||||
//
|
||||
// committedHeader - header at height H1.Height == H2.Height
|
||||
// valSet - validator set at height H1.Height == H2.Height
|
||||
func (ev *ConflictingHeadersEvidence) Split(committedHeader *Header, valSet *ValidatorSet) []Evidence {
|
||||
evList := make([]Evidence, 0)
|
||||
|
||||
var alternativeHeader *SignedHeader
|
||||
if bytes.Equal(committedHeader.Hash(), ev.H1.Hash()) {
|
||||
alternativeHeader = ev.H2
|
||||
} else {
|
||||
alternativeHeader = ev.H1
|
||||
}
|
||||
|
||||
// If ValidatorsHash, NextValidatorsHash, ConsensusHash, AppHash, and
|
||||
// LastResultsHash in alternativeHeader are different (incorrect application
|
||||
// state transition), then it is a lunatic misbehavior => immediately
|
||||
// slashable (#F5).
|
||||
var invalidField string
|
||||
switch {
|
||||
case !bytes.Equal(committedHeader.ValidatorsHash, alternativeHeader.ValidatorsHash):
|
||||
invalidField = "ValidatorsHash"
|
||||
case !bytes.Equal(committedHeader.NextValidatorsHash, alternativeHeader.NextValidatorsHash):
|
||||
invalidField = "NextValidatorsHash"
|
||||
case !bytes.Equal(committedHeader.ConsensusHash, alternativeHeader.ConsensusHash):
|
||||
invalidField = "ConsensusHash"
|
||||
case !bytes.Equal(committedHeader.AppHash, alternativeHeader.AppHash):
|
||||
invalidField = "AppHash"
|
||||
case !bytes.Equal(committedHeader.LastResultsHash, alternativeHeader.LastResultsHash):
|
||||
invalidField = "LastResultsHash"
|
||||
}
|
||||
if invalidField != "" {
|
||||
for i, sig := range alternativeHeader.Commit.Signatures {
|
||||
if sig.Absent() {
|
||||
continue
|
||||
}
|
||||
evList = append(evList, NewLunaticValidatorEvidence(
|
||||
alternativeHeader.Header,
|
||||
alternativeHeader.Commit.GetVote(int32(i)),
|
||||
invalidField,
|
||||
committedHeader.Time, //take the time of our own trusted header
|
||||
))
|
||||
}
|
||||
return evList
|
||||
}
|
||||
|
||||
// Use the fact that signatures are sorted by ValidatorAddress.
|
||||
var (
|
||||
i = 0
|
||||
j = 0
|
||||
)
|
||||
OUTER_LOOP:
|
||||
for i < len(ev.H1.Commit.Signatures) {
|
||||
sigA := ev.H1.Commit.Signatures[i]
|
||||
if sigA.Absent() {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
// FIXME: Replace with HasAddress once DuplicateVoteEvidence#PubKey is
|
||||
// removed.
|
||||
_, val := valSet.GetByAddress(sigA.ValidatorAddress)
|
||||
if val == nil {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
for j < len(ev.H2.Commit.Signatures) {
|
||||
sigB := ev.H2.Commit.Signatures[j]
|
||||
if sigB.Absent() {
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
||||
switch bytes.Compare(sigA.ValidatorAddress, sigB.ValidatorAddress) {
|
||||
case 0:
|
||||
// if H1.Round == H2.Round, and some signers signed different precommit
|
||||
// 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, 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, committedHeader.Time)
|
||||
|
||||
// has the validator incorrectly voted for a previous round
|
||||
if newEv.VoteA.Round > newEv.VoteB.Round {
|
||||
evList = append(evList, NewAmnesiaEvidence(newEv, NewEmptyPOLC()))
|
||||
} else {
|
||||
evList = append(evList, newEv)
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
j++
|
||||
continue OUTER_LOOP
|
||||
case 1:
|
||||
i++
|
||||
continue OUTER_LOOP
|
||||
case -1:
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return evList
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) Height() int64 { return ev.H1.Height }
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) Bytes() []byte {
|
||||
pbe := ev.ToProto()
|
||||
|
||||
bz, err := pbe.Marshal()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return bz
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) Hash() []byte {
|
||||
bz := make([]byte, tmhash.Size*2)
|
||||
copy(bz[:tmhash.Size-1], ev.H1.Hash().Bytes())
|
||||
copy(bz[tmhash.Size:], ev.H2.Hash().Bytes())
|
||||
return tmhash.Sum(bz)
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) Verify(chainID string, _ crypto.PubKey) error {
|
||||
panic("use ConflictingHeadersEvidence#VerifyComposite to verify composite evidence")
|
||||
}
|
||||
|
||||
// VerifyComposite verifies that both headers belong to the same chain, same
|
||||
// height and signed by 1/3+ of validators at height H1.Height == H2.Height.
|
||||
func (ev *ConflictingHeadersEvidence) VerifyComposite(committedHeader *Header, valSet *ValidatorSet) error {
|
||||
var alternativeHeader *SignedHeader
|
||||
switch {
|
||||
case bytes.Equal(committedHeader.Hash(), ev.H1.Hash()):
|
||||
alternativeHeader = ev.H2
|
||||
case bytes.Equal(committedHeader.Hash(), ev.H2.Hash()):
|
||||
alternativeHeader = ev.H1
|
||||
default:
|
||||
return errors.New("none of the headers are committed from this node's perspective")
|
||||
}
|
||||
|
||||
// ChainID must be the same
|
||||
if committedHeader.ChainID != alternativeHeader.ChainID {
|
||||
return errors.New("alt header is from a different chain")
|
||||
}
|
||||
|
||||
// Height must be the same
|
||||
if committedHeader.Height != alternativeHeader.Height {
|
||||
return errors.New("alt header is from a different height")
|
||||
}
|
||||
|
||||
// Limit the number of signatures to avoid DoS attacks where a header
|
||||
// contains too many signatures.
|
||||
//
|
||||
// Validator set size = 100 [node]
|
||||
// Max validator set size = 100 * 2 = 200 [fork?]
|
||||
maxNumValidators := valSet.Size() * 2
|
||||
if len(alternativeHeader.Commit.Signatures) > maxNumValidators {
|
||||
return fmt.Errorf("alt commit contains too many signatures: %d, expected no more than %d",
|
||||
len(alternativeHeader.Commit.Signatures),
|
||||
maxNumValidators)
|
||||
}
|
||||
|
||||
// Header must be signed by at least 1/3+ of voting power of currently
|
||||
// trusted validator set.
|
||||
if err := valSet.VerifyCommitLightTrusting(
|
||||
alternativeHeader.ChainID,
|
||||
alternativeHeader.Commit,
|
||||
tmmath.Fraction{Numerator: 1, Denominator: 3}); err != nil {
|
||||
return fmt.Errorf("alt header does not have 1/3+ of voting power of our validator set: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) Equal(ev2 Evidence) bool {
|
||||
if e2, ok := ev2.(*ConflictingHeadersEvidence); ok {
|
||||
return bytes.Equal(ev.H1.Hash(), e2.H1.Hash()) && bytes.Equal(ev.H2.Hash(), e2.H2.Hash())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) ValidateBasic() error {
|
||||
if ev == nil {
|
||||
return errors.New("empty conflicting headers evidence")
|
||||
}
|
||||
|
||||
if ev.H1 == nil {
|
||||
return errors.New("first header is missing")
|
||||
}
|
||||
|
||||
if ev.H2 == nil {
|
||||
return errors.New("second header is missing")
|
||||
}
|
||||
|
||||
if err := ev.H1.ValidateBasic(ev.H1.ChainID); err != nil {
|
||||
return fmt.Errorf("h1: %w", err)
|
||||
}
|
||||
if err := ev.H2.ValidateBasic(ev.H2.ChainID); err != nil {
|
||||
return fmt.Errorf("h2: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) String() string {
|
||||
return fmt.Sprintf("ConflictingHeadersEvidence{H1: %d#%X, H2: %d#%X}",
|
||||
ev.H1.Height, ev.H1.Hash(),
|
||||
ev.H2.Height, ev.H2.Hash())
|
||||
}
|
||||
|
||||
func (ev *ConflictingHeadersEvidence) ToProto() *tmproto.ConflictingHeadersEvidence {
|
||||
pbh1 := ev.H1.ToProto()
|
||||
pbh2 := ev.H2.ToProto()
|
||||
|
||||
tp := &tmproto.ConflictingHeadersEvidence{
|
||||
H1: pbh1,
|
||||
H2: pbh2,
|
||||
}
|
||||
return tp
|
||||
}
|
||||
|
||||
func ConflictingHeadersEvidenceFromProto(pb *tmproto.ConflictingHeadersEvidence) (*ConflictingHeadersEvidence, error) {
|
||||
if pb == nil {
|
||||
return &ConflictingHeadersEvidence{}, errors.New("nil ConflictingHeadersEvidence")
|
||||
}
|
||||
h1, err := SignedHeaderFromProto(pb.H1)
|
||||
if err != nil {
|
||||
return &ConflictingHeadersEvidence{}, fmt.Errorf("from proto err: %w", err)
|
||||
}
|
||||
h2, err := SignedHeaderFromProto(pb.H2)
|
||||
if err != nil {
|
||||
return &ConflictingHeadersEvidence{}, fmt.Errorf("from proto err: %w", err)
|
||||
}
|
||||
|
||||
tp := &ConflictingHeadersEvidence{
|
||||
H1: h1,
|
||||
H2: h2,
|
||||
}
|
||||
|
||||
return tp, tp.ValidateBasic()
|
||||
}
|
||||
|
||||
//-------------------------------------------
|
||||
|
||||
type LunaticValidatorEvidence struct {
|
||||
@@ -1572,10 +1273,3 @@ 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
|
||||
}
|
||||
|
||||
@@ -112,19 +112,12 @@ func TestMaxEvidenceBytes(t *testing.T) {
|
||||
// InvalidHeaderField: "",
|
||||
// }
|
||||
|
||||
// signedHeader := SignedHeader{Header: makeHeaderRandom(), Commit: randCommit(time.Now())}
|
||||
// evc := &ConflictingHeadersEvidence{
|
||||
// H1: &signedHeader,
|
||||
// H2: &signedHeader,
|
||||
// }
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
evidence Evidence
|
||||
}{
|
||||
{"DuplicateVote", ev},
|
||||
// {"LunaticValidatorEvidence", evl},
|
||||
// {"ConflictingHeadersEvidence", evc},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
@@ -256,76 +249,6 @@ func TestLunaticValidatorEvidence(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestConflictingHeadersEvidence(t *testing.T) {
|
||||
const (
|
||||
chainID = "TestConflictingHeadersEvidence"
|
||||
height int64 = 37
|
||||
)
|
||||
|
||||
var (
|
||||
blockID = makeBlockIDRandom()
|
||||
header1 = makeHeaderRandom()
|
||||
header2 = makeHeaderRandom()
|
||||
)
|
||||
|
||||
header1.Height = height
|
||||
header1.LastBlockID = blockID
|
||||
header1.ChainID = chainID
|
||||
|
||||
header2.Height = height
|
||||
header2.LastBlockID = blockID
|
||||
header2.ChainID = chainID
|
||||
|
||||
voteSet1, valSet, vals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1)
|
||||
voteSet2 := NewVoteSet(chainID, height, 1, tmproto.PrecommitType, valSet)
|
||||
|
||||
commit1, err := MakeCommit(BlockID{
|
||||
Hash: header1.Hash(),
|
||||
PartSetHeader: PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: crypto.CRandBytes(tmhash.Size),
|
||||
},
|
||||
}, height, 1, voteSet1, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
commit2, err := MakeCommit(BlockID{
|
||||
Hash: header2.Hash(),
|
||||
PartSetHeader: PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: crypto.CRandBytes(tmhash.Size),
|
||||
},
|
||||
}, height, 1, voteSet2, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
h1 := &SignedHeader{
|
||||
Header: header1,
|
||||
Commit: commit1,
|
||||
}
|
||||
h2 := &SignedHeader{
|
||||
Header: header2,
|
||||
Commit: commit2,
|
||||
}
|
||||
|
||||
ev := NewConflictingHeadersEvidence(h1, h2)
|
||||
|
||||
assert.Panics(t, func() {
|
||||
ev.Address()
|
||||
})
|
||||
|
||||
assert.Panics(t, func() {
|
||||
pubKey, _ := vals[0].GetPubKey()
|
||||
ev.Verify(chainID, pubKey)
|
||||
})
|
||||
|
||||
assert.Equal(t, height, ev.Height())
|
||||
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))
|
||||
assert.True(t, ev.Equal(ev))
|
||||
assert.NoError(t, ev.ValidateBasic())
|
||||
assert.NotEmpty(t, ev.String())
|
||||
}
|
||||
|
||||
func TestPotentialAmnesiaEvidence(t *testing.T) {
|
||||
const (
|
||||
chainID = "TestPotentialAmnesiaEvidence"
|
||||
@@ -643,35 +566,6 @@ func TestEvidenceProto(t *testing.T) {
|
||||
header2.LastBlockID = blockID
|
||||
header2.ChainID = chainID
|
||||
|
||||
voteSet1, valSet, vals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1)
|
||||
voteSet2 := NewVoteSet(chainID, height, 1, tmproto.PrecommitType, valSet)
|
||||
|
||||
commit1, err := MakeCommit(BlockID{
|
||||
Hash: header1.Hash(),
|
||||
PartSetHeader: PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: crypto.CRandBytes(tmhash.Size),
|
||||
},
|
||||
}, height, 1, voteSet1, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
commit2, err := MakeCommit(BlockID{
|
||||
Hash: header2.Hash(),
|
||||
PartSetHeader: PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: crypto.CRandBytes(tmhash.Size),
|
||||
},
|
||||
}, height, 1, voteSet2, vals, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
h1 := &SignedHeader{
|
||||
Header: header1,
|
||||
Commit: commit1,
|
||||
}
|
||||
h2 := &SignedHeader{
|
||||
Header: header2,
|
||||
Commit: commit2,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
testName string
|
||||
evidence Evidence
|
||||
@@ -683,10 +577,6 @@ func TestEvidenceProto(t *testing.T) {
|
||||
{"DuplicateVoteEvidence nil voteB", &DuplicateVoteEvidence{VoteA: v, VoteB: nil}, false, true},
|
||||
{"DuplicateVoteEvidence nil voteA", &DuplicateVoteEvidence{VoteA: nil, VoteB: v}, false, true},
|
||||
{"DuplicateVoteEvidence success", &DuplicateVoteEvidence{VoteA: v2, VoteB: v}, false, false},
|
||||
{"ConflictingHeadersEvidence empty fail", &ConflictingHeadersEvidence{}, false, true},
|
||||
{"ConflictingHeadersEvidence nil H2", &ConflictingHeadersEvidence{H1: h1, H2: nil}, false, true},
|
||||
{"ConflictingHeadersEvidence nil H1", &ConflictingHeadersEvidence{H1: nil, H2: h2}, false, true},
|
||||
{"ConflictingHeadersEvidence success", &ConflictingHeadersEvidence{H1: h1, H2: h2}, false, false},
|
||||
{"LunaticValidatorEvidence success", &LunaticValidatorEvidence{Header: header1,
|
||||
Vote: v, InvalidHeaderField: "ValidatorsHash"}, false, true},
|
||||
{"&LunaticValidatorEvidence empty fail", &LunaticValidatorEvidence{}, false, true},
|
||||
|
||||
Reference in New Issue
Block a user