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.

508 lines
16 KiB

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