From 312c4f8fe100157f237fa43f4a2834653b41c256 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Tue, 11 Aug 2020 14:39:07 +0200 Subject: [PATCH] evidence: change evidence time to block time (#5219) adds blockstore interface to evidence and adds fix to byzantine test --- .golangci.yml | 2 +- CHANGELOG_PENDING.md | 4 + abci/types/types.pb.go | 9 +- consensus/byzantine_test.go | 34 ++-- consensus/reactor_test.go | 6 +- consensus/state.go | 11 +- evidence/mocks/block_store.go | 30 +++ evidence/pool.go | 26 ++- evidence/pool_test.go | 47 +++-- evidence/reactor_test.go | 13 +- evidence/services.go | 11 ++ node/node.go | 1 - proto/tendermint/types/evidence.pb.go | 261 +++++++++++++++++++++----- proto/tendermint/types/evidence.proto | 9 + rpc/client/evidence_test.go | 12 +- state/validation.go | 49 ++--- state/validation_test.go | 97 ++++++---- types/block_test.go | 5 +- types/evidence.go | 68 ++++--- types/evidence_test.go | 35 ++-- types/vote.go | 8 +- types/vote_set.go | 2 +- 22 files changed, 527 insertions(+), 213 deletions(-) create mode 100644 evidence/mocks/block_store.go create mode 100644 evidence/services.go diff --git a/.golangci.yml b/.golangci.yml index 683181678..e7eb81b5f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -20,7 +20,7 @@ linters: - gosimple - govet - ineffassign - - interfacer + # - interfacer - lll - misspell # - maligned diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c5a010eb5..7139a7949 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,6 +18,10 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [abci] [\#5174](https://github.com/tendermint/tendermint/pull/5174) Add amnesia evidence and remove mock and potential amnesia evidence from abci (@cmwaters) +### IMPROVEMENTS: + +- [evidence] [\#5219](https://github.com/tendermint/tendermint/pull/5219) Change the source of evidence time to block time (@cmwaters) + ### BUG FIXES: - [evidence] [\#5170](https://github.com/tendermint/tendermint/pull/5170) change abci evidence time to the time the infraction happened not the time the evidence was committed on the block (@cmwaters) diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 6de0fc82b..0a7ea38c6 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -2944,10 +2944,13 @@ func (m *VoteInfo) GetSignedLastBlock() bool { } type Evidence struct { - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // The offending validator Validator Validator `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator"` - Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` - Time time.Time `protobuf:"bytes,4,opt,name=time,proto3,stdtime" json:"time"` + // The height when the offense occurred + Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + // The corresponding time where the offense occurred + Time time.Time `protobuf:"bytes,4,opt,name=time,proto3,stdtime" json:"time"` // Total voting power of the validator set in case the ABCI application does // not store historical validators. // https://github.com/tendermint/tendermint/issues/4581 diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 43b2f96bd..becb01de5 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -157,25 +157,25 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { } defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) - // Check that evidence is submitted and committed at the third height - for i := 0; i < 2; i++ { - timeoutWaitGroup(t, nValidators, func(j int) { - <-blocksSubs[j].Out() - }, css) - } - timeoutWaitGroup(t, nValidators, func(j int) { - msg := <-blocksSubs[j].Out() + // Evidence should be submitted and committed at the third height but + // we will check the first five just in case + var evidence types.Evidence + + for i := 0; i < 5; i++ { + msg := <-blocksSubs[nValidators-1].Out() block := msg.Data().(types.EventDataNewBlock).Block - // assert that we have evidence - if assert.True(t, len(block.Evidence.Evidence) == 1) { - // and that the evidence is of type DuplicateVoteEvidence - ev, ok := block.Evidence.Evidence[0].(*types.DuplicateVoteEvidence) - assert.True(t, ok) - // and that the address matches to that of the byzantine node - pubkey, _ := bcs.privValidator.GetPubKey() - assert.Equal(t, []byte(pubkey.Address()), ev.Address()) + if len(block.Evidence.Evidence) > 0 { + evidence = block.Evidence.Evidence[0] + break } - }, css) + } + + if assert.NotNil(t, evidence) { + ev, ok := evidence.(*types.DuplicateVoteEvidence) + assert.True(t, ok) + pubkey, _ := bcs.privValidator.GetPubKey() + assert.Equal(t, []byte(pubkey.Address()), ev.Address()) + } } // 4 validators. 1 is byzantine. The other three are partitioned into A (1 val) and B (2 vals). diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 32721eaa9..28562ea88 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -39,6 +39,8 @@ import ( //---------------------------------------------- // in-process testnets +var defaultTestTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) + func startConsensusNet(t *testing.T, css []*State, n int) ( []*Reactor, []types.Subscription, @@ -202,7 +204,7 @@ type mockEvidencePool struct { func newMockEvidencePool(val types.PrivValidator) *mockEvidencePool { return &mockEvidencePool{ - ev: []types.Evidence{types.NewMockDuplicateVoteEvidenceWithValidator(1, time.Now().UTC(), val, config.ChainID())}, + ev: []types.Evidence{types.NewMockDuplicateVoteEvidenceWithValidator(1, defaultTestTime, val, config.ChainID())}, } } @@ -234,7 +236,7 @@ func (m *mockEvidencePool) IsPending(evidence types.Evidence) bool { return false } func (m *mockEvidencePool) AddPOLC(*types.ProofOfLockChange) error { return nil } -func (m *mockEvidencePool) Header(int64) *types.Header { return nil } +func (m *mockEvidencePool) Header(int64) *types.Header { return &types.Header{Time: defaultTestTime} } //------------------------------------ diff --git a/consensus/state.go b/consensus/state.go index 564f9ed11..f62a3b737 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1817,7 +1817,16 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) { vote.Type) return added, err } - cs.evpool.AddEvidence(voteErr.DuplicateVoteEvidence) + var timestamp time.Time + if voteErr.VoteA.Height == 1 { + timestamp = cs.state.LastBlockTime // genesis time + } else { + timestamp = sm.MedianTime(cs.LastCommit.MakeCommit(), cs.LastValidators) + } + evidenceErr := cs.evpool.AddEvidence(types.NewDuplicateVoteEvidence(voteErr.VoteA, voteErr.VoteB, timestamp)) + if evidenceErr != nil { + cs.Logger.Error("Failed to add evidence to the evidence pool", "err", evidenceErr) + } return added, err } else if err == types.ErrVoteNonDeterministicSignature { cs.Logger.Debug("Vote has non-deterministic signature", "err", err) diff --git a/evidence/mocks/block_store.go b/evidence/mocks/block_store.go new file mode 100644 index 000000000..3c491122a --- /dev/null +++ b/evidence/mocks/block_store.go @@ -0,0 +1,30 @@ +// Code generated by mockery v2.1.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + types "github.com/tendermint/tendermint/types" +) + +// BlockStore is an autogenerated mock type for the BlockStore type +type BlockStore struct { + mock.Mock +} + +// LoadBlockMeta provides a mock function with given fields: height +func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta { + ret := _m.Called(height) + + var r0 *types.BlockMeta + if rf, ok := ret.Get(0).(func(int64) *types.BlockMeta); ok { + r0 = rf(height) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.BlockMeta) + } + } + + return r0 +} diff --git a/evidence/pool.go b/evidence/pool.go index 7f984ca16..1f6216513 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -13,7 +13,6 @@ import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" sm "github.com/tendermint/tendermint/state" - "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" ) @@ -34,7 +33,7 @@ type Pool struct { // needed to load validators to verify evidence stateDB dbm.DB // needed to load headers to verify evidence - blockStore *store.BlockStore + blockStore BlockStore mtx sync.Mutex // latest state @@ -48,7 +47,7 @@ type Pool struct { // Creates a new pool. If using an existing evidence store, it will add all pending evidence // to the concurrent list. -func NewPool(stateDB, evidenceDB dbm.DB, blockStore *store.BlockStore) (*Pool, error) { +func NewPool(stateDB, evidenceDB dbm.DB, blockStore BlockStore) (*Pool, error) { var ( state = sm.LoadState(stateDB) ) @@ -184,17 +183,30 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error { } } - // For lunatic validator evidence, a header needs to be fetched. + // A header needs to be fetched. For lunatic evidence this is so we can verify + // that some of the fields are different to the ones we have. For all evidence it + // it so we can verify that the time of the evidence is correct + var header *types.Header - if _, ok := ev.(*types.LunaticValidatorEvidence); ok { + // if the evidence is from the current height - this means the evidence is fresh from the consensus + // and we won't have it in the block store. We thus check that the time isn't before the previous block + if ev.Height() == evpool.State().LastBlockHeight+1 { + if ev.Time().Before(evpool.State().LastBlockTime) { + return fmt.Errorf("evidence is from an earlier time than the previous block: %v < %v", + ev.Time(), + evpool.State().LastBlockTime) + } + header = &types.Header{Time: ev.Time()} + } else { // if the evidence is from a prior height header = evpool.Header(ev.Height()) if header == nil { - return fmt.Errorf("don't have block meta at height #%d", ev.Height()) + return fmt.Errorf("don't have header at height #%d", ev.Height()) } } // 1) Verify against state. if err := sm.VerifyEvidence(evpool.stateDB, state, ev, header); err != nil { + evpool.logger.Debug("Inbound evidence is invalid", "evidence", ev, "err", err) return types.NewErrEvidenceInvalid(ev, err) } @@ -238,7 +250,7 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error { // 3) Add evidence to clist. evpool.evidenceList.PushBack(ev) - evpool.logger.Info("Verified new evidence of byzantine behaviour", "evidence", ev) + evpool.logger.Info("Verified new evidence of byzantine behavior", "evidence", ev) } return nil diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 09d848bcd..2ab68cf5b 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -7,11 +7,13 @@ import ( "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/evidence/mocks" "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -33,18 +35,20 @@ const evidenceChainID = "test_chain" func TestEvidencePool(t *testing.T) { var ( val = types.NewMockPV() - valAddr = val.PrivKey.PubKey().Address() height = int64(52) stateDB = initializeValidatorState(val, height) evidenceDB = dbm.NewMemDB() - blockStoreDB = dbm.NewMemDB() - blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr) + blockStore = &mocks.BlockStore{} evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, evidenceTime, val, evidenceChainID) badEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(1, evidenceTime, val, evidenceChainID) ) + blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( + &types.BlockMeta{Header: types.Header{Time: evidenceTime}}, + ) + pool, err := NewPool(stateDB, evidenceDB, blockStore) require.NoError(t, err) @@ -86,11 +90,14 @@ func TestProposingAndCommittingEvidence(t *testing.T) { height = int64(1) stateDB = initializeValidatorState(val, height) evidenceDB = dbm.NewMemDB() - blockStoreDB = dbm.NewMemDB() - blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), val.PrivKey.PubKey().Address()) + blockStore = &mocks.BlockStore{} evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) ) + blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( + &types.BlockMeta{Header: types.Header{Time: evidenceTime}}, + ) + pool, err := NewPool(stateDB, evidenceDB, blockStore) require.NoError(t, err) @@ -300,12 +307,11 @@ func TestAddingPotentialAmnesiaEvidence(t *testing.T) { }, Proposer: val.ExtractIntoValidator(1), } - height = int64(30) - stateDB = initializeStateFromValidatorSet(valSet, height) - evidenceDB = dbm.NewMemDB() - blockStoreDB = dbm.NewMemDB() - state = sm.LoadState(stateDB) - blockStore = initializeBlockStore(blockStoreDB, state, pubKey.Address()) + height = int64(30) + stateDB = initializeStateFromValidatorSet(valSet, height) + evidenceDB = dbm.NewMemDB() + state = sm.LoadState(stateDB) + blockStore = &mocks.BlockStore{} //evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) firstBlockID = types.BlockID{ Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), @@ -324,6 +330,10 @@ func TestAddingPotentialAmnesiaEvidence(t *testing.T) { evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) ) + blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( + &types.BlockMeta{Header: types.Header{Time: evidenceTime}}, + ) + // TEST SETUP pool, err := NewPool(stateDB, evidenceDB, blockStore) require.NoError(t, err) @@ -346,8 +356,9 @@ func TestAddingPotentialAmnesiaEvidence(t *testing.T) { voteC.Signature = vC.Signature require.NoError(t, err) ev := &types.PotentialAmnesiaEvidence{ - VoteA: voteA, - VoteB: voteB, + VoteA: voteA, + VoteB: voteB, + Timestamp: evidenceTime, } polc := &types.ProofOfLockChange{ @@ -410,8 +421,9 @@ func TestAddingPotentialAmnesiaEvidence(t *testing.T) { pool.logger.Info("CASE D") // evidence of voting back in the past which is instantly punishable -> amnesia evidence is made directly ev2 := &types.PotentialAmnesiaEvidence{ - VoteA: voteC, - VoteB: voteB, + VoteA: voteC, + VoteB: voteB, + Timestamp: evidenceTime, } err = pool.AddEvidence(ev2) assert.NoError(t, err) @@ -445,8 +457,9 @@ func TestAddingPotentialAmnesiaEvidence(t *testing.T) { // a new amnesia evidence is seen. It has an empty polc so we should extract the potential amnesia evidence // and start our own trial newPe := &types.PotentialAmnesiaEvidence{ - VoteA: voteB, - VoteB: voteD, + VoteA: voteB, + VoteB: voteD, + Timestamp: evidenceTime, } newAe := &types.AmnesiaEvidence{ PotentialAmnesiaEvidence: newPe, diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index d35e78294..9996f21a0 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -8,14 +8,15 @@ import ( "github.com/go-kit/kit/log/term" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/evidence/mocks" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" - sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -38,11 +39,14 @@ func makeAndConnectReactors(config *cfg.Config, stateDBs []dbm.DB) []*Reactor { reactors := make([]*Reactor, N) logger := evidenceLogger() + evidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) for i := 0; i < N; i++ { evidenceDB := dbm.NewMemDB() - blockStoreDB := dbm.NewMemDB() - blockStore := initializeBlockStore(blockStoreDB, sm.LoadState(stateDBs[i]), []byte("myval")) + blockStore := &mocks.BlockStore{} + blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( + &types.BlockMeta{Header: types.Header{Time: evidenceTime}}, + ) pool, err := NewPool(stateDBs[i], evidenceDB, blockStore) if err != nil { panic(err) @@ -115,7 +119,8 @@ func _waitForEvidence( func sendEvidence(t *testing.T, evpool *Pool, val types.PrivValidator, n int) types.EvidenceList { evList := make([]types.Evidence, n) for i := 0; i < n; i++ { - ev := types.NewMockDuplicateVoteEvidenceWithValidator(int64(i+1), time.Now().UTC(), val, evidenceChainID) + ev := types.NewMockDuplicateVoteEvidenceWithValidator(int64(i+1), + time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC), val, evidenceChainID) err := evpool.AddEvidence(ev) require.NoError(t, err) evList[i] = ev diff --git a/evidence/services.go b/evidence/services.go new file mode 100644 index 000000000..e86960653 --- /dev/null +++ b/evidence/services.go @@ -0,0 +1,11 @@ +package evidence + +import ( + "github.com/tendermint/tendermint/types" +) + +//go:generate mockery --case underscore --name BlockStore + +type BlockStore interface { + LoadBlockMeta(height int64) *types.BlockMeta +} diff --git a/node/node.go b/node/node.go index 4873920ef..de9530f35 100644 --- a/node/node.go +++ b/node/node.go @@ -345,7 +345,6 @@ func createEvidenceReactor(config *cfg.Config, dbProvider DBProvider, if err != nil { return nil, nil, err } - evidencePool.SetLogger(evidenceLogger) evidenceReactor := evidence.NewReactor(evidencePool) evidenceReactor.SetLogger(evidenceLogger) return evidenceReactor, evidencePool, nil diff --git a/proto/tendermint/types/evidence.pb.go b/proto/tendermint/types/evidence.pb.go index 0939e1fce..0beea6ac4 100644 --- a/proto/tendermint/types/evidence.pb.go +++ b/proto/tendermint/types/evidence.pb.go @@ -7,16 +7,20 @@ import ( fmt "fmt" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" + _ "github.com/gogo/protobuf/types" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" crypto "github.com/tendermint/tendermint/proto/tendermint/crypto" io "io" math "math" math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -27,8 +31,9 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // DuplicateVoteEvidence contains evidence a validator signed two conflicting // votes. type DuplicateVoteEvidence struct { - VoteA *Vote `protobuf:"bytes,1,opt,name=vote_a,json=voteA,proto3" json:"vote_a,omitempty"` - VoteB *Vote `protobuf:"bytes,2,opt,name=vote_b,json=voteB,proto3" json:"vote_b,omitempty"` + VoteA *Vote `protobuf:"bytes,1,opt,name=vote_a,json=voteA,proto3" json:"vote_a,omitempty"` + VoteB *Vote `protobuf:"bytes,2,opt,name=vote_b,json=voteB,proto3" json:"vote_b,omitempty"` + Timestamp time.Time `protobuf:"bytes,3,opt,name=timestamp,proto3,stdtime" json:"timestamp"` } func (m *DuplicateVoteEvidence) Reset() { *m = DuplicateVoteEvidence{} } @@ -78,10 +83,18 @@ func (m *DuplicateVoteEvidence) GetVoteB() *Vote { return nil } +func (m *DuplicateVoteEvidence) GetTimestamp() time.Time { + if m != nil { + return m.Timestamp + } + return time.Time{} +} + type PotentialAmnesiaEvidence struct { - VoteA *Vote `protobuf:"bytes,1,opt,name=vote_a,json=voteA,proto3" json:"vote_a,omitempty"` - VoteB *Vote `protobuf:"bytes,2,opt,name=vote_b,json=voteB,proto3" json:"vote_b,omitempty"` - HeightStamp int64 `protobuf:"varint,3,opt,name=height_stamp,json=heightStamp,proto3" json:"height_stamp,omitempty"` + VoteA *Vote `protobuf:"bytes,1,opt,name=vote_a,json=voteA,proto3" json:"vote_a,omitempty"` + VoteB *Vote `protobuf:"bytes,2,opt,name=vote_b,json=voteB,proto3" json:"vote_b,omitempty"` + HeightStamp int64 `protobuf:"varint,3,opt,name=height_stamp,json=heightStamp,proto3" json:"height_stamp,omitempty"` + Timestamp time.Time `protobuf:"bytes,4,opt,name=timestamp,proto3,stdtime" json:"timestamp"` } func (m *PotentialAmnesiaEvidence) Reset() { *m = PotentialAmnesiaEvidence{} } @@ -138,6 +151,13 @@ func (m *PotentialAmnesiaEvidence) GetHeightStamp() int64 { return 0 } +func (m *PotentialAmnesiaEvidence) GetTimestamp() time.Time { + if m != nil { + return m.Timestamp + } + return time.Time{} +} + type AmnesiaEvidence struct { PotentialAmnesiaEvidence *PotentialAmnesiaEvidence `protobuf:"bytes,1,opt,name=potential_amnesia_evidence,json=potentialAmnesiaEvidence,proto3" json:"potential_amnesia_evidence,omitempty"` Polc *ProofOfLockChange `protobuf:"bytes,2,opt,name=polc,proto3" json:"polc,omitempty"` @@ -243,9 +263,10 @@ func (m *ConflictingHeadersEvidence) GetH2() *SignedHeader { } type LunaticValidatorEvidence struct { - Header *Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Vote *Vote `protobuf:"bytes,2,opt,name=vote,proto3" json:"vote,omitempty"` - InvalidHeaderField string `protobuf:"bytes,3,opt,name=invalid_header_field,json=invalidHeaderField,proto3" json:"invalid_header_field,omitempty"` + Header *Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` + Vote *Vote `protobuf:"bytes,2,opt,name=vote,proto3" json:"vote,omitempty"` + InvalidHeaderField string `protobuf:"bytes,3,opt,name=invalid_header_field,json=invalidHeaderField,proto3" json:"invalid_header_field,omitempty"` + Timestamp time.Time `protobuf:"bytes,4,opt,name=timestamp,proto3,stdtime" json:"timestamp"` } func (m *LunaticValidatorEvidence) Reset() { *m = LunaticValidatorEvidence{} } @@ -302,6 +323,13 @@ func (m *LunaticValidatorEvidence) GetInvalidHeaderField() string { return "" } +func (m *LunaticValidatorEvidence) GetTimestamp() time.Time { + if m != nil { + return m.Timestamp + } + return time.Time{} +} + type Evidence struct { // Types that are valid to be assigned to Sum: // *Evidence_DuplicateVoteEvidence @@ -545,48 +573,52 @@ func init() { func init() { proto.RegisterFile("tendermint/types/evidence.proto", fileDescriptor_6825fabc78e0a168) } var fileDescriptor_6825fabc78e0a168 = []byte{ - // 656 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x4f, 0x4f, 0xd4, 0x40, - 0x1c, 0x6d, 0xd9, 0x65, 0xc5, 0x1f, 0x24, 0x60, 0x03, 0xda, 0x34, 0xa4, 0x40, 0x3d, 0x48, 0x08, - 0x76, 0x61, 0x8d, 0xe1, 0xe2, 0x85, 0x3f, 0x9a, 0x4d, 0x20, 0x8a, 0x43, 0xc2, 0xc1, 0x4b, 0x9d, - 0x6d, 0x67, 0xdb, 0x81, 0x6e, 0xa7, 0xd9, 0x4e, 0x37, 0x6e, 0xe2, 0x67, 0x30, 0xde, 0xfd, 0x06, - 0x9e, 0xfd, 0x10, 0x1c, 0x39, 0x7a, 0x32, 0x06, 0xbe, 0x88, 0xe9, 0x74, 0x76, 0x17, 0xb7, 0x5b, - 0x36, 0x5e, 0xbc, 0x34, 0xcd, 0xef, 0xf7, 0xe6, 0xbd, 0x37, 0x79, 0xf3, 0x9b, 0x81, 0x35, 0x4e, - 0x22, 0x8f, 0x74, 0x3b, 0x34, 0xe2, 0x75, 0xde, 0x8f, 0x49, 0x52, 0x27, 0x3d, 0xea, 0x91, 0xc8, - 0x25, 0x76, 0xdc, 0x65, 0x9c, 0x69, 0x4b, 0x23, 0x80, 0x2d, 0x00, 0xc6, 0xb2, 0xcf, 0x7c, 0x26, - 0x9a, 0xf5, 0xec, 0x2f, 0xc7, 0x19, 0xab, 0x05, 0x22, 0xf1, 0x9d, 0xd0, 0x75, 0xbb, 0xfd, 0x98, - 0xb3, 0xfa, 0x25, 0xe9, 0xcb, 0xae, 0x95, 0xc2, 0xca, 0x51, 0x1a, 0x87, 0xd4, 0xc5, 0x9c, 0x9c, - 0x33, 0x4e, 0x5e, 0x4b, 0x0b, 0xda, 0x73, 0xa8, 0xf5, 0x18, 0x27, 0x0e, 0xd6, 0xd5, 0x75, 0x75, - 0x73, 0xbe, 0xf1, 0xd8, 0x1e, 0x77, 0x63, 0x67, 0x78, 0x34, 0x9b, 0xa1, 0xf6, 0x87, 0xf0, 0x96, - 0x3e, 0x33, 0x1d, 0x7e, 0x60, 0x7d, 0x53, 0x41, 0x3f, 0x65, 0x9c, 0x44, 0x9c, 0xe2, 0x70, 0xbf, - 0x13, 0x91, 0x84, 0xe2, 0xff, 0x23, 0xad, 0x6d, 0xc0, 0x42, 0x40, 0xa8, 0x1f, 0x70, 0x27, 0xe1, - 0xb8, 0x13, 0xeb, 0x95, 0x75, 0x75, 0xb3, 0x82, 0xe6, 0xf3, 0xda, 0x59, 0x56, 0xb2, 0x7e, 0xa8, - 0xb0, 0x38, 0x6e, 0x2a, 0x00, 0x23, 0x1e, 0x18, 0x76, 0x70, 0xde, 0x74, 0x06, 0x81, 0x49, 0xa3, - 0x5b, 0x45, 0xe5, 0xb2, 0x4d, 0x22, 0x3d, 0x2e, 0xdb, 0xfe, 0x1e, 0x54, 0x63, 0x16, 0xba, 0x72, - 0x37, 0x4f, 0x27, 0x70, 0x76, 0x19, 0x6b, 0xbf, 0x6b, 0x9f, 0x30, 0xf7, 0xf2, 0x30, 0xc0, 0x91, - 0x4f, 0x90, 0x58, 0x60, 0x7d, 0x06, 0xe3, 0x90, 0x45, 0xed, 0x90, 0xba, 0x9c, 0x46, 0x7e, 0x93, - 0x60, 0x8f, 0x74, 0x93, 0x21, 0xad, 0x0d, 0x33, 0xc1, 0xae, 0x34, 0x6a, 0x16, 0x49, 0xcf, 0xa8, - 0x1f, 0x11, 0x2f, 0x5f, 0x84, 0x66, 0x82, 0x5d, 0x81, 0x6f, 0x48, 0x13, 0xd3, 0xf1, 0x0d, 0xeb, - 0xbb, 0x0a, 0xfa, 0x49, 0x1a, 0x61, 0x4e, 0xdd, 0x73, 0x1c, 0x52, 0x0f, 0x73, 0xd6, 0x1d, 0x8a, - 0xef, 0x40, 0x2d, 0x10, 0x50, 0x69, 0x40, 0x2f, 0x12, 0x4a, 0x2a, 0x89, 0xd3, 0xb6, 0xa0, 0x9a, - 0xe5, 0x35, 0x25, 0x53, 0x81, 0xd1, 0x76, 0x60, 0x99, 0x46, 0xbd, 0x4c, 0xd4, 0xc9, 0x57, 0x3b, - 0x6d, 0x4a, 0x42, 0x4f, 0x44, 0xfb, 0x10, 0x69, 0xb2, 0x97, 0x0b, 0xbc, 0xc9, 0x3a, 0xd6, 0x97, - 0x2a, 0xcc, 0x0d, 0xcd, 0x61, 0x78, 0xe2, 0x0d, 0x66, 0xc0, 0x11, 0x47, 0x69, 0x2c, 0xd7, 0x67, - 0x45, 0xf5, 0x89, 0x43, 0xd3, 0x54, 0xd0, 0x8a, 0x37, 0x71, 0x9a, 0x62, 0x58, 0x75, 0x47, 0xd1, - 0x48, 0x97, 0xc9, 0x48, 0x27, 0xdf, 0xe5, 0x76, 0x51, 0xa7, 0x3c, 0xd0, 0xa6, 0x82, 0x0c, 0xb7, - 0x3c, 0xee, 0x0b, 0x30, 0xc2, 0x3c, 0x0d, 0xa7, 0x37, 0x88, 0x63, 0xa4, 0x57, 0x29, 0x3b, 0xaf, - 0x65, 0x09, 0x36, 0x15, 0xa4, 0x87, 0x65, 0xe9, 0x5e, 0xdc, 0x3b, 0x1b, 0xd5, 0x7f, 0x9d, 0x8d, - 0x4c, 0xab, 0x74, 0x3a, 0xde, 0xc2, 0x52, 0x41, 0x61, 0x56, 0x28, 0x6c, 0x14, 0x15, 0x8a, 0xc4, - 0x8b, 0xf8, 0xef, 0xd2, 0xc1, 0x2c, 0x54, 0x92, 0xb4, 0x63, 0x7d, 0x84, 0x85, 0x41, 0xe9, 0x08, - 0x73, 0xac, 0xbd, 0x82, 0xb9, 0x3b, 0x87, 0xa0, 0xb2, 0x39, 0xdf, 0x30, 0x8a, 0xf4, 0x43, 0x92, - 0xea, 0xd5, 0xaf, 0x35, 0x05, 0x0d, 0x57, 0x68, 0x1a, 0x54, 0x03, 0x9c, 0x04, 0x22, 0xd6, 0x05, - 0x24, 0xfe, 0xad, 0x4f, 0xf0, 0xa8, 0x30, 0xb8, 0xda, 0x36, 0x88, 0x5b, 0x29, 0x91, 0x1a, 0xf7, - 0x5e, 0x5d, 0x89, 0xf6, 0x12, 0x1e, 0xc4, 0x69, 0xcb, 0xb9, 0x24, 0x7d, 0x79, 0x60, 0x56, 0xef, - 0xe2, 0xf3, 0xcb, 0xdd, 0x3e, 0x4d, 0x5b, 0x21, 0x75, 0x8f, 0x49, 0x1f, 0xd5, 0xe2, 0xb4, 0x75, - 0x4c, 0xfa, 0x07, 0xef, 0xaf, 0x6e, 0x4c, 0xf5, 0xfa, 0xc6, 0x54, 0x7f, 0xdf, 0x98, 0xea, 0xd7, - 0x5b, 0x53, 0xb9, 0xbe, 0x35, 0x95, 0x9f, 0xb7, 0xa6, 0xf2, 0x61, 0xcf, 0xa7, 0x3c, 0x48, 0x5b, - 0xb6, 0xcb, 0x3a, 0xf5, 0xbb, 0x8f, 0xc8, 0xe8, 0x37, 0x7f, 0x6c, 0xc6, 0x1f, 0x98, 0x56, 0x4d, - 0xd4, 0x5f, 0xfc, 0x09, 0x00, 0x00, 0xff, 0xff, 0xb8, 0xe6, 0x16, 0x08, 0xc4, 0x06, 0x00, 0x00, + // 710 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xc1, 0x4e, 0xdb, 0x4a, + 0x14, 0xb5, 0x89, 0xc9, 0x83, 0x01, 0x09, 0x9e, 0x05, 0xef, 0x59, 0x16, 0x72, 0x4a, 0xba, 0x68, + 0x85, 0xa8, 0x0d, 0x54, 0x15, 0x9b, 0x6e, 0x08, 0xb4, 0x8a, 0x04, 0x6a, 0xa9, 0xa9, 0x58, 0x74, + 0xe3, 0x8e, 0xed, 0x89, 0x3d, 0xe0, 0x78, 0xac, 0x78, 0x1c, 0x35, 0x52, 0xbf, 0xa1, 0xe2, 0x63, + 0xba, 0xe9, 0x1f, 0xb0, 0x64, 0xd9, 0x55, 0xa9, 0x60, 0xdf, 0x6f, 0xa8, 0x3c, 0x1e, 0xdb, 0x10, + 0xc7, 0xa0, 0x56, 0x55, 0x37, 0x91, 0x33, 0xf7, 0xdc, 0x7b, 0xee, 0x99, 0x39, 0x77, 0x06, 0xb4, + 0x28, 0x0a, 0x5d, 0x34, 0xe8, 0xe3, 0x90, 0x1a, 0x74, 0x14, 0xa1, 0xd8, 0x40, 0x43, 0xec, 0xa2, + 0xd0, 0x41, 0x7a, 0x34, 0x20, 0x94, 0xc8, 0x8b, 0x25, 0x40, 0x67, 0x00, 0x75, 0xc9, 0x23, 0x1e, + 0x61, 0x41, 0x23, 0xfd, 0xca, 0x70, 0x6a, 0xcb, 0x23, 0xc4, 0x0b, 0x90, 0xc1, 0xfe, 0xd9, 0x49, + 0xcf, 0xa0, 0xb8, 0x8f, 0x62, 0x0a, 0xfb, 0x11, 0x07, 0xac, 0x54, 0x98, 0xd8, 0xef, 0x84, 0xa8, + 0x33, 0x18, 0x45, 0x94, 0x18, 0xa7, 0x68, 0xc4, 0xa3, 0xed, 0x2f, 0x22, 0x58, 0xde, 0x4b, 0xa2, + 0x00, 0x3b, 0x90, 0xa2, 0x63, 0x42, 0xd1, 0x0b, 0xde, 0xa4, 0xfc, 0x04, 0x34, 0x87, 0x84, 0x22, + 0x0b, 0x2a, 0xe2, 0x03, 0xf1, 0xf1, 0xdc, 0xd6, 0x7f, 0xfa, 0x78, 0xbf, 0x7a, 0x8a, 0x37, 0xa7, + 0x53, 0xd4, 0x4e, 0x01, 0xb7, 0x95, 0xa9, 0xfb, 0xe1, 0x1d, 0xb9, 0x03, 0x66, 0x0b, 0x19, 0x4a, + 0x83, 0x65, 0xa8, 0x7a, 0x26, 0x54, 0xcf, 0x85, 0xea, 0x6f, 0x73, 0x44, 0x67, 0xe6, 0xfc, 0x5b, + 0x4b, 0x38, 0xbb, 0x6c, 0x89, 0x66, 0x99, 0xd6, 0xbe, 0x14, 0x81, 0x72, 0x48, 0x28, 0x0a, 0x29, + 0x86, 0xc1, 0x4e, 0x3f, 0x44, 0x31, 0x86, 0x7f, 0xa9, 0xfd, 0x55, 0x30, 0xef, 0x23, 0xec, 0xf9, + 0xd4, 0x2a, 0x15, 0x34, 0xcc, 0xb9, 0x6c, 0xed, 0x28, 0x5d, 0xba, 0xad, 0x50, 0xfa, 0x3d, 0x85, + 0x9f, 0x45, 0xb0, 0x30, 0x2e, 0xcc, 0x07, 0x6a, 0x94, 0x8b, 0xb6, 0x60, 0x16, 0xb4, 0x72, 0x6b, + 0x71, 0xb1, 0x6b, 0xd5, 0xee, 0xeb, 0x36, 0xca, 0x54, 0xa2, 0xba, 0x2d, 0xdc, 0x06, 0x52, 0x44, + 0x02, 0x87, 0xef, 0xc8, 0xc3, 0x09, 0x35, 0x07, 0x84, 0xf4, 0x5e, 0xf7, 0x0e, 0x88, 0x73, 0xba, + 0xeb, 0xc3, 0xd0, 0x43, 0x26, 0x4b, 0x68, 0x7f, 0x04, 0xea, 0x2e, 0x09, 0x7b, 0x01, 0x76, 0x28, + 0x0e, 0xbd, 0x2e, 0x82, 0x2e, 0x1a, 0xc4, 0x45, 0x59, 0x1d, 0x4c, 0xf9, 0x9b, 0xbc, 0x51, 0xad, + 0x5a, 0xf4, 0x08, 0x7b, 0x21, 0x72, 0xb3, 0x24, 0x73, 0xca, 0xdf, 0x64, 0xf8, 0x2d, 0xde, 0xc4, + 0xfd, 0xf8, 0xad, 0xf6, 0x0f, 0x11, 0x28, 0x07, 0x49, 0x08, 0x29, 0x76, 0x8e, 0x61, 0x80, 0x5d, + 0x48, 0xc9, 0xa0, 0x20, 0xdf, 0x00, 0x4d, 0x9f, 0x41, 0x79, 0x03, 0x4a, 0xb5, 0x20, 0x2f, 0xc5, + 0x71, 0xf2, 0x1a, 0x90, 0xd2, 0x33, 0xbf, 0xc7, 0x17, 0x0c, 0x23, 0x6f, 0x80, 0x25, 0x1c, 0x0e, + 0x53, 0x52, 0x2b, 0xcb, 0xb6, 0x7a, 0x18, 0x05, 0x2e, 0xb3, 0xc7, 0xac, 0x29, 0xf3, 0x58, 0x46, + 0xf0, 0x32, 0x8d, 0xfc, 0x11, 0x97, 0x7c, 0x92, 0xc0, 0x4c, 0x21, 0x10, 0x82, 0xff, 0xdd, 0x7c, + 0x9e, 0x2d, 0x66, 0xe9, 0x31, 0x6f, 0x3c, 0xaa, 0x2a, 0x98, 0x78, 0x01, 0x74, 0x05, 0x73, 0xd9, + 0x9d, 0x78, 0x33, 0x44, 0x60, 0xc5, 0x29, 0x8f, 0x97, 0x2b, 0x8d, 0x4b, 0x9e, 0x6c, 0xa7, 0xd6, + 0xab, 0x3c, 0xf5, 0xa6, 0xe8, 0x0a, 0xa6, 0xea, 0xd4, 0x5b, 0xe6, 0x04, 0xa8, 0x41, 0x76, 0xa2, + 0xd6, 0x30, 0x3f, 0xd2, 0x92, 0xaf, 0x51, 0xe7, 0xf9, 0x3a, 0x17, 0x74, 0x05, 0x53, 0x09, 0xea, + 0x1c, 0x72, 0x72, 0xe7, 0x7c, 0x49, 0xbf, 0x3a, 0x5f, 0x29, 0x57, 0xed, 0x84, 0xbd, 0x02, 0x8b, + 0x15, 0x86, 0x69, 0xc6, 0xb0, 0x5a, 0x65, 0xa8, 0x16, 0x5e, 0x80, 0xb7, 0x97, 0x3a, 0xd3, 0xa0, + 0x11, 0x27, 0xfd, 0xf6, 0x7b, 0x30, 0x9f, 0x2f, 0xed, 0x41, 0x0a, 0xe5, 0xe7, 0x60, 0xe6, 0x86, + 0x09, 0x1a, 0xcc, 0x63, 0x95, 0xf2, 0x45, 0x11, 0x29, 0xf5, 0x98, 0x59, 0x64, 0xc8, 0x32, 0x90, + 0x7c, 0x18, 0xfb, 0xec, 0x58, 0xe7, 0x4d, 0xf6, 0xdd, 0xfe, 0x00, 0xfe, 0xad, 0x0c, 0xbf, 0xbc, + 0x0e, 0xd8, 0xed, 0x18, 0x73, 0x8e, 0x3b, 0xaf, 0xd0, 0x58, 0x7e, 0x06, 0xfe, 0x89, 0x12, 0xdb, + 0x3a, 0x45, 0x23, 0x6e, 0x98, 0x95, 0x9b, 0xf8, 0xec, 0xa5, 0xd2, 0x0f, 0x13, 0x3b, 0xc0, 0xce, + 0x3e, 0x1a, 0x99, 0xcd, 0x28, 0xb1, 0xf7, 0xd1, 0xa8, 0xf3, 0xe6, 0xfc, 0x4a, 0x13, 0x2f, 0xae, + 0x34, 0xf1, 0xfb, 0x95, 0x26, 0x9e, 0x5d, 0x6b, 0xc2, 0xc5, 0xb5, 0x26, 0x7c, 0xbd, 0xd6, 0x84, + 0x77, 0xdb, 0x1e, 0xa6, 0x7e, 0x62, 0xeb, 0x0e, 0xe9, 0x1b, 0x37, 0x5f, 0xc4, 0xf2, 0x33, 0x7b, + 0x5a, 0xc7, 0x5f, 0x4b, 0xbb, 0xc9, 0xd6, 0x9f, 0xfe, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x84, 0x99, + 0x61, 0x70, 0xb2, 0x07, 0x00, 0x00, } func (m *DuplicateVoteEvidence) Marshal() (dAtA []byte, err error) { @@ -609,6 +641,14 @@ func (m *DuplicateVoteEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintEvidence(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x1a if m.VoteB != nil { { size, err := m.VoteB.MarshalToSizedBuffer(dAtA[:i]) @@ -656,6 +696,14 @@ func (m *PotentialAmnesiaEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintEvidence(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x22 if m.HeightStamp != 0 { i = encodeVarintEvidence(dAtA, i, uint64(m.HeightStamp)) i-- @@ -802,6 +850,14 @@ func (m *LunaticValidatorEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err11 != nil { + return 0, err11 + } + i -= n11 + i = encodeVarintEvidence(dAtA, i, uint64(n11)) + i-- + dAtA[i] = 0x22 if len(m.InvalidHeaderField) > 0 { i -= len(m.InvalidHeaderField) copy(dAtA[i:], m.InvalidHeaderField) @@ -1091,6 +1147,8 @@ func (m *DuplicateVoteEvidence) Size() (n int) { l = m.VoteB.Size() n += 1 + l + sovEvidence(uint64(l)) } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovEvidence(uint64(l)) return n } @@ -1111,6 +1169,8 @@ func (m *PotentialAmnesiaEvidence) Size() (n int) { if m.HeightStamp != 0 { n += 1 + sovEvidence(uint64(m.HeightStamp)) } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovEvidence(uint64(l)) return n } @@ -1166,6 +1226,8 @@ func (m *LunaticValidatorEvidence) Size() (n int) { if l > 0 { n += 1 + l + sovEvidence(uint64(l)) } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovEvidence(uint64(l)) return n } @@ -1386,6 +1448,39 @@ func (m *DuplicateVoteEvidence) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvidence + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvidence + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvidence + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvidence(dAtA[iNdEx:]) @@ -1530,6 +1625,39 @@ func (m *PotentialAmnesiaEvidence) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvidence + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvidence + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvidence + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvidence(dAtA[iNdEx:]) @@ -1937,6 +2065,39 @@ func (m *LunaticValidatorEvidence) Unmarshal(dAtA []byte) error { } m.InvalidHeaderField = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvidence + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvidence + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvidence + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvidence(dAtA[iNdEx:]) diff --git a/proto/tendermint/types/evidence.proto b/proto/tendermint/types/evidence.proto index 31744a938..c2bb74ca4 100644 --- a/proto/tendermint/types/evidence.proto +++ b/proto/tendermint/types/evidence.proto @@ -4,6 +4,7 @@ package tendermint.types; option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; import "tendermint/types/types.proto"; import "tendermint/crypto/keys.proto"; @@ -12,6 +13,9 @@ import "tendermint/crypto/keys.proto"; message DuplicateVoteEvidence { Vote vote_a = 1; Vote vote_b = 2; + + google.protobuf.Timestamp timestamp = 3 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; } message PotentialAmnesiaEvidence { @@ -19,6 +23,8 @@ message PotentialAmnesiaEvidence { Vote vote_b = 2; int64 height_stamp = 3; + google.protobuf.Timestamp timestamp = 4 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; } message AmnesiaEvidence { @@ -35,6 +41,9 @@ message LunaticValidatorEvidence { Header header = 1; Vote vote = 2; string invalid_header_field = 3; + + google.protobuf.Timestamp timestamp = 4 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; } message Evidence { diff --git a/rpc/client/evidence_test.go b/rpc/client/evidence_test.go index a4ce2176b..170f087ce 100644 --- a/rpc/client/evidence_test.go +++ b/rpc/client/evidence_test.go @@ -20,6 +20,12 @@ import ( "github.com/tendermint/tendermint/types" ) +// For some reason the empty node used in tests has a time of +// 2018-10-10 08:20:13.695936996 +0000 UTC +// this is because the test genesis time is set here +// so in order to validate evidence we need evidence to be the same time +var defaultTestTime = time.Date(2018, 10, 10, 8, 20, 13, 695936996, time.UTC) + func newEvidence(t *testing.T, val *privval.FilePV, vote *types.Vote, vote2 *types.Vote, chainID string) *types.DuplicateVoteEvidence { @@ -35,7 +41,7 @@ func newEvidence(t *testing.T, val *privval.FilePV, vote2.Signature, err = val.Key.PrivKey.Sign(types.VoteSignBytes(chainID, v2)) require.NoError(t, err) - return types.NewDuplicateVoteEvidence(vote, vote2) + return types.NewDuplicateVoteEvidence(vote, vote2, defaultTestTime) } func makeEvidences( @@ -49,7 +55,7 @@ func makeEvidences( Height: 1, Round: 0, Type: tmproto.PrevoteType, - Timestamp: time.Now().UTC(), + Timestamp: defaultTestTime, BlockID: types.BlockID{ Hash: tmhash.Sum([]byte("blockhash")), PartSetHeader: types.PartSetHeader{ @@ -121,6 +127,8 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) { for i, c := range GetClients() { t.Logf("client %d", i) + t.Log(correct.Time()) + result, err := c.BroadcastEvidence(correct) require.NoError(t, err, "BroadcastEvidence(%s) failed", correct) assert.Equal(t, correct.Hash(), result.Hash, "expected result hash to match evidence hash") diff --git a/state/validation.go b/state/validation.go index 6faa275d9..1a7230dc4 100644 --- a/state/validation.go +++ b/state/validation.go @@ -94,6 +94,21 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block } } + // NOTE: We can't actually verify it's the right proposer because we dont + // know what round the block was first proposed. So just check that it's + // a legit address and a known validator. + if len(block.ProposerAddress) != crypto.AddressSize { + return fmt.Errorf("expected ProposerAddress size %d, got %d", + crypto.AddressSize, + len(block.ProposerAddress), + ) + } + if !state.Validators.HasAddress(block.ProposerAddress) { + return fmt.Errorf("block.Header.ProposerAddress %X is not a validator", + block.ProposerAddress, + ) + } + // Validate block Time if block.Height > 1 { if !block.Time.After(state.LastBlockTime) { @@ -154,12 +169,12 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block return types.NewErrEvidenceInvalid(ev, errors.New("amnesia evidence is new and hasn't undergone trial period yet")) } - var header *types.Header - if _, ok := ev.(*types.LunaticValidatorEvidence); ok { - header = evidencePool.Header(ev.Height()) - if header == nil { - return fmt.Errorf("don't have block meta at height #%d", ev.Height()) - } + // A header needs to be fetched. For lunatic evidence this is so we can verify + // that some of the fields are different to the ones we have. For all evidence it + // it so we can verify that the time of the evidence is correct + header := evidencePool.Header(ev.Height()) + if header == nil { + return fmt.Errorf("don't have block meta at height #%d", ev.Height()) } if err := VerifyEvidence(stateDB, state, ev, header); err != nil { @@ -168,21 +183,6 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block } - // NOTE: We can't actually verify it's the right proposer because we dont - // know what round the block was first proposed. So just check that it's - // a legit address and a known validator. - if len(block.ProposerAddress) != crypto.AddressSize { - return fmt.Errorf("expected ProposerAddress size %d, got %d", - crypto.AddressSize, - len(block.ProposerAddress), - ) - } - if !state.Validators.HasAddress(block.ProposerAddress) { - return fmt.Errorf("block.Header.ProposerAddress %X is not a validator", - block.ProposerAddress, - ) - } - return nil } @@ -200,6 +200,13 @@ func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence, commit ageNumBlocks = height - evidence.Height() ) + if committedHeader.Time != evidence.Time() { + return fmt.Errorf("evidence time (%v) is different to the time of the header we have for the same height (%v)", + evidence.Time(), + committedHeader.Time, + ) + } + if ageDuration > evidenceParams.MaxAgeDuration && ageNumBlocks > evidenceParams.MaxAgeNumBlocks { return fmt.Errorf( "evidence from height %d (created at: %v) is too old; min height is %d and evidence can not be older than %v", diff --git a/state/validation_test.go b/state/validation_test.go index a6e4c3ee4..162f65888 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" @@ -216,20 +217,32 @@ func TestValidateBlockEvidence(t *testing.T) { defer proxyApp.Stop() //nolint:errcheck // ignore for tests state, stateDB, privVals := makeState(4, 1) + defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) + + evpool := &mocks.EvidencePool{} + evpool.On("IsPending", mock.AnythingOfType("*types.DuplicateVoteEvidence")).Return(false) + evpool.On("IsCommitted", mock.AnythingOfType("*types.DuplicateVoteEvidence")).Return(false) + evpool.On("Header", mock.AnythingOfType("int64")).Return(func(height int64) *types.Header { + return &types.Header{ + Time: defaultEvidenceTime, + Height: height, + } + }) + evpool.On("Update", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("state.State")).Return() + state.ConsensusParams.Evidence.MaxNum = 3 blockExec := sm.NewBlockExecutor( stateDB, log.TestingLogger(), proxyApp.Consensus(), memmock.Mempool{}, - sm.MockEvidencePool{}, + evpool, ) lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) for height := int64(1); height < validationTestsStopHeight; height++ { proposerAddr := state.Validators.GetProposer().Address maxNumEvidence := state.ConsensusParams.Evidence.MaxNum - t.Log(maxNumEvidence) if height > 1 { /* A block with too much evidence fails @@ -243,8 +256,10 @@ func TestValidateBlockEvidence(t *testing.T) { } block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr) err := blockExec.ValidateBlock(state, block) - _, ok := err.(*types.ErrEvidenceOverflow) - require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d", height) + if assert.Error(t, err) { + _, ok := err.(*types.ErrEvidenceOverflow) + require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d but got %v", height, err) + } } /* @@ -256,7 +271,7 @@ func TestValidateBlockEvidence(t *testing.T) { for i := int32(0); uint32(i) < maxNumEvidence; i++ { // make different evidence for each validator _, val := state.Validators.GetByIndex(i) - evidence = append(evidence, types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), + evidence = append(evidence, types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, privVals[val.Address.String()], chainID)) } @@ -284,11 +299,14 @@ func TestValidateFailBlockOnCommittedEvidence(t *testing.T) { ev2 := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultTestTime, privVals[val2.Address.String()], chainID) + header := &types.Header{Time: defaultTestTime} + evpool := &mocks.EvidencePool{} evpool.On("IsPending", ev).Return(false) evpool.On("IsPending", ev2).Return(false) evpool.On("IsCommitted", ev).Return(false) evpool.On("IsCommitted", ev2).Return(true) + evpool.On("Header", height).Return(header) blockExec := sm.NewBlockExecutor( stateDB, log.TestingLogger(), @@ -314,12 +332,14 @@ func TestValidateAlreadyPendingEvidence(t *testing.T) { privVals[val.Address.String()], chainID) ev2 := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultTestTime, privVals[val2.Address.String()], chainID) + header := &types.Header{Time: defaultTestTime} evpool := &mocks.EvidencePool{} evpool.On("IsPending", ev).Return(false) evpool.On("IsPending", ev2).Return(true) evpool.On("IsCommitted", ev).Return(false) evpool.On("IsCommitted", ev2).Return(false) + evpool.On("Header", height).Return(header) blockExec := sm.NewBlockExecutor( stateDB, log.TestingLogger(), @@ -430,30 +450,30 @@ func TestValidatePrimedAmnesiaEvidence(t *testing.T) { state, stateDB, vals := makeState(1, int(height)) addr, val := state.Validators.GetByIndex(0) voteA := makeVote(height, 1, 0, addr, blockID) - voteA.Timestamp = time.Now().Add(1 * time.Minute) + voteA.Timestamp = defaultTestTime.Add(1 * time.Minute) vA := voteA.ToProto() err := vals[val.Address.String()].SignVote(chainID, vA) require.NoError(t, err) voteA.Signature = vA.Signature voteB := makeVote(height, 2, 0, addr, differentBlockID) + voteB.Timestamp = defaultTestTime vB := voteB.ToProto() err = vals[val.Address.String()].SignVote(chainID, vB) voteB.Signature = vB.Signature require.NoError(t, err) - pe := &types.PotentialAmnesiaEvidence{ - VoteA: voteB, - VoteB: voteA, - } + pe := types.NewPotentialAmnesiaEvidence(voteB, voteA, defaultTestTime) ae := &types.AmnesiaEvidence{ PotentialAmnesiaEvidence: pe, Polc: types.NewEmptyPOLC(), } + header := &types.Header{Time: defaultTestTime} evpool := &mocks.EvidencePool{} evpool.On("IsPending", ae).Return(false) evpool.On("IsCommitted", ae).Return(false) evpool.On("AddEvidence", ae).Return(nil) evpool.On("AddEvidence", pe).Return(nil) + evpool.On("Header", height).Return(header) blockExec := sm.NewBlockExecutor( stateDB, log.TestingLogger(), @@ -475,11 +495,19 @@ func TestVerifyEvidenceWrongAddress(t *testing.T) { state, stateDB, _ := makeState(1, int(height)) ev := types.NewMockDuplicateVoteEvidence(height, defaultTestTime, chainID) + header := &types.Header{Time: defaultTestTime} + + evpool := &mocks.EvidencePool{} + evpool.On("IsPending", ev).Return(false) + evpool.On("IsCommitted", ev).Return(false) + evpool.On("Header", height).Return(header) + blockExec := sm.NewBlockExecutor( stateDB, log.TestingLogger(), nil, nil, - sm.MockEvidencePool{}) + evpool, + ) // A block with a couple pieces of evidence passes. block := makeBlock(state, height) block.Evidence.Evidence = []types.Evidence{ev} @@ -496,13 +524,26 @@ func TestVerifyEvidenceExpiredEvidence(t *testing.T) { state, stateDB, _ := makeState(1, int(height)) state.ConsensusParams.Evidence.MaxAgeNumBlocks = 1 ev := types.NewMockDuplicateVoteEvidence(1, defaultTestTime, chainID) - err := sm.VerifyEvidence(stateDB, state, ev, nil) + err := sm.VerifyEvidence(stateDB, state, ev, &types.Header{Time: defaultTestTime}) errMsg := "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old" if assert.Error(t, err) { assert.Equal(t, err.Error()[:len(errMsg)], errMsg) } } +func TestVerifyEvidenceInvalidTime(t *testing.T) { + height := 4 + state, stateDB, _ := makeState(1, height) + differentTime := time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC) + ev := types.NewMockDuplicateVoteEvidence(int64(height), differentTime, chainID) + err := sm.VerifyEvidence(stateDB, state, ev, &types.Header{Time: defaultTestTime}) + errMsg := "evidence time (2019-02-01 00:00:00 +0000 UTC) is different to the time" + + " of the header we have for the same height (2019-01-01 00:00:00 +0000 UTC)" + if assert.Error(t, err) { + assert.Equal(t, errMsg, err.Error()) + } +} + func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) { var height int64 = 1 state, stateDB, vals := makeState(4, int(height)) @@ -518,6 +559,9 @@ func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) { err = vals[val.Address.String()].SignVote(chainID, vB) voteB.Signature = vB.Signature require.NoError(t, err) + + pae := types.NewPotentialAmnesiaEvidence(voteA, voteB, defaultTestTime) + voteC := makeVote(height, 2, 1, addr2, blockID) vC := voteC.ToProto() err = vals[val2.Address.String()].SignVote(chainID, vC) @@ -525,16 +569,13 @@ func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) { require.NoError(t, err) //var ae types.Evidence badAe := &types.AmnesiaEvidence{ - PotentialAmnesiaEvidence: &types.PotentialAmnesiaEvidence{ - VoteA: voteA, - VoteB: voteB, - }, + PotentialAmnesiaEvidence: pae, Polc: &types.ProofOfLockChange{ Votes: []*types.Vote{voteC}, PubKey: val.PubKey, }, } - err = sm.VerifyEvidence(stateDB, state, badAe, nil) + err = sm.VerifyEvidence(stateDB, state, badAe, &types.Header{Time: defaultTestTime}) if assert.Error(t, err) { assert.Equal(t, err.Error(), "amnesia evidence contains invalid polc, err: "+ "invalid commit -- insufficient voting power: got 1000, needed more than 2667") @@ -553,26 +594,20 @@ func TestVerifyEvidenceWithAmnesiaEvidence(t *testing.T) { require.NoError(t, err) goodAe := &types.AmnesiaEvidence{ - PotentialAmnesiaEvidence: &types.PotentialAmnesiaEvidence{ - VoteA: voteA, - VoteB: voteB, - }, + PotentialAmnesiaEvidence: pae, Polc: &types.ProofOfLockChange{ Votes: []*types.Vote{voteC, voteD, voteE}, PubKey: val.PubKey, }, } - err = sm.VerifyEvidence(stateDB, state, goodAe, nil) + err = sm.VerifyEvidence(stateDB, state, goodAe, &types.Header{Time: defaultTestTime}) assert.NoError(t, err) goodAe = &types.AmnesiaEvidence{ - PotentialAmnesiaEvidence: &types.PotentialAmnesiaEvidence{ - VoteA: voteA, - VoteB: voteB, - }, - Polc: types.NewEmptyPOLC(), + PotentialAmnesiaEvidence: pae, + Polc: types.NewEmptyPOLC(), } - err = sm.VerifyEvidence(stateDB, state, goodAe, nil) + err = sm.VerifyEvidence(stateDB, state, goodAe, &types.Header{Time: defaultTestTime}) assert.NoError(t, err) } @@ -608,11 +643,7 @@ func TestVerifyEvidenceWithLunaticValidatorEvidence(t *testing.T) { err := vals[val.Address.String()].SignVote(chainID, v) vote.Signature = v.Signature require.NoError(t, err) - ev := &types.LunaticValidatorEvidence{ - Header: h, - Vote: vote, - InvalidHeaderField: "ConsensusHash", - } + ev := types.NewLunaticValidatorEvidence(h, vote, "ConsensusHash", defaultTestTime) err = ev.ValidateBasic() require.NoError(t, err) err = sm.VerifyEvidence(stateDB, state, ev, h) diff --git a/types/block_test.go b/types/block_test.go index 7accf5b66..f6c5a03b8 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -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 { diff --git a/types/evidence.go b/types/evidence.go index b65ac9311..d5e9c042a 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -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, diff --git a/types/evidence_test.go b/types/evidence_test.go index a6e16bc5f..fb6c6f196 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -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{ diff --git a/types/vote.go b/types/vote.go index 355e8131b..0f8ea9a18 100644 --- a/types/vote.go +++ b/types/vote.go @@ -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, } } diff --git a/types/vote_set.go b/types/vote_set.go index bf72b18cb..42bde9856 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -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")