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.

466 lines
16 KiB

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