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.

579 lines
18 KiB

7 years ago
  1. package evidence
  2. import (
  3. "os"
  4. "testing"
  5. "time"
  6. "github.com/gogo/protobuf/proto"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/mock"
  9. "github.com/stretchr/testify/require"
  10. dbm "github.com/tendermint/tm-db"
  11. "github.com/tendermint/tendermint/crypto/tmhash"
  12. "github.com/tendermint/tendermint/evidence/mocks"
  13. "github.com/tendermint/tendermint/libs/bytes"
  14. "github.com/tendermint/tendermint/libs/log"
  15. tmrand "github.com/tendermint/tendermint/libs/rand"
  16. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  17. sm "github.com/tendermint/tendermint/state"
  18. "github.com/tendermint/tendermint/store"
  19. "github.com/tendermint/tendermint/types"
  20. tmtime "github.com/tendermint/tendermint/types/time"
  21. )
  22. func TestMain(m *testing.M) {
  23. code := m.Run()
  24. os.Exit(code)
  25. }
  26. const evidenceChainID = "test_chain"
  27. func TestEvidencePool(t *testing.T) {
  28. var (
  29. val = types.NewMockPV()
  30. height = int64(52)
  31. stateDB = initializeValidatorState(val, height)
  32. evidenceDB = dbm.NewMemDB()
  33. blockStore = &mocks.BlockStore{}
  34. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  35. goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, evidenceTime, val, evidenceChainID)
  36. badEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(1, evidenceTime, val, evidenceChainID)
  37. )
  38. blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
  39. &types.BlockMeta{Header: types.Header{Time: evidenceTime}},
  40. )
  41. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  42. require.NoError(t, err)
  43. // bad evidence
  44. err = pool.AddEvidence(badEvidence)
  45. if assert.Error(t, err) {
  46. assert.Contains(t, err.Error(), "is too old; min height is 32 and evidence can not be older than")
  47. }
  48. assert.False(t, pool.IsPending(badEvidence))
  49. assert.True(t, pool.IsEvidenceExpired(badEvidence))
  50. // good evidence
  51. evAdded := make(chan struct{})
  52. go func() {
  53. <-pool.EvidenceWaitChan()
  54. close(evAdded)
  55. }()
  56. err = pool.AddEvidence(goodEvidence)
  57. require.NoError(t, err)
  58. select {
  59. case <-evAdded:
  60. case <-time.After(5 * time.Second):
  61. t.Fatal("evidence was not added to list after 5s")
  62. }
  63. assert.Equal(t, 1, pool.evidenceList.Len())
  64. // if we send it again, it shouldnt add and return an error
  65. err = pool.AddEvidence(goodEvidence)
  66. assert.NoError(t, err)
  67. assert.Equal(t, 1, pool.evidenceList.Len())
  68. }
  69. func TestProposingAndCommittingEvidence(t *testing.T) {
  70. var (
  71. val = types.NewMockPV()
  72. height = int64(1)
  73. stateDB = initializeValidatorState(val, height)
  74. evidenceDB = dbm.NewMemDB()
  75. blockStore = &mocks.BlockStore{}
  76. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  77. )
  78. blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
  79. &types.BlockMeta{Header: types.Header{Time: evidenceTime}},
  80. )
  81. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  82. require.NoError(t, err)
  83. // evidence not seen yet:
  84. evidence := types.NewMockDuplicateVoteEvidenceWithValidator(height, evidenceTime, val, evidenceChainID)
  85. assert.False(t, pool.IsCommitted(evidence))
  86. // evidence seen but not yet committed:
  87. assert.NoError(t, pool.AddEvidence(evidence))
  88. assert.False(t, pool.IsCommitted(evidence))
  89. // test evidence is proposed
  90. proposedEvidence := pool.AllPendingEvidence()
  91. assert.Equal(t, proposedEvidence[0], evidence)
  92. // evidence seen and committed:
  93. pool.MarkEvidenceAsCommitted(height, proposedEvidence)
  94. assert.True(t, pool.IsCommitted(evidence))
  95. assert.False(t, pool.IsPending(evidence))
  96. assert.Equal(t, 0, pool.evidenceList.Len())
  97. // evidence should
  98. }
  99. func TestAddEvidence(t *testing.T) {
  100. var (
  101. val = types.NewMockPV()
  102. valAddr = val.PrivKey.PubKey().Address()
  103. height = int64(30)
  104. stateDB = initializeValidatorState(val, height)
  105. evidenceDB = dbm.NewMemDB()
  106. blockStoreDB = dbm.NewMemDB()
  107. blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
  108. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  109. )
  110. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  111. require.NoError(t, err)
  112. testCases := []struct {
  113. evHeight int64
  114. evTime time.Time
  115. expErr bool
  116. evDescription string
  117. }{
  118. {height, time.Now(), false, "valid evidence"},
  119. {height, evidenceTime, false, "valid evidence (despite old time)"},
  120. {int64(1), time.Now(), false, "valid evidence (despite old height)"},
  121. {int64(1), evidenceTime, true,
  122. "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"},
  123. }
  124. for _, tc := range testCases {
  125. tc := tc
  126. t.Run(tc.evDescription, func(t *testing.T) {
  127. ev := types.NewMockDuplicateVoteEvidence(tc.evHeight, tc.evTime, evidenceChainID)
  128. err := pool.AddEvidence(ev)
  129. if tc.expErr {
  130. assert.Error(t, err)
  131. t.Log(err)
  132. }
  133. })
  134. }
  135. }
  136. func TestEvidencePoolUpdate(t *testing.T) {
  137. var (
  138. val = types.NewMockPV()
  139. valAddr = val.PrivKey.PubKey().Address()
  140. height = int64(21)
  141. stateDB = initializeValidatorState(val, height)
  142. evidenceDB = dbm.NewMemDB()
  143. blockStoreDB = dbm.NewMemDB()
  144. state = sm.LoadState(stateDB)
  145. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  146. )
  147. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  148. require.NoError(t, err)
  149. // create new block (no need to save it to blockStore)
  150. evidence := types.NewMockDuplicateVoteEvidence(height, time.Now(), evidenceChainID)
  151. lastCommit := makeCommit(height, valAddr)
  152. block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{evidence})
  153. // update state (partially)
  154. state.LastBlockHeight = height + 1
  155. pool.Update(block, state)
  156. // a) Update marks evidence as committed
  157. assert.True(t, pool.IsCommitted(evidence))
  158. }
  159. func TestAddingAndPruningPOLC(t *testing.T) {
  160. var (
  161. val = types.NewMockPV()
  162. valAddr = val.PrivKey.PubKey().Address()
  163. stateDB = initializeValidatorState(val, 1)
  164. evidenceDB = dbm.NewMemDB()
  165. blockStoreDB = dbm.NewMemDB()
  166. state = sm.LoadState(stateDB)
  167. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  168. height = state.ConsensusParams.Evidence.MaxAgeNumBlocks * 2
  169. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  170. firstBlockID = types.BlockID{
  171. Hash: tmrand.Bytes(tmhash.Size),
  172. PartSetHeader: types.PartSetHeader{
  173. Total: 1,
  174. Hash: tmrand.Bytes(tmhash.Size),
  175. },
  176. }
  177. )
  178. voteA := makeVote(1, 1, 0, val.PrivKey.PubKey().Address(), firstBlockID, evidenceTime)
  179. vA := voteA.ToProto()
  180. err := val.SignVote(evidenceChainID, vA)
  181. require.NoError(t, err)
  182. voteA.Signature = vA.Signature
  183. pubKey, _ := types.NewMockPV().GetPubKey()
  184. polc := &types.ProofOfLockChange{
  185. Votes: []*types.Vote{voteA},
  186. PubKey: pubKey,
  187. }
  188. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  189. require.NoError(t, err)
  190. err = pool.AddPOLC(polc)
  191. assert.NoError(t, err)
  192. // should be able to retrieve polc
  193. newPolc, err := pool.RetrievePOLC(1, 1)
  194. assert.NoError(t, err)
  195. assert.True(t, polc.Equal(newPolc))
  196. // should not be able to retrieve because it doesn't exist
  197. emptyPolc, err := pool.RetrievePOLC(2, 1)
  198. assert.NoError(t, err)
  199. assert.Nil(t, emptyPolc)
  200. lastCommit := makeCommit(height-1, valAddr)
  201. block := types.MakeBlock(height, []types.Tx{}, lastCommit, []types.Evidence{})
  202. // update state (partially)
  203. state.LastBlockHeight = height
  204. pool.state.LastBlockHeight = height
  205. // update should prune the polc
  206. pool.Update(block, state)
  207. emptyPolc, err = pool.RetrievePOLC(1, 1)
  208. assert.NoError(t, err)
  209. assert.Nil(t, emptyPolc)
  210. }
  211. func TestRecoverPendingEvidence(t *testing.T) {
  212. var (
  213. val = types.NewMockPV()
  214. valAddr = val.PrivKey.PubKey().Address()
  215. height = int64(30)
  216. stateDB = initializeValidatorState(val, height)
  217. evidenceDB = dbm.NewMemDB()
  218. blockStoreDB = dbm.NewMemDB()
  219. state = sm.LoadState(stateDB)
  220. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  221. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  222. goodEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), val, evidenceChainID)
  223. expiredEvidence = types.NewMockDuplicateVoteEvidenceWithValidator(int64(1), evidenceTime, val, evidenceChainID)
  224. )
  225. // load good evidence
  226. goodKey := keyPending(goodEvidence)
  227. evi, err := types.EvidenceToProto(goodEvidence)
  228. require.NoError(t, err)
  229. goodEvidenceBytes, err := proto.Marshal(evi)
  230. require.NoError(t, err)
  231. _ = evidenceDB.Set(goodKey, goodEvidenceBytes)
  232. // load expired evidence
  233. expiredKey := keyPending(expiredEvidence)
  234. eevi, err := types.EvidenceToProto(expiredEvidence)
  235. require.NoError(t, err)
  236. expiredEvidenceBytes, err := proto.Marshal(eevi)
  237. require.NoError(t, err)
  238. _ = evidenceDB.Set(expiredKey, expiredEvidenceBytes)
  239. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  240. require.NoError(t, err)
  241. assert.Equal(t, 1, pool.evidenceList.Len())
  242. assert.True(t, pool.IsPending(goodEvidence))
  243. }
  244. // Comprehensive set of test cases relating to the adding, upgrading and overall
  245. // processing of PotentialAmnesiaEvidence and AmnesiaEvidence
  246. func TestAddingPotentialAmnesiaEvidence(t *testing.T) {
  247. var (
  248. val = types.NewMockPV()
  249. val2 = types.NewMockPV()
  250. pubKey = val.PrivKey.PubKey()
  251. pubKey2 = val2.PrivKey.PubKey()
  252. valSet = &types.ValidatorSet{
  253. Validators: []*types.Validator{
  254. val.ExtractIntoValidator(1),
  255. val2.ExtractIntoValidator(3),
  256. },
  257. Proposer: val.ExtractIntoValidator(1),
  258. }
  259. height = int64(30)
  260. stateDB = initializeStateFromValidatorSet(valSet, height)
  261. evidenceDB = dbm.NewMemDB()
  262. state = sm.LoadState(stateDB)
  263. blockStore = &mocks.BlockStore{}
  264. //evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  265. firstBlockID = types.BlockID{
  266. Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
  267. PartSetHeader: types.PartSetHeader{
  268. Total: 1,
  269. Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
  270. },
  271. }
  272. secondBlockID = types.BlockID{
  273. Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
  274. PartSetHeader: types.PartSetHeader{
  275. Total: 1,
  276. Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
  277. },
  278. }
  279. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  280. )
  281. blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return(
  282. &types.BlockMeta{Header: types.Header{Time: evidenceTime}},
  283. )
  284. // TEST SETUP
  285. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  286. require.NoError(t, err)
  287. pool.SetLogger(log.TestingLogger())
  288. voteA := makeVote(height, 0, 0, pubKey.Address(), firstBlockID, evidenceTime)
  289. vA := voteA.ToProto()
  290. err = val.SignVote(evidenceChainID, vA)
  291. voteA.Signature = vA.Signature
  292. require.NoError(t, err)
  293. voteB := makeVote(height, 1, 0, pubKey.Address(), secondBlockID, evidenceTime.Add(3*time.Second))
  294. vB := voteB.ToProto()
  295. err = val.SignVote(evidenceChainID, vB)
  296. voteB.Signature = vB.Signature
  297. require.NoError(t, err)
  298. voteC := makeVote(height, 2, 0, pubKey.Address(), firstBlockID, evidenceTime.Add(2*time.Second))
  299. vC := voteC.ToProto()
  300. err = val.SignVote(evidenceChainID, vC)
  301. voteC.Signature = vC.Signature
  302. require.NoError(t, err)
  303. ev := &types.PotentialAmnesiaEvidence{
  304. VoteA: voteA,
  305. VoteB: voteB,
  306. Timestamp: evidenceTime,
  307. }
  308. polc := &types.ProofOfLockChange{
  309. Votes: []*types.Vote{voteB},
  310. PubKey: pubKey2,
  311. }
  312. err = pool.AddPOLC(polc)
  313. require.NoError(t, err)
  314. polc, err = pool.RetrievePOLC(height, 1)
  315. require.NoError(t, err)
  316. require.NotEmpty(t, polc)
  317. secondValVote := makeVote(height, 1, 0, pubKey2.Address(), secondBlockID, evidenceTime.Add(1*time.Second))
  318. vv2 := secondValVote.ToProto()
  319. err = val2.SignVote(evidenceChainID, vv2)
  320. require.NoError(t, err)
  321. secondValVote.Signature = vv2.Signature
  322. validPolc := &types.ProofOfLockChange{
  323. Votes: []*types.Vote{secondValVote},
  324. PubKey: pubKey,
  325. }
  326. // CASE A
  327. pool.logger.Info("CASE A")
  328. // we expect the evidence pool to find the polc but log an error as the polc is not valid -> vote was
  329. // not from a validator in this set. However, an error isn't thrown because the evidence pool
  330. // should still be able to save the regular potential amnesia evidence.
  331. err = pool.AddEvidence(ev)
  332. assert.NoError(t, err)
  333. // evidence requires trial period until it is available -> we expect no evidence to be returned
  334. assert.Equal(t, 0, len(pool.PendingEvidence(1)))
  335. assert.True(t, pool.IsOnTrial(ev))
  336. nextHeight := pool.nextEvidenceTrialEndedHeight
  337. assert.Greater(t, nextHeight, int64(0))
  338. // CASE B
  339. pool.logger.Info("CASE B")
  340. // evidence is not ready to be upgraded so we return the height we expect the evidence to be.
  341. nextHeight = pool.upgradePotentialAmnesiaEvidence()
  342. assert.Equal(t, height+pool.state.ConsensusParams.Evidence.ProofTrialPeriod, nextHeight)
  343. // CASE C
  344. pool.logger.Info("CASE C")
  345. // now evidence is ready to be upgraded to amnesia evidence -> we expect -1 to be the next height as their is
  346. // no more pending potential amnesia evidence left
  347. lastCommit := makeCommit(height+1, pubKey.Address())
  348. block := types.MakeBlock(height+2, []types.Tx{}, lastCommit, []types.Evidence{})
  349. state.LastBlockHeight = height + 2
  350. pool.Update(block, state)
  351. assert.Equal(t, int64(-1), pool.nextEvidenceTrialEndedHeight)
  352. assert.Equal(t, 1, len(pool.PendingEvidence(1)))
  353. // CASE D
  354. pool.logger.Info("CASE D")
  355. // evidence of voting back in the past which is instantly punishable -> amnesia evidence is made directly
  356. ev2 := &types.PotentialAmnesiaEvidence{
  357. VoteA: voteC,
  358. VoteB: voteB,
  359. Timestamp: evidenceTime,
  360. }
  361. err = pool.AddEvidence(ev2)
  362. assert.NoError(t, err)
  363. expectedAe := &types.AmnesiaEvidence{
  364. PotentialAmnesiaEvidence: ev2,
  365. Polc: types.NewEmptyPOLC(),
  366. }
  367. assert.True(t, pool.IsPending(expectedAe))
  368. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  369. // CASE E
  370. pool.logger.Info("CASE E")
  371. // test for receiving amnesia evidence
  372. ae := types.NewAmnesiaEvidence(ev, types.NewEmptyPOLC())
  373. // we need to run the trial period ourselves so amnesia evidence should not be added, instead
  374. // we should extract out the potential amnesia evidence and trying to add that before realising
  375. // that we already have it -> no error
  376. err = pool.AddEvidence(ae)
  377. assert.NoError(t, err)
  378. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  379. voteD := makeVote(height, 2, 0, pubKey.Address(), firstBlockID, evidenceTime.Add(4*time.Second))
  380. vD := voteD.ToProto()
  381. err = val.SignVote(evidenceChainID, vD)
  382. require.NoError(t, err)
  383. voteD.Signature = vD.Signature
  384. // CASE F
  385. pool.logger.Info("CASE F")
  386. // a new amnesia evidence is seen. It has an empty polc so we should extract the potential amnesia evidence
  387. // and start our own trial
  388. newPe := &types.PotentialAmnesiaEvidence{
  389. VoteA: voteB,
  390. VoteB: voteD,
  391. Timestamp: evidenceTime,
  392. }
  393. newAe := &types.AmnesiaEvidence{
  394. PotentialAmnesiaEvidence: newPe,
  395. Polc: types.NewEmptyPOLC(),
  396. }
  397. err = pool.AddEvidence(newAe)
  398. assert.NoError(t, err)
  399. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  400. assert.True(t, pool.IsOnTrial(newPe))
  401. // CASE G
  402. pool.logger.Info("CASE G")
  403. // Finally, we receive an amnesia evidence containing a valid polc for an earlier potential amnesia evidence
  404. // that we have already upgraded to. We should ad this new amnesia evidence in replace of the prior
  405. // amnesia evidence with an empty polc that we have
  406. aeWithPolc := &types.AmnesiaEvidence{
  407. PotentialAmnesiaEvidence: ev,
  408. Polc: validPolc,
  409. }
  410. err = pool.AddEvidence(aeWithPolc)
  411. assert.NoError(t, err)
  412. assert.True(t, pool.IsPending(aeWithPolc))
  413. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  414. t.Log(pool.AllPendingEvidence())
  415. }
  416. func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) dbm.DB {
  417. stateDB := dbm.NewMemDB()
  418. state := sm.State{
  419. ChainID: evidenceChainID,
  420. InitialHeight: 1,
  421. LastBlockHeight: height,
  422. LastBlockTime: tmtime.Now(),
  423. Validators: valSet,
  424. NextValidators: valSet.CopyIncrementProposerPriority(1),
  425. LastValidators: valSet,
  426. LastHeightValidatorsChanged: 1,
  427. ConsensusParams: tmproto.ConsensusParams{
  428. Block: tmproto.BlockParams{
  429. MaxBytes: 22020096,
  430. MaxGas: -1,
  431. },
  432. Evidence: tmproto.EvidenceParams{
  433. MaxAgeNumBlocks: 20,
  434. MaxAgeDuration: 48 * time.Hour,
  435. MaxNum: 50,
  436. ProofTrialPeriod: 1,
  437. },
  438. },
  439. }
  440. // save all states up to height
  441. for i := int64(0); i <= height; i++ {
  442. state.LastBlockHeight = i
  443. sm.SaveState(stateDB, state)
  444. }
  445. return stateDB
  446. }
  447. func initializeValidatorState(privVal types.PrivValidator, height int64) dbm.DB {
  448. pubKey, _ := privVal.GetPubKey()
  449. validator := &types.Validator{Address: pubKey.Address(), VotingPower: 0, PubKey: pubKey}
  450. // create validator set and state
  451. valSet := &types.ValidatorSet{
  452. Validators: []*types.Validator{validator},
  453. Proposer: validator,
  454. }
  455. return initializeStateFromValidatorSet(valSet, height)
  456. }
  457. // initializeBlockStore creates a block storage and populates it w/ a dummy
  458. // block at +height+.
  459. func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore {
  460. blockStore := store.NewBlockStore(db)
  461. for i := int64(1); i <= state.LastBlockHeight; i++ {
  462. lastCommit := makeCommit(i-1, valAddr)
  463. block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil,
  464. state.Validators.GetProposer().Address)
  465. const parts = 1
  466. partSet := block.MakePartSet(parts)
  467. seenCommit := makeCommit(i, valAddr)
  468. blockStore.SaveBlock(block, partSet, seenCommit)
  469. }
  470. return blockStore
  471. }
  472. func makeCommit(height int64, valAddr []byte) *types.Commit {
  473. commitSigs := []types.CommitSig{{
  474. BlockIDFlag: types.BlockIDFlagCommit,
  475. ValidatorAddress: valAddr,
  476. Timestamp: time.Now(),
  477. Signature: []byte("Signature"),
  478. }}
  479. return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
  480. }
  481. func makeVote(height int64, round, index int32, addr bytes.HexBytes,
  482. blockID types.BlockID, time time.Time) *types.Vote {
  483. return &types.Vote{
  484. Type: tmproto.SignedMsgType(2),
  485. Height: height,
  486. Round: round,
  487. BlockID: blockID,
  488. Timestamp: time,
  489. ValidatorAddress: addr,
  490. ValidatorIndex: index,
  491. }
  492. }