You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

323 lines
9.7 KiB

7 years ago
  1. package evidence
  2. import (
  3. "os"
  4. "testing"
  5. "time"
  6. "github.com/gogo/protobuf/proto"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/mock"
  9. "github.com/stretchr/testify/require"
  10. dbm "github.com/tendermint/tm-db"
  11. "github.com/tendermint/tendermint/evidence/mocks"
  12. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  13. sm "github.com/tendermint/tendermint/state"
  14. "github.com/tendermint/tendermint/store"
  15. "github.com/tendermint/tendermint/types"
  16. )
  17. func TestMain(m *testing.M) {
  18. code := m.Run()
  19. os.Exit(code)
  20. }
  21. const evidenceChainID = "test_chain"
  22. var defaultEvidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  23. func TestEvidencePoolBasic(t *testing.T) {
  24. var (
  25. val = types.NewMockPV()
  26. height = int64(1)
  27. stateStore = initializeValidatorState(val, height)
  28. evidenceDB = dbm.NewMemDB()
  29. blockStore = &mocks.BlockStore{}
  30. )
  31. blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
  32. &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}},
  33. )
  34. pool, err := NewPool(evidenceDB, stateStore, blockStore)
  35. require.NoError(t, err)
  36. // evidence not seen yet:
  37. evidence := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, val, evidenceChainID)
  38. assert.False(t, pool.IsCommitted(evidence))
  39. // good evidence
  40. evAdded := make(chan struct{})
  41. go func() {
  42. <-pool.EvidenceWaitChan()
  43. close(evAdded)
  44. }()
  45. // evidence seen but not yet committed:
  46. assert.NoError(t, pool.AddEvidence(evidence))
  47. select {
  48. case <-evAdded:
  49. case <-time.After(5 * time.Second):
  50. t.Fatal("evidence was not added to list after 5s")
  51. }
  52. assert.Equal(t, 1, pool.evidenceList.Len())
  53. assert.False(t, pool.IsCommitted(evidence))
  54. assert.True(t, pool.IsPending(evidence))
  55. // test evidence is proposed
  56. proposedEvidence := pool.AllPendingEvidence()
  57. assert.Equal(t, proposedEvidence[0], evidence)
  58. proposedEvidence = pool.PendingEvidence(1)
  59. assert.Equal(t, proposedEvidence[0], evidence)
  60. // evidence seen and committed:
  61. pool.MarkEvidenceAsCommitted(height, proposedEvidence)
  62. assert.True(t, pool.IsCommitted(evidence))
  63. assert.False(t, pool.IsPending(evidence))
  64. assert.Equal(t, 0, pool.evidenceList.Len())
  65. // no evidence should be pending
  66. proposedEvidence = pool.PendingEvidence(1)
  67. assert.Empty(t, proposedEvidence)
  68. }
  69. // Tests inbound evidence for the right time and height
  70. func TestAddExpiredEvidence(t *testing.T) {
  71. var (
  72. val = types.NewMockPV()
  73. height = int64(30)
  74. stateStore = initializeValidatorState(val, height)
  75. evidenceDB = dbm.NewMemDB()
  76. blockStore = &mocks.BlockStore{}
  77. expiredEvidenceTime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
  78. expiredHeight = int64(2)
  79. )
  80. blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(func(h int64) *types.BlockMeta {
  81. if h == height || h == expiredHeight {
  82. return &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}}
  83. }
  84. return &types.BlockMeta{Header: types.Header{Time: expiredEvidenceTime}}
  85. })
  86. pool, err := NewPool(evidenceDB, stateStore, blockStore)
  87. require.NoError(t, err)
  88. testCases := []struct {
  89. evHeight int64
  90. evTime time.Time
  91. expErr bool
  92. evDescription string
  93. }{
  94. {height, defaultEvidenceTime, false, "valid evidence"},
  95. {expiredHeight, defaultEvidenceTime, false, "valid evidence (despite old height)"},
  96. {height - 1, expiredEvidenceTime, false, "valid evidence (despite old time)"},
  97. {expiredHeight - 1, expiredEvidenceTime, true,
  98. "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"},
  99. }
  100. for _, tc := range testCases {
  101. tc := tc
  102. t.Run(tc.evDescription, func(t *testing.T) {
  103. ev := types.NewMockDuplicateVoteEvidenceWithValidator(tc.evHeight, tc.evTime, val, evidenceChainID)
  104. err := pool.AddEvidence(ev)
  105. if tc.expErr {
  106. assert.Error(t, err)
  107. } else {
  108. assert.NoError(t, err)
  109. }
  110. })
  111. }
  112. }
  113. func TestEvidencePoolUpdate(t *testing.T) {
  114. height := int64(21)
  115. pool, val := defaultTestPool(height)
  116. state := pool.State()
  117. // create new block (no need to save it to blockStore)
  118. evidence := types.NewMockDuplicateVoteEvidence(height, time.Now(), evidenceChainID)
  119. lastCommit := makeCommit(height, val.PrivKey.PubKey().Address())
  120. block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{evidence})
  121. // update state (partially)
  122. state.LastBlockHeight = height + 1
  123. pool.Update(block, state)
  124. // a) Update marks evidence as committed
  125. assert.True(t, pool.IsCommitted(evidence))
  126. }
  127. func TestVerifyEvidenceCommittedEvidenceFails(t *testing.T) {
  128. height := int64(1)
  129. pool, _ := defaultTestPool(height)
  130. committedEvidence := types.NewMockDuplicateVoteEvidence(height, time.Now(), evidenceChainID)
  131. pool.MarkEvidenceAsCommitted(height, []types.Evidence{committedEvidence})
  132. err := pool.Verify(committedEvidence)
  133. if assert.Error(t, err) {
  134. assert.Equal(t, "evidence was already committed", err.Error())
  135. }
  136. }
  137. func TestVeriyEvidencePendingEvidencePasses(t *testing.T) {
  138. var (
  139. val = types.NewMockPV()
  140. height = int64(1)
  141. stateStore = initializeValidatorState(val, height)
  142. blockStore = &mocks.BlockStore{}
  143. )
  144. blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
  145. &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}},
  146. )
  147. pool, err := NewPool(dbm.NewMemDB(), stateStore, blockStore)
  148. require.NoError(t, err)
  149. evidence := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, val, evidenceChainID)
  150. err = pool.AddEvidence(evidence)
  151. require.NoError(t, err)
  152. err = pool.Verify(evidence)
  153. assert.NoError(t, err)
  154. }
  155. func TestRecoverPendingEvidence(t *testing.T) {
  156. var (
  157. val = types.NewMockPV()
  158. valAddr = val.PrivKey.PubKey().Address()
  159. height = int64(30)
  160. stateStore = initializeValidatorState(val, height)
  161. evidenceDB = dbm.NewMemDB()
  162. blockStoreDB = dbm.NewMemDB()
  163. state = stateStore.LoadState()
  164. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  165. expiredEvidenceTime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
  166. goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height,
  167. defaultEvidenceTime, val, evidenceChainID)
  168. expiredEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(int64(1),
  169. expiredEvidenceTime, val, evidenceChainID)
  170. )
  171. // load good evidence
  172. goodKey := keyPending(goodEvidence)
  173. evi, err := types.EvidenceToProto(goodEvidence)
  174. require.NoError(t, err)
  175. goodEvidenceBytes, err := proto.Marshal(evi)
  176. require.NoError(t, err)
  177. _ = evidenceDB.Set(goodKey, goodEvidenceBytes)
  178. // load expired evidence
  179. expiredKey := keyPending(expiredEvidence)
  180. eevi, err := types.EvidenceToProto(expiredEvidence)
  181. require.NoError(t, err)
  182. expiredEvidenceBytes, err := proto.Marshal(eevi)
  183. require.NoError(t, err)
  184. _ = evidenceDB.Set(expiredKey, expiredEvidenceBytes)
  185. pool, err := NewPool(evidenceDB, stateStore, blockStore)
  186. require.NoError(t, err)
  187. assert.Equal(t, 1, pool.evidenceList.Len())
  188. assert.True(t, pool.IsPending(goodEvidence))
  189. assert.False(t, pool.Has(expiredEvidence))
  190. }
  191. func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) StateStore {
  192. stateDB := dbm.NewMemDB()
  193. state := sm.State{
  194. ChainID: evidenceChainID,
  195. InitialHeight: 1,
  196. LastBlockHeight: height,
  197. LastBlockTime: defaultEvidenceTime,
  198. Validators: valSet,
  199. NextValidators: valSet.CopyIncrementProposerPriority(1),
  200. LastValidators: valSet,
  201. LastHeightValidatorsChanged: 1,
  202. ConsensusParams: tmproto.ConsensusParams{
  203. Block: tmproto.BlockParams{
  204. MaxBytes: 22020096,
  205. MaxGas: -1,
  206. },
  207. Evidence: tmproto.EvidenceParams{
  208. MaxAgeNumBlocks: 20,
  209. MaxAgeDuration: 48 * time.Hour,
  210. MaxNum: 50,
  211. },
  212. },
  213. }
  214. // save all states up to height
  215. for i := int64(0); i <= height; i++ {
  216. state.LastBlockHeight = i
  217. sm.SaveState(stateDB, state)
  218. }
  219. return &stateStore{db: stateDB}
  220. }
  221. func initializeValidatorState(privVal types.PrivValidator, height int64) StateStore {
  222. pubKey, _ := privVal.GetPubKey()
  223. validator := &types.Validator{Address: pubKey.Address(), VotingPower: 0, PubKey: pubKey}
  224. // create validator set and state
  225. valSet := &types.ValidatorSet{
  226. Validators: []*types.Validator{validator},
  227. Proposer: validator,
  228. }
  229. return initializeStateFromValidatorSet(valSet, height)
  230. }
  231. // initializeBlockStore creates a block storage and populates it w/ a dummy
  232. // block at +height+.
  233. func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore {
  234. blockStore := store.NewBlockStore(db)
  235. for i := int64(1); i <= state.LastBlockHeight; i++ {
  236. lastCommit := makeCommit(i-1, valAddr)
  237. block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil,
  238. state.Validators.GetProposer().Address)
  239. const parts = 1
  240. partSet := block.MakePartSet(parts)
  241. seenCommit := makeCommit(i, valAddr)
  242. blockStore.SaveBlock(block, partSet, seenCommit)
  243. }
  244. return blockStore
  245. }
  246. func makeCommit(height int64, valAddr []byte) *types.Commit {
  247. commitSigs := []types.CommitSig{{
  248. BlockIDFlag: types.BlockIDFlagCommit,
  249. ValidatorAddress: valAddr,
  250. Timestamp: time.Now(),
  251. Signature: []byte("Signature"),
  252. }}
  253. return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
  254. }
  255. func defaultTestPool(height int64) (*Pool, types.MockPV) {
  256. val := types.NewMockPV()
  257. valAddress := val.PrivKey.PubKey().Address()
  258. evidenceDB := dbm.NewMemDB()
  259. stateStore := initializeValidatorState(val, height)
  260. blockStore := initializeBlockStore(dbm.NewMemDB(), stateStore.LoadState(), valAddress)
  261. pool, err := NewPool(evidenceDB, stateStore, blockStore)
  262. if err != nil {
  263. panic("test evidence pool could not be created")
  264. }
  265. return pool, val
  266. }