github.com/DFWallet/tendermint-cosmos@v0.0.2/evidence/pool_test.go (about) 1 package evidence_test 2 3 import ( 4 "os" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/mock" 10 "github.com/stretchr/testify/require" 11 12 dbm "github.com/tendermint/tm-db" 13 14 "github.com/DFWallet/tendermint-cosmos/evidence" 15 "github.com/DFWallet/tendermint-cosmos/evidence/mocks" 16 "github.com/DFWallet/tendermint-cosmos/libs/log" 17 tmproto "github.com/DFWallet/tendermint-cosmos/proto/tendermint/types" 18 tmversion "github.com/DFWallet/tendermint-cosmos/proto/tendermint/version" 19 sm "github.com/DFWallet/tendermint-cosmos/state" 20 smmocks "github.com/DFWallet/tendermint-cosmos/state/mocks" 21 "github.com/DFWallet/tendermint-cosmos/store" 22 "github.com/DFWallet/tendermint-cosmos/types" 23 "github.com/DFWallet/tendermint-cosmos/version" 24 ) 25 26 func TestMain(m *testing.M) { 27 28 code := m.Run() 29 os.Exit(code) 30 } 31 32 const evidenceChainID = "test_chain" 33 34 var ( 35 defaultEvidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 36 defaultEvidenceMaxBytes int64 = 1000 37 ) 38 39 func TestEvidencePoolBasic(t *testing.T) { 40 var ( 41 height = int64(1) 42 stateStore = &smmocks.Store{} 43 evidenceDB = dbm.NewMemDB() 44 blockStore = &mocks.BlockStore{} 45 ) 46 47 valSet, privVals := types.RandValidatorSet(1, 10) 48 49 blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( 50 &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}}, 51 ) 52 stateStore.On("LoadValidators", mock.AnythingOfType("int64")).Return(valSet, nil) 53 stateStore.On("Load").Return(createState(height+1, valSet), nil) 54 55 pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) 56 require.NoError(t, err) 57 pool.SetLogger(log.TestingLogger()) 58 59 // evidence not seen yet: 60 evs, size := pool.PendingEvidence(defaultEvidenceMaxBytes) 61 assert.Equal(t, 0, len(evs)) 62 assert.Zero(t, size) 63 64 ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, privVals[0], evidenceChainID) 65 66 // good evidence 67 evAdded := make(chan struct{}) 68 go func() { 69 <-pool.EvidenceWaitChan() 70 close(evAdded) 71 }() 72 73 // evidence seen but not yet committed: 74 assert.NoError(t, pool.AddEvidence(ev)) 75 76 select { 77 case <-evAdded: 78 case <-time.After(5 * time.Second): 79 t.Fatal("evidence was not added to list after 5s") 80 } 81 82 next := pool.EvidenceFront() 83 assert.Equal(t, ev, next.Value.(types.Evidence)) 84 85 const evidenceBytes int64 = 372 86 evs, size = pool.PendingEvidence(evidenceBytes) 87 assert.Equal(t, 1, len(evs)) 88 assert.Equal(t, evidenceBytes, size) // check that the size of the single evidence in bytes is correct 89 90 // shouldn't be able to add evidence twice 91 assert.NoError(t, pool.AddEvidence(ev)) 92 evs, _ = pool.PendingEvidence(defaultEvidenceMaxBytes) 93 assert.Equal(t, 1, len(evs)) 94 95 } 96 97 // Tests inbound evidence for the right time and height 98 func TestAddExpiredEvidence(t *testing.T) { 99 var ( 100 val = types.NewMockPV() 101 height = int64(30) 102 stateStore = initializeValidatorState(val, height) 103 evidenceDB = dbm.NewMemDB() 104 blockStore = &mocks.BlockStore{} 105 expiredEvidenceTime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC) 106 expiredHeight = int64(2) 107 ) 108 109 blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(func(h int64) *types.BlockMeta { 110 if h == height || h == expiredHeight { 111 return &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}} 112 } 113 return &types.BlockMeta{Header: types.Header{Time: expiredEvidenceTime}} 114 }) 115 116 pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) 117 require.NoError(t, err) 118 119 testCases := []struct { 120 evHeight int64 121 evTime time.Time 122 expErr bool 123 evDescription string 124 }{ 125 {height, defaultEvidenceTime, false, "valid evidence"}, 126 {expiredHeight, defaultEvidenceTime, false, "valid evidence (despite old height)"}, 127 {height - 1, expiredEvidenceTime, false, "valid evidence (despite old time)"}, 128 {expiredHeight - 1, expiredEvidenceTime, true, 129 "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"}, 130 {height, defaultEvidenceTime.Add(1 * time.Minute), true, "evidence time and block time is different"}, 131 } 132 133 for _, tc := range testCases { 134 tc := tc 135 t.Run(tc.evDescription, func(t *testing.T) { 136 ev := types.NewMockDuplicateVoteEvidenceWithValidator(tc.evHeight, tc.evTime, val, evidenceChainID) 137 err := pool.AddEvidence(ev) 138 if tc.expErr { 139 assert.Error(t, err) 140 } else { 141 assert.NoError(t, err) 142 } 143 }) 144 } 145 } 146 147 func TestReportConflictingVotes(t *testing.T) { 148 var height int64 = 10 149 150 pool, pv := defaultTestPool(height) 151 val := types.NewValidator(pv.PrivKey.PubKey(), 10) 152 ev := types.NewMockDuplicateVoteEvidenceWithValidator(height+1, defaultEvidenceTime, pv, evidenceChainID) 153 154 pool.ReportConflictingVotes(ev.VoteA, ev.VoteB) 155 156 // shouldn't be able to submit the same evidence twice 157 pool.ReportConflictingVotes(ev.VoteA, ev.VoteB) 158 159 // evidence from consensus should not be added immediately but reside in the consensus buffer 160 evList, evSize := pool.PendingEvidence(defaultEvidenceMaxBytes) 161 require.Empty(t, evList) 162 require.Zero(t, evSize) 163 164 next := pool.EvidenceFront() 165 require.Nil(t, next) 166 167 // move to next height and update state and evidence pool 168 state := pool.State() 169 state.LastBlockHeight++ 170 state.LastBlockTime = ev.Time() 171 state.LastValidators = types.NewValidatorSet([]*types.Validator{val}) 172 pool.Update(state, []types.Evidence{}) 173 174 // should be able to retrieve evidence from pool 175 evList, _ = pool.PendingEvidence(defaultEvidenceMaxBytes) 176 require.Equal(t, []types.Evidence{ev}, evList) 177 178 next = pool.EvidenceFront() 179 require.NotNil(t, next) 180 } 181 182 func TestEvidencePoolUpdate(t *testing.T) { 183 height := int64(21) 184 pool, val := defaultTestPool(height) 185 state := pool.State() 186 187 // create new block (no need to save it to blockStore) 188 prunedEv := types.NewMockDuplicateVoteEvidenceWithValidator(1, defaultEvidenceTime.Add(1*time.Minute), 189 val, evidenceChainID) 190 err := pool.AddEvidence(prunedEv) 191 require.NoError(t, err) 192 ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime.Add(21*time.Minute), 193 val, evidenceChainID) 194 lastCommit := makeCommit(height, val.PrivKey.PubKey().Address()) 195 block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{ev}) 196 // update state (partially) 197 state.LastBlockHeight = height + 1 198 state.LastBlockTime = defaultEvidenceTime.Add(22 * time.Minute) 199 err = pool.CheckEvidence(types.EvidenceList{ev}) 200 require.NoError(t, err) 201 202 pool.Update(state, block.Evidence.Evidence) 203 // a) Update marks evidence as committed so pending evidence should be empty 204 evList, evSize := pool.PendingEvidence(defaultEvidenceMaxBytes) 205 assert.Empty(t, evList) 206 assert.Zero(t, evSize) 207 208 // b) If we try to check this evidence again it should fail because it has already been committed 209 err = pool.CheckEvidence(types.EvidenceList{ev}) 210 if assert.Error(t, err) { 211 assert.Equal(t, "evidence was already committed", err.(*types.ErrInvalidEvidence).Reason.Error()) 212 } 213 } 214 215 func TestVerifyPendingEvidencePasses(t *testing.T) { 216 var height int64 = 1 217 pool, val := defaultTestPool(height) 218 ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime.Add(1*time.Minute), 219 val, evidenceChainID) 220 err := pool.AddEvidence(ev) 221 require.NoError(t, err) 222 223 err = pool.CheckEvidence(types.EvidenceList{ev}) 224 assert.NoError(t, err) 225 } 226 227 func TestVerifyDuplicatedEvidenceFails(t *testing.T) { 228 var height int64 = 1 229 pool, val := defaultTestPool(height) 230 ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime.Add(1*time.Minute), 231 val, evidenceChainID) 232 err := pool.CheckEvidence(types.EvidenceList{ev, ev}) 233 if assert.Error(t, err) { 234 assert.Equal(t, "duplicate evidence", err.(*types.ErrInvalidEvidence).Reason.Error()) 235 } 236 } 237 238 // check that valid light client evidence is correctly validated and stored in 239 // evidence pool 240 func TestLightClientAttackEvidenceLifecycle(t *testing.T) { 241 var ( 242 height int64 = 100 243 commonHeight int64 = 90 244 ) 245 246 ev, trusted, common := makeLunaticEvidence(t, height, commonHeight, 247 10, 5, 5, defaultEvidenceTime, defaultEvidenceTime.Add(1*time.Hour)) 248 249 state := sm.State{ 250 LastBlockTime: defaultEvidenceTime.Add(2 * time.Hour), 251 LastBlockHeight: 110, 252 ConsensusParams: *types.DefaultConsensusParams(), 253 } 254 stateStore := &smmocks.Store{} 255 stateStore.On("LoadValidators", height).Return(trusted.ValidatorSet, nil) 256 stateStore.On("LoadValidators", commonHeight).Return(common.ValidatorSet, nil) 257 stateStore.On("Load").Return(state, nil) 258 blockStore := &mocks.BlockStore{} 259 blockStore.On("LoadBlockMeta", height).Return(&types.BlockMeta{Header: *trusted.Header}) 260 blockStore.On("LoadBlockMeta", commonHeight).Return(&types.BlockMeta{Header: *common.Header}) 261 blockStore.On("LoadBlockCommit", height).Return(trusted.Commit) 262 blockStore.On("LoadBlockCommit", commonHeight).Return(common.Commit) 263 264 pool, err := evidence.NewPool(dbm.NewMemDB(), stateStore, blockStore) 265 require.NoError(t, err) 266 pool.SetLogger(log.TestingLogger()) 267 268 err = pool.AddEvidence(ev) 269 assert.NoError(t, err) 270 271 hash := ev.Hash() 272 273 require.NoError(t, pool.AddEvidence(ev)) 274 require.NoError(t, pool.AddEvidence(ev)) 275 276 pendingEv, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes) 277 require.Equal(t, 1, len(pendingEv)) 278 require.Equal(t, ev, pendingEv[0]) 279 280 require.NoError(t, pool.CheckEvidence(pendingEv)) 281 require.Equal(t, ev, pendingEv[0]) 282 283 state.LastBlockHeight++ 284 state.LastBlockTime = state.LastBlockTime.Add(1 * time.Minute) 285 pool.Update(state, pendingEv) 286 require.Equal(t, hash, pendingEv[0].Hash()) 287 288 remaindingEv, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes) 289 require.Empty(t, remaindingEv) 290 291 // evidence is already committed so it shouldn't pass 292 require.Error(t, pool.CheckEvidence(types.EvidenceList{ev})) 293 require.NoError(t, pool.AddEvidence(ev)) 294 295 remaindingEv, _ = pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes) 296 require.Empty(t, remaindingEv) 297 } 298 299 // Tests that restarting the evidence pool after a potential failure will recover the 300 // pending evidence and continue to gossip it 301 func TestRecoverPendingEvidence(t *testing.T) { 302 height := int64(10) 303 val := types.NewMockPV() 304 valAddress := val.PrivKey.PubKey().Address() 305 evidenceDB := dbm.NewMemDB() 306 stateStore := initializeValidatorState(val, height) 307 state, err := stateStore.Load() 308 require.NoError(t, err) 309 blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress) 310 // create previous pool and populate it 311 pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) 312 require.NoError(t, err) 313 pool.SetLogger(log.TestingLogger()) 314 goodEvidence := types.NewMockDuplicateVoteEvidenceWithValidator(height, 315 defaultEvidenceTime.Add(10*time.Minute), val, evidenceChainID) 316 expiredEvidence := types.NewMockDuplicateVoteEvidenceWithValidator(int64(1), 317 defaultEvidenceTime.Add(1*time.Minute), val, evidenceChainID) 318 err = pool.AddEvidence(goodEvidence) 319 require.NoError(t, err) 320 err = pool.AddEvidence(expiredEvidence) 321 require.NoError(t, err) 322 323 // now recover from the previous pool at a different time 324 newStateStore := &smmocks.Store{} 325 newStateStore.On("Load").Return(sm.State{ 326 LastBlockTime: defaultEvidenceTime.Add(25 * time.Minute), 327 LastBlockHeight: height + 15, 328 ConsensusParams: tmproto.ConsensusParams{ 329 Block: tmproto.BlockParams{ 330 MaxBytes: 22020096, 331 MaxGas: -1, 332 }, 333 Evidence: tmproto.EvidenceParams{ 334 MaxAgeNumBlocks: 20, 335 MaxAgeDuration: 20 * time.Minute, 336 MaxBytes: defaultEvidenceMaxBytes, 337 }, 338 }, 339 }, nil) 340 newPool, err := evidence.NewPool(evidenceDB, newStateStore, blockStore) 341 assert.NoError(t, err) 342 evList, _ := newPool.PendingEvidence(defaultEvidenceMaxBytes) 343 assert.Equal(t, 1, len(evList)) 344 next := newPool.EvidenceFront() 345 assert.Equal(t, goodEvidence, next.Value.(types.Evidence)) 346 347 } 348 349 func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) sm.Store { 350 stateDB := dbm.NewMemDB() 351 stateStore := sm.NewStore(stateDB) 352 state := sm.State{ 353 ChainID: evidenceChainID, 354 InitialHeight: 1, 355 LastBlockHeight: height, 356 LastBlockTime: defaultEvidenceTime, 357 Validators: valSet, 358 NextValidators: valSet.CopyIncrementProposerPriority(1), 359 LastValidators: valSet, 360 LastHeightValidatorsChanged: 1, 361 ConsensusParams: tmproto.ConsensusParams{ 362 Block: tmproto.BlockParams{ 363 MaxBytes: 22020096, 364 MaxGas: -1, 365 }, 366 Evidence: tmproto.EvidenceParams{ 367 MaxAgeNumBlocks: 20, 368 MaxAgeDuration: 20 * time.Minute, 369 MaxBytes: 1000, 370 }, 371 }, 372 } 373 374 // save all states up to height 375 for i := int64(0); i <= height; i++ { 376 state.LastBlockHeight = i 377 if err := stateStore.Save(state); err != nil { 378 panic(err) 379 } 380 } 381 382 return stateStore 383 } 384 385 func initializeValidatorState(privVal types.PrivValidator, height int64) sm.Store { 386 387 pubKey, _ := privVal.GetPubKey() 388 validator := &types.Validator{Address: pubKey.Address(), VotingPower: 10, PubKey: pubKey} 389 390 // create validator set and state 391 valSet := &types.ValidatorSet{ 392 Validators: []*types.Validator{validator}, 393 Proposer: validator, 394 } 395 396 return initializeStateFromValidatorSet(valSet, height) 397 } 398 399 // initializeBlockStore creates a block storage and populates it w/ a dummy 400 // block at +height+. 401 func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore { 402 blockStore := store.NewBlockStore(db) 403 404 for i := int64(1); i <= state.LastBlockHeight; i++ { 405 lastCommit := makeCommit(i-1, valAddr) 406 block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil, 407 state.Validators.GetProposer().Address) 408 block.Header.Time = defaultEvidenceTime.Add(time.Duration(i) * time.Minute) 409 block.Header.Version = tmversion.Consensus{Block: version.BlockProtocol, App: 1} 410 const parts = 1 411 partSet := block.MakePartSet(parts) 412 413 seenCommit := makeCommit(i, valAddr) 414 blockStore.SaveBlock(block, partSet, seenCommit) 415 } 416 417 return blockStore 418 } 419 420 func makeCommit(height int64, valAddr []byte) *types.Commit { 421 commitSigs := []types.CommitSig{{ 422 BlockIDFlag: types.BlockIDFlagCommit, 423 ValidatorAddress: valAddr, 424 Timestamp: defaultEvidenceTime, 425 Signature: []byte("Signature"), 426 }} 427 return types.NewCommit(height, 0, types.BlockID{}, commitSigs) 428 } 429 430 func defaultTestPool(height int64) (*evidence.Pool, types.MockPV) { 431 val := types.NewMockPV() 432 valAddress := val.PrivKey.PubKey().Address() 433 evidenceDB := dbm.NewMemDB() 434 stateStore := initializeValidatorState(val, height) 435 state, _ := stateStore.Load() 436 blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress) 437 pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) 438 if err != nil { 439 panic("test evidence pool could not be created") 440 } 441 pool.SetLogger(log.TestingLogger()) 442 return pool, val 443 } 444 445 func createState(height int64, valSet *types.ValidatorSet) sm.State { 446 return sm.State{ 447 ChainID: evidenceChainID, 448 LastBlockHeight: height, 449 LastBlockTime: defaultEvidenceTime, 450 Validators: valSet, 451 ConsensusParams: *types.DefaultConsensusParams(), 452 } 453 }