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.

507 lines
16 KiB

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