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.

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