mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-30 03:35:34 +00:00
@@ -33,14 +33,6 @@ type Evidence interface {
|
||||
const (
|
||||
// MaxEvidenceBytes is a maximum size of any evidence (including amino overhead).
|
||||
MaxEvidenceBytes int64 = 444
|
||||
|
||||
// An invalid field in the header from LunaticValidatorEvidence.
|
||||
// Must be a function of the ABCI application state.
|
||||
ValidatorsHashField = "ValidatorsHash"
|
||||
NextValidatorsHashField = "NextValidatorsHash"
|
||||
ConsensusHashField = "ConsensusHash"
|
||||
AppHashField = "AppHash"
|
||||
LastResultsHashField = "LastResultsHash"
|
||||
)
|
||||
|
||||
// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
|
||||
@@ -92,17 +84,6 @@ func EvidenceToProto(evidence Evidence) (*tmproto.Evidence, error) {
|
||||
}
|
||||
return tp, nil
|
||||
|
||||
case *LunaticValidatorEvidence:
|
||||
pbevi := evi.ToProto()
|
||||
|
||||
tp := &tmproto.Evidence{
|
||||
Sum: &tmproto.Evidence_LunaticValidatorEvidence{
|
||||
LunaticValidatorEvidence: pbevi,
|
||||
},
|
||||
}
|
||||
|
||||
return tp, nil
|
||||
|
||||
case *PotentialAmnesiaEvidence:
|
||||
pbevi := evi.ToProto()
|
||||
|
||||
@@ -137,8 +118,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) {
|
||||
switch evi := evidence.Sum.(type) {
|
||||
case *tmproto.Evidence_DuplicateVoteEvidence:
|
||||
return DuplicateVoteEvidenceFromProto(evi.DuplicateVoteEvidence)
|
||||
case *tmproto.Evidence_LunaticValidatorEvidence:
|
||||
return LunaticValidatorEvidenceFromProto(evi.LunaticValidatorEvidence)
|
||||
case *tmproto.Evidence_PotentialAmnesiaEvidence:
|
||||
return PotentialAmnesiaEvidenceFromProto(evi.PotentialAmnesiaEvidence)
|
||||
case *tmproto.Evidence_AmnesiaEvidence:
|
||||
@@ -150,7 +129,6 @@ func EvidenceFromProto(evidence *tmproto.Evidence) (Evidence, error) {
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(&DuplicateVoteEvidence{}, "tendermint/DuplicateVoteEvidence")
|
||||
tmjson.RegisterType(&LunaticValidatorEvidence{}, "tendermint/LunaticValidatorEvidence")
|
||||
tmjson.RegisterType(&PotentialAmnesiaEvidence{}, "tendermint/PotentialAmnesiaEvidence")
|
||||
tmjson.RegisterType(&AmnesiaEvidence{}, "tendermint/AmnesiaEvidence")
|
||||
}
|
||||
@@ -362,216 +340,6 @@ func DuplicateVoteEvidenceFromProto(pb *tmproto.DuplicateVoteEvidence) (*Duplica
|
||||
|
||||
//-------------------------------------------
|
||||
|
||||
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, time time.Time) *LunaticValidatorEvidence {
|
||||
return &LunaticValidatorEvidence{
|
||||
Header: header,
|
||||
Vote: vote,
|
||||
InvalidHeaderField: invalidHeaderField,
|
||||
|
||||
Timestamp: time,
|
||||
}
|
||||
}
|
||||
|
||||
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.Timestamp
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) Address() []byte {
|
||||
return e.Vote.ValidatorAddress
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) Hash() []byte {
|
||||
bz := make([]byte, tmhash.Size+crypto.AddressSize)
|
||||
copy(bz[:tmhash.Size-1], e.Header.Hash().Bytes())
|
||||
copy(bz[tmhash.Size:], e.Vote.ValidatorAddress.Bytes())
|
||||
return tmhash.Sum(bz)
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) Bytes() []byte {
|
||||
pbe := e.ToProto()
|
||||
|
||||
bz, err := pbe.Marshal()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return bz
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) Verify(chainID string, pubKey crypto.PubKey) error {
|
||||
// chainID must be the same
|
||||
if chainID != e.Header.ChainID {
|
||||
return fmt.Errorf("chainID do not match: %s vs %s",
|
||||
chainID,
|
||||
e.Header.ChainID,
|
||||
)
|
||||
}
|
||||
|
||||
v := e.Vote.ToProto()
|
||||
if !pubKey.VerifySignature(VoteSignBytes(chainID, v), e.Vote.Signature) {
|
||||
return errors.New("invalid signature")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) Equal(ev Evidence) bool {
|
||||
if e2, ok := ev.(*LunaticValidatorEvidence); ok {
|
||||
return bytes.Equal(e.Header.Hash(), e2.Header.Hash()) &&
|
||||
bytes.Equal(e.Vote.ValidatorAddress, e2.Vote.ValidatorAddress)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) ValidateBasic() error {
|
||||
if e == nil {
|
||||
return errors.New("empty lunatic validator evidence")
|
||||
}
|
||||
|
||||
if e.Header == nil {
|
||||
return errors.New("empty header")
|
||||
}
|
||||
|
||||
if e.Vote == nil {
|
||||
return errors.New("empty vote")
|
||||
}
|
||||
|
||||
if err := e.Header.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("invalid header: %v", err)
|
||||
}
|
||||
|
||||
if err := e.Vote.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("invalid signature: %v", err)
|
||||
}
|
||||
|
||||
if !e.Vote.BlockID.IsComplete() {
|
||||
return errors.New("expected vote for block")
|
||||
}
|
||||
|
||||
if e.Header.Height != e.Vote.Height {
|
||||
return fmt.Errorf("header and vote have different heights: %d vs %d",
|
||||
e.Header.Height,
|
||||
e.Vote.Height,
|
||||
)
|
||||
}
|
||||
|
||||
switch e.InvalidHeaderField {
|
||||
case "ValidatorsHash", "NextValidatorsHash", "ConsensusHash", "AppHash", "LastResultsHash":
|
||||
break
|
||||
default:
|
||||
return errors.New("unknown invalid header field")
|
||||
}
|
||||
|
||||
if !bytes.Equal(e.Header.Hash(), e.Vote.BlockID.Hash) {
|
||||
return fmt.Errorf("vote was not for header: %X != %X",
|
||||
e.Vote.BlockID.Hash,
|
||||
e.Header.Hash(),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) String() string {
|
||||
return fmt.Sprintf("LunaticValidatorEvidence{%X voted for %d/%X, which contains invalid %s}",
|
||||
e.Vote.ValidatorAddress, e.Header.Height, e.Header.Hash(), e.InvalidHeaderField)
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) VerifyHeader(committedHeader *Header) error {
|
||||
matchErr := func(field string) error {
|
||||
return fmt.Errorf("%s matches committed hash", field)
|
||||
}
|
||||
|
||||
if committedHeader == nil {
|
||||
return errors.New("committed header is nil")
|
||||
}
|
||||
|
||||
switch e.InvalidHeaderField {
|
||||
case ValidatorsHashField:
|
||||
if bytes.Equal(committedHeader.ValidatorsHash, e.Header.ValidatorsHash) {
|
||||
return matchErr(ValidatorsHashField)
|
||||
}
|
||||
case NextValidatorsHashField:
|
||||
if bytes.Equal(committedHeader.NextValidatorsHash, e.Header.NextValidatorsHash) {
|
||||
return matchErr(NextValidatorsHashField)
|
||||
}
|
||||
case ConsensusHashField:
|
||||
if bytes.Equal(committedHeader.ConsensusHash, e.Header.ConsensusHash) {
|
||||
return matchErr(ConsensusHashField)
|
||||
}
|
||||
case AppHashField:
|
||||
if bytes.Equal(committedHeader.AppHash, e.Header.AppHash) {
|
||||
return matchErr(AppHashField)
|
||||
}
|
||||
case LastResultsHashField:
|
||||
if bytes.Equal(committedHeader.LastResultsHash, e.Header.LastResultsHash) {
|
||||
return matchErr(LastResultsHashField)
|
||||
}
|
||||
default:
|
||||
return errors.New("unknown InvalidHeaderField")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LunaticValidatorEvidence) ToProto() *tmproto.LunaticValidatorEvidence {
|
||||
h := e.Header.ToProto()
|
||||
v := e.Vote.ToProto()
|
||||
|
||||
tp := &tmproto.LunaticValidatorEvidence{
|
||||
Header: h,
|
||||
Vote: v,
|
||||
InvalidHeaderField: e.InvalidHeaderField,
|
||||
Timestamp: e.Timestamp,
|
||||
}
|
||||
|
||||
return tp
|
||||
}
|
||||
|
||||
func LunaticValidatorEvidenceFromProto(pb *tmproto.LunaticValidatorEvidence) (*LunaticValidatorEvidence, error) {
|
||||
if pb == nil {
|
||||
return nil, errors.New("nil LunaticValidatorEvidence")
|
||||
}
|
||||
|
||||
h, err := HeaderFromProto(pb.GetHeader())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := VoteFromProto(pb.GetVote())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tp := LunaticValidatorEvidence{
|
||||
Header: &h,
|
||||
Vote: v,
|
||||
InvalidHeaderField: pb.InvalidHeaderField,
|
||||
Timestamp: pb.Timestamp,
|
||||
}
|
||||
|
||||
return &tp, tp.ValidateBasic()
|
||||
}
|
||||
|
||||
//-------------------------------------------
|
||||
|
||||
// PotentialAmnesiaEvidence is constructed when a validator votes on two different blocks at different rounds
|
||||
// in the same height. PotentialAmnesiaEvidence can then evolve into AmnesiaEvidence if the indicted validator
|
||||
// is incapable of providing the proof of lock change that validates voting twice in the allotted trial period.
|
||||
|
||||
@@ -105,19 +105,11 @@ func TestMaxEvidenceBytes(t *testing.T) {
|
||||
|
||||
//TODO: Add other types of evidence to test and set MaxEvidenceBytes accordingly
|
||||
|
||||
// evl := &LunaticValidatorEvidence{
|
||||
// Header: makeHeaderRandom(),
|
||||
// Vote: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2),
|
||||
|
||||
// InvalidHeaderField: "",
|
||||
// }
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
evidence Evidence
|
||||
}{
|
||||
{"DuplicateVote", ev},
|
||||
// {"LunaticValidatorEvidence", evl},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
@@ -186,69 +178,6 @@ func TestMockEvidenceValidateBasic(t *testing.T) {
|
||||
assert.Nil(t, goodEvidence.ValidateBasic())
|
||||
}
|
||||
|
||||
func TestLunaticValidatorEvidence(t *testing.T) {
|
||||
var (
|
||||
invalidBlockID = makeBlockIDRandom()
|
||||
header = makeHeaderRandom()
|
||||
altHeader = makeHeaderRandom()
|
||||
bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
|
||||
val = NewMockPV()
|
||||
)
|
||||
|
||||
header.Time = bTime
|
||||
|
||||
blockID := BlockID{
|
||||
Hash: header.Hash(),
|
||||
PartSetHeader: PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: crypto.CRandBytes(tmhash.Size),
|
||||
},
|
||||
}
|
||||
|
||||
vote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, blockID, defaultVoteTime)
|
||||
|
||||
ev := NewLunaticValidatorEvidence(header, vote, "AppHash", bTime)
|
||||
|
||||
//happy path
|
||||
assert.Equal(t, header.Height, ev.Height())
|
||||
assert.Equal(t, bTime, ev.Time())
|
||||
assert.EqualValues(t, vote.ValidatorAddress, ev.Address())
|
||||
assert.NotEmpty(t, ev.Hash())
|
||||
assert.NotEmpty(t, ev.Bytes())
|
||||
assert.True(t, ev.Equal(ev))
|
||||
pubKey, err := val.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, ev.Verify(header.ChainID, pubKey))
|
||||
assert.NoError(t, ev.ValidateBasic())
|
||||
assert.NotEmpty(t, ev.String())
|
||||
assert.NoError(t, ev.VerifyHeader(altHeader))
|
||||
|
||||
// invalid evidence
|
||||
assert.Error(t, ev.Verify("other", pubKey))
|
||||
privKey2 := ed25519.GenPrivKey()
|
||||
pubKey2 := privKey2.PubKey()
|
||||
assert.Error(t, ev.Verify(header.ChainID, pubKey2))
|
||||
assert.Error(t, ev.VerifyHeader(header))
|
||||
|
||||
invalidVote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, invalidBlockID, defaultVoteTime)
|
||||
invalidHeightVote := makeVote(t, val, header.ChainID, 0, header.Height+1, 0, 2, blockID, defaultVoteTime)
|
||||
emptyBlockVote := makeVote(t, val, header.ChainID, 0, header.Height, 0, 2, BlockID{}, defaultVoteTime)
|
||||
|
||||
invalidLunaticEvidence := []*LunaticValidatorEvidence{
|
||||
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 {
|
||||
assert.Error(t, ev.ValidateBasic(), "#%d", idx)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPotentialAmnesiaEvidence(t *testing.T) {
|
||||
const (
|
||||
chainID = "TestPotentialAmnesiaEvidence"
|
||||
@@ -577,13 +506,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},
|
||||
{"LunaticValidatorEvidence success", &LunaticValidatorEvidence{Header: header1,
|
||||
Vote: v, InvalidHeaderField: "ValidatorsHash"}, false, true},
|
||||
{"&LunaticValidatorEvidence empty fail", &LunaticValidatorEvidence{}, false, true},
|
||||
{"LunaticValidatorEvidence only header fail", &LunaticValidatorEvidence{Header: header1}, false, true},
|
||||
{"LunaticValidatorEvidence only vote fail", &LunaticValidatorEvidence{Vote: v}, false, true},
|
||||
{"LunaticValidatorEvidence header & vote fail", &LunaticValidatorEvidence{Header: header1, Vote: v}, false, true},
|
||||
{"LunaticValidatorEvidence empty fail", &LunaticValidatorEvidence{}, false, true},
|
||||
{"PotentialAmnesiaEvidence empty fail", &PotentialAmnesiaEvidence{}, false, true},
|
||||
{"PotentialAmnesiaEvidence nil VoteB", &PotentialAmnesiaEvidence{VoteA: v, VoteB: nil}, false, true},
|
||||
{"PotentialAmnesiaEvidence nil VoteA", &PotentialAmnesiaEvidence{VoteA: nil, VoteB: v2}, false, true},
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
|
||||
const (
|
||||
ABCIEvidenceTypeDuplicateVote = "duplicate/vote"
|
||||
ABCIEvidenceTypeLunatic = "lunatic"
|
||||
ABCIEvidenceTypeAmnesia = "amnesia"
|
||||
)
|
||||
|
||||
@@ -130,8 +129,6 @@ func (tm2pb) Evidence(ev Evidence, valSet *ValidatorSet) abci.Evidence {
|
||||
switch ev.(type) {
|
||||
case *DuplicateVoteEvidence:
|
||||
evType = ABCIEvidenceTypeDuplicateVote
|
||||
case *LunaticValidatorEvidence:
|
||||
evType = ABCIEvidenceTypeLunatic
|
||||
case *AmnesiaEvidence:
|
||||
evType = ABCIEvidenceTypeAmnesia
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user