func TestRecoverPendingEvidence(t *testing.T) {
	height := int64(10)
	val := types.NewMockPV()
	valAddress := val.PrivKey.PubKey().Address()
	evidenceDB := dbm.NewMemDB()
	stateStore := initializeValidatorState(t, val, height)
	state, err := stateStore.Load()
	require.NoError(t, err)
	blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress)
	pool, err := evidence.NewPool(log.TestingLogger(), evidenceDB, stateStore, blockStore)
	require.NoError(t, err)
	goodEvidence := types.NewMockDuplicateVoteEvidenceWithValidator(
		height,
		defaultEvidenceTime.Add(10*time.Minute),
		val,
		evidenceChainID,
	)
	expiredEvidence := types.NewMockDuplicateVoteEvidenceWithValidator(
		int64(1),
		defaultEvidenceTime.Add(1*time.Minute),
		val,
		evidenceChainID,
	)
	require.NoError(t, pool.AddEvidence(goodEvidence))
	require.NoError(t, pool.AddEvidence(expiredEvidence))
	newStateStore := &smmocks.Store{}
	newStateStore.On("Load").Return(sm.State{
		LastBlockTime:   defaultEvidenceTime.Add(25 * time.Minute),
		LastBlockHeight: height + 15,
		ConsensusParams: types.ConsensusParams{
			Block: types.BlockParams{
				MaxBytes: 22020096,
				MaxGas:   -1,
			},
			Evidence: types.EvidenceParams{
				MaxAgeNumBlocks: 20,
				MaxAgeDuration:  20 * time.Minute,
				MaxBytes:        defaultEvidenceMaxBytes,
			},
		},
	}, nil)
	newPool, err := evidence.NewPool(log.TestingLogger(), evidenceDB, newStateStore, blockStore)
	require.NoError(t, err)
	evList, _ := newPool.PendingEvidence(defaultEvidenceMaxBytes)
	require.Equal(t, 1, len(evList))
	next := newPool.EvidenceFront()
	require.Equal(t, goodEvidence, next.Value.(types.Evidence))
}
Tests that restarting the evidence pool after a potential failure will recover the
pending evidence and continue to gossip it
package evidence_test
import (
	"context"
	"testing"
	"time"
	"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/internal/evidence"
	"github.com/tendermint/tendermint/internal/evidence/mocks"
	sm "github.com/tendermint/tendermint/internal/state"
	smmocks "github.com/tendermint/tendermint/internal/state/mocks"
	sf "github.com/tendermint/tendermint/internal/state/test/factory"
	"github.com/tendermint/tendermint/internal/store"
	"github.com/tendermint/tendermint/internal/test/factory"
	"github.com/tendermint/tendermint/libs/log"
	"github.com/tendermint/tendermint/types"
	"github.com/tendermint/tendermint/version"
)
const evidenceChainID = "test_chain"
var (
	defaultEvidenceTime           = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
	defaultEvidenceMaxBytes int64 = 1000
)
func TestEvidencePoolBasic(t *testing.T) {
	var (
		height     = int64(1)
		stateStore = &smmocks.Store{}
		evidenceDB = dbm.NewMemDB()
		blockStore = &mocks.BlockStore{}
	)
	valSet, privVals := factory.RandValidatorSet(1, 10)
	blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
		&types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}},
	)
	stateStore.On("LoadValidators", mock.AnythingOfType("int64")).Return(valSet, nil)
	stateStore.On("Load").Return(createState(height+1, valSet), nil)
	pool, err := evidence.NewPool(log.TestingLogger(), evidenceDB, stateStore, blockStore)
	require.NoError(t, err)
	evs, size := pool.PendingEvidence(defaultEvidenceMaxBytes)
	require.Equal(t, 0, len(evs))
	require.Zero(t, size)
	ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, privVals[0], evidenceChainID)
	evAdded := make(chan struct{})
	go func() {
		<-pool.EvidenceWaitChan()
		close(evAdded)
	}()
	require.NoError(t, pool.AddEvidence(ev))
	select {
	case <-evAdded:
	case <-time.After(5 * time.Second):
		t.Fatal("evidence was not added to list after 5s")
	}
	next := pool.EvidenceFront()
	require.Equal(t, ev, next.Value.(types.Evidence))
	const evidenceBytes int64 = 372
	evs, size = pool.PendingEvidence(evidenceBytes)
	require.Equal(t, 1, len(evs))
	require.Equal(t, evidenceBytes, size)  
	require.NoError(t, pool.AddEvidence(ev))
	evs, _ = pool.PendingEvidence(defaultEvidenceMaxBytes)
	require.Equal(t, 1, len(evs))
}
func TestAddExpiredEvidence(t *testing.T) {
	var (
		val                 = types.NewMockPV()
		height              = int64(30)
		stateStore          = initializeValidatorState(t, val, height)
		evidenceDB          = dbm.NewMemDB()
		blockStore          = &mocks.BlockStore{}
		expiredEvidenceTime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
		expiredHeight       = int64(2)
	)
	blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(func(h int64) *types.BlockMeta {
		if h == height || h == expiredHeight {
			return &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}}
		}
		return &types.BlockMeta{Header: types.Header{Time: expiredEvidenceTime}}
	})
	pool, err := evidence.NewPool(log.TestingLogger(), evidenceDB, stateStore, blockStore)
	require.NoError(t, err)
	testCases := []struct {
		evHeight      int64
		evTime        time.Time
		expErr        bool
		evDescription string
	}{
		{height, defaultEvidenceTime, false, "valid evidence"},
		{expiredHeight, defaultEvidenceTime, false, "valid evidence (despite old height)"},
		{height - 1, expiredEvidenceTime, false, "valid evidence (despite old time)"},
		{expiredHeight - 1, expiredEvidenceTime, true,
			"evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"},
		{height, defaultEvidenceTime.Add(1 * time.Minute), true, "evidence time and block time is different"},
	}
	for _, tc := range testCases {
		tc := tc
		t.Run(tc.evDescription, func(t *testing.T) {
			ev := types.NewMockDuplicateVoteEvidenceWithValidator(tc.evHeight, tc.evTime, val, evidenceChainID)
			err := pool.AddEvidence(ev)
			if tc.expErr {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
			}
		})
	}
}
func TestReportConflictingVotes(t *testing.T) {
	var height int64 = 10
	pool, pv := defaultTestPool(t, height)
	val := types.NewValidator(pv.PrivKey.PubKey(), 10)
	ev := types.NewMockDuplicateVoteEvidenceWithValidator(height+1, defaultEvidenceTime, pv, evidenceChainID)
	pool.ReportConflictingVotes(ev.VoteA, ev.VoteB)
	pool.ReportConflictingVotes(ev.VoteA, ev.VoteB)
	evList, evSize := pool.PendingEvidence(defaultEvidenceMaxBytes)
	require.Empty(t, evList)
	require.Zero(t, evSize)
	next := pool.EvidenceFront()
	require.Nil(t, next)
	state := pool.State()
	state.LastBlockHeight++
	state.LastBlockTime = ev.Time()
	state.LastValidators = types.NewValidatorSet([]*types.Validator{val})
	pool.Update(state, []types.Evidence{})
	evList, _ = pool.PendingEvidence(defaultEvidenceMaxBytes)
	require.Equal(t, []types.Evidence{ev}, evList)
	next = pool.EvidenceFront()
	require.NotNil(t, next)
}
func TestEvidencePoolUpdate(t *testing.T) {
	height := int64(21)
	pool, val := defaultTestPool(t, height)
	state := pool.State()
	prunedEv := types.NewMockDuplicateVoteEvidenceWithValidator(
		1,
		defaultEvidenceTime.Add(1*time.Minute),
		val,
		evidenceChainID,
	)
	notPrunedEv := types.NewMockDuplicateVoteEvidenceWithValidator(
		2,
		defaultEvidenceTime.Add(2*time.Minute),
		val,
		evidenceChainID,
	)
	require.NoError(t, pool.AddEvidence(prunedEv))
	require.NoError(t, pool.AddEvidence(notPrunedEv))
	ev := types.NewMockDuplicateVoteEvidenceWithValidator(
		height,
		defaultEvidenceTime.Add(21*time.Minute),
		val,
		evidenceChainID,
	)
	lastCommit := makeCommit(height, val.PrivKey.PubKey().Address())
	block := types.MakeBlock(height+1, []types.Tx{}, []types.Evidence{ev}, nil, nil, lastCommit)
	state.LastBlockHeight = height + 1
	state.LastBlockTime = defaultEvidenceTime.Add(22 * time.Minute)
	evList, _ := pool.PendingEvidence(2 * defaultEvidenceMaxBytes)
	require.Equal(t, 2, len(evList))
	require.Equal(t, uint32(2), pool.Size())
	require.NoError(t, pool.CheckEvidence(types.EvidenceList{ev}))
	evList, _ = pool.PendingEvidence(3 * defaultEvidenceMaxBytes)
	require.Equal(t, 3, len(evList))
	require.Equal(t, uint32(3), pool.Size())
	pool.Update(state, block.Evidence.Evidence)
	evList, _ = pool.PendingEvidence(defaultEvidenceMaxBytes)
	require.Equal(t, []types.Evidence{notPrunedEv}, evList)
	err := pool.CheckEvidence(types.EvidenceList{ev})
	if assert.Error(t, err) {
		assert.Equal(t, "evidence was already committed", err.(*types.ErrInvalidEvidence).Reason.Error())
	}
}
func TestVerifyPendingEvidencePasses(t *testing.T) {
	var height int64 = 1
	pool, val := defaultTestPool(t, height)
	ev := types.NewMockDuplicateVoteEvidenceWithValidator(
		height,
		defaultEvidenceTime.Add(1*time.Minute),
		val,
		evidenceChainID,
	)
	require.NoError(t, pool.AddEvidence(ev))
	require.NoError(t, pool.CheckEvidence(types.EvidenceList{ev}))
}
func TestVerifyDuplicatedEvidenceFails(t *testing.T) {
	var height int64 = 1
	pool, val := defaultTestPool(t, height)
	ev := types.NewMockDuplicateVoteEvidenceWithValidator(
		height,
		defaultEvidenceTime.Add(1*time.Minute),
		val,
		evidenceChainID,
	)
	err := pool.CheckEvidence(types.EvidenceList{ev, ev})
	if assert.Error(t, err) {
		assert.Equal(t, "duplicate evidence", err.(*types.ErrInvalidEvidence).Reason.Error())
	}
}
func TestLightClientAttackEvidenceLifecycle(t *testing.T) {
	var (
		height       int64 = 100
		commonHeight int64 = 90
	)
	ev, trusted, common := makeLunaticEvidence(t, height, commonHeight,
		10, 5, 5, defaultEvidenceTime, defaultEvidenceTime.Add(1*time.Hour))
	state := sm.State{
		LastBlockTime:   defaultEvidenceTime.Add(2 * time.Hour),
		LastBlockHeight: 110,
		ConsensusParams: *types.DefaultConsensusParams(),
	}
	stateStore := &smmocks.Store{}
	stateStore.On("LoadValidators", height).Return(trusted.ValidatorSet, nil)
	stateStore.On("LoadValidators", commonHeight).Return(common.ValidatorSet, nil)
	stateStore.On("Load").Return(state, nil)
	blockStore := &mocks.BlockStore{}
	blockStore.On("LoadBlockMeta", height).Return(&types.BlockMeta{Header: *trusted.Header})
	blockStore.On("LoadBlockMeta", commonHeight).Return(&types.BlockMeta{Header: *common.Header})
	blockStore.On("LoadBlockCommit", height).Return(trusted.Commit)
	blockStore.On("LoadBlockCommit", commonHeight).Return(common.Commit)
	pool, err := evidence.NewPool(log.TestingLogger(), dbm.NewMemDB(), stateStore, blockStore)
	require.NoError(t, err)
	hash := ev.Hash()
	require.NoError(t, pool.AddEvidence(ev))
	require.NoError(t, pool.AddEvidence(ev))
	pendingEv, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
	require.Equal(t, 1, len(pendingEv))
	require.Equal(t, ev, pendingEv[0])
	require.NoError(t, pool.CheckEvidence(pendingEv))
	require.Equal(t, ev, pendingEv[0])
	state.LastBlockHeight++
	state.LastBlockTime = state.LastBlockTime.Add(1 * time.Minute)
	pool.Update(state, pendingEv)
	require.Equal(t, hash, pendingEv[0].Hash())
	remaindingEv, _ := pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
	require.Empty(t, remaindingEv)
	require.Error(t, pool.CheckEvidence(types.EvidenceList{ev}))
	require.NoError(t, pool.AddEvidence(ev))
	remaindingEv, _ = pool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
	require.Empty(t, remaindingEv)
}