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.

488 lines
15 KiB

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