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.

478 lines
14 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/require"
  9. dbm "github.com/tendermint/tm-db"
  10. "github.com/tendermint/tendermint/crypto"
  11. "github.com/tendermint/tendermint/libs/bytes"
  12. "github.com/tendermint/tendermint/libs/log"
  13. tmrand "github.com/tendermint/tendermint/libs/rand"
  14. tmproto "github.com/tendermint/tendermint/proto/types"
  15. sm "github.com/tendermint/tendermint/state"
  16. "github.com/tendermint/tendermint/store"
  17. "github.com/tendermint/tendermint/types"
  18. tmtime "github.com/tendermint/tendermint/types/time"
  19. )
  20. func TestMain(m *testing.M) {
  21. code := m.Run()
  22. os.Exit(code)
  23. }
  24. const evidenceChainID = "test_chain"
  25. func TestEvidencePool(t *testing.T) {
  26. var (
  27. valAddr = tmrand.Bytes(crypto.AddressSize)
  28. height = int64(52)
  29. stateDB = initializeValidatorState(valAddr, height)
  30. evidenceDB = dbm.NewMemDB()
  31. blockStoreDB = dbm.NewMemDB()
  32. blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
  33. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  34. goodEvidence = types.NewMockEvidence(height, time.Now(), valAddr)
  35. badEvidence = types.NewMockEvidence(1, evidenceTime, valAddr)
  36. )
  37. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  38. require.NoError(t, err)
  39. // bad evidence
  40. err = pool.AddEvidence(badEvidence)
  41. if assert.Error(t, err) {
  42. assert.Contains(t, err.Error(), "is too old; min height is 32 and evidence can not be older than")
  43. }
  44. assert.False(t, pool.IsPending(badEvidence))
  45. assert.True(t, pool.IsEvidenceExpired(badEvidence))
  46. // good evidence
  47. evAdded := make(chan struct{})
  48. go func() {
  49. <-pool.EvidenceWaitChan()
  50. close(evAdded)
  51. }()
  52. err = pool.AddEvidence(goodEvidence)
  53. require.NoError(t, err)
  54. select {
  55. case <-evAdded:
  56. case <-time.After(5 * time.Second):
  57. t.Fatal("evidence was not added to list after 5s")
  58. }
  59. assert.Equal(t, 1, pool.evidenceList.Len())
  60. // if we send it again, it shouldnt add and return an error
  61. err = pool.AddEvidence(goodEvidence)
  62. assert.NoError(t, err)
  63. assert.Equal(t, 1, pool.evidenceList.Len())
  64. }
  65. func TestProposingAndCommittingEvidence(t *testing.T) {
  66. var (
  67. valAddr = tmrand.Bytes(crypto.AddressSize)
  68. height = int64(1)
  69. stateDB = initializeValidatorState(valAddr, height)
  70. evidenceDB = dbm.NewMemDB()
  71. blockStoreDB = dbm.NewMemDB()
  72. blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
  73. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  74. )
  75. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  76. require.NoError(t, err)
  77. // evidence not seen yet:
  78. evidence := types.NewMockEvidence(height, evidenceTime, valAddr)
  79. assert.False(t, pool.IsCommitted(evidence))
  80. // evidence seen but not yet committed:
  81. assert.NoError(t, pool.AddEvidence(evidence))
  82. assert.False(t, pool.IsCommitted(evidence))
  83. // test evidence is proposed
  84. proposedEvidence := pool.AllPendingEvidence()
  85. assert.Equal(t, proposedEvidence[0], evidence)
  86. // evidence seen and committed:
  87. pool.MarkEvidenceAsCommitted(height, proposedEvidence)
  88. assert.True(t, pool.IsCommitted(evidence))
  89. assert.False(t, pool.IsPending(evidence))
  90. assert.Equal(t, 0, pool.evidenceList.Len())
  91. // evidence should
  92. }
  93. func TestAddEvidence(t *testing.T) {
  94. var (
  95. valAddr = tmrand.Bytes(crypto.AddressSize)
  96. height = int64(30)
  97. stateDB = initializeValidatorState(valAddr, height)
  98. evidenceDB = dbm.NewMemDB()
  99. blockStoreDB = dbm.NewMemDB()
  100. blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
  101. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  102. )
  103. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  104. require.NoError(t, err)
  105. testCases := []struct {
  106. evHeight int64
  107. evTime time.Time
  108. expErr bool
  109. evDescription string
  110. }{
  111. {height, time.Now(), false, "valid evidence"},
  112. {height, evidenceTime, false, "valid evidence (despite old time)"},
  113. {int64(1), time.Now(), false, "valid evidence (despite old height)"},
  114. {int64(1), evidenceTime, true,
  115. "evidence from height 1 (created at: 2019-01-01 00:00:00 +0000 UTC) is too old"},
  116. }
  117. for _, tc := range testCases {
  118. tc := tc
  119. t.Run(tc.evDescription, func(t *testing.T) {
  120. ev := types.NewMockEvidence(tc.evHeight, tc.evTime, valAddr)
  121. err := pool.AddEvidence(ev)
  122. if tc.expErr {
  123. assert.Error(t, err)
  124. t.Log(err)
  125. }
  126. })
  127. }
  128. }
  129. func TestEvidencePoolUpdate(t *testing.T) {
  130. var (
  131. valAddr = tmrand.Bytes(crypto.AddressSize)
  132. height = int64(21)
  133. stateDB = initializeValidatorState(valAddr, height)
  134. evidenceDB = dbm.NewMemDB()
  135. blockStoreDB = dbm.NewMemDB()
  136. state = sm.LoadState(stateDB)
  137. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  138. )
  139. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  140. require.NoError(t, err)
  141. // create new block (no need to save it to blockStore)
  142. evidence := types.NewMockEvidence(height, time.Now(), valAddr)
  143. lastCommit := makeCommit(height, valAddr)
  144. block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{evidence})
  145. // update state (partially)
  146. state.LastBlockHeight = height + 1
  147. pool.Update(block, state)
  148. // a) Update marks evidence as committed
  149. assert.True(t, pool.IsCommitted(evidence))
  150. // b) Update updates valToLastHeight map
  151. assert.Equal(t, height+1, pool.ValidatorLastHeight(valAddr))
  152. }
  153. func TestEvidencePoolNewPool(t *testing.T) {
  154. var (
  155. valAddr = tmrand.Bytes(crypto.AddressSize)
  156. height = int64(1)
  157. stateDB = initializeValidatorState(valAddr, height)
  158. evidenceDB = dbm.NewMemDB()
  159. blockStoreDB = dbm.NewMemDB()
  160. state = sm.LoadState(stateDB)
  161. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  162. )
  163. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  164. require.NoError(t, err)
  165. assert.Equal(t, height, pool.ValidatorLastHeight(valAddr))
  166. assert.EqualValues(t, 0, pool.ValidatorLastHeight([]byte("non-existent-validator")))
  167. }
  168. func TestAddingAndPruningPOLC(t *testing.T) {
  169. var (
  170. valAddr = tmrand.Bytes(crypto.AddressSize)
  171. stateDB = initializeValidatorState(valAddr, 1)
  172. evidenceDB = dbm.NewMemDB()
  173. blockStoreDB = dbm.NewMemDB()
  174. state = sm.LoadState(stateDB)
  175. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  176. height = state.ConsensusParams.Evidence.MaxAgeNumBlocks * 2
  177. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  178. )
  179. pubKey, _ := types.NewMockPV().GetPubKey()
  180. polc := types.NewMockPOLC(1, evidenceTime, pubKey)
  181. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  182. require.NoError(t, err)
  183. err = pool.AddPOLC(polc)
  184. assert.NoError(t, err)
  185. // should be able to retrieve polc
  186. newPolc, err := pool.RetrievePOLC(1, 1)
  187. assert.NoError(t, err)
  188. assert.True(t, polc.Equal(newPolc))
  189. // should not be able to retrieve
  190. emptyPolc, err := pool.RetrievePOLC(2, 1)
  191. assert.Error(t, err)
  192. assert.Equal(t, types.ProofOfLockChange{}, emptyPolc)
  193. lastCommit := makeCommit(height-1, valAddr)
  194. block := types.MakeBlock(height, []types.Tx{}, lastCommit, []types.Evidence{})
  195. // update state (partially)
  196. state.LastBlockHeight = height
  197. pool.state.LastBlockHeight = height
  198. // update should prune the polc
  199. pool.Update(block, state)
  200. emptyPolc, err = pool.RetrievePOLC(1, 1)
  201. assert.Error(t, err)
  202. assert.Equal(t, types.ProofOfLockChange{}, emptyPolc)
  203. }
  204. func TestRecoverPendingEvidence(t *testing.T) {
  205. var (
  206. valAddr = tmrand.Bytes(crypto.AddressSize)
  207. height = int64(30)
  208. stateDB = initializeValidatorState(valAddr, height)
  209. evidenceDB = dbm.NewMemDB()
  210. blockStoreDB = dbm.NewMemDB()
  211. state = sm.LoadState(stateDB)
  212. blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
  213. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  214. goodEvidence = types.NewMockEvidence(height, time.Now(), valAddr)
  215. expiredEvidence = types.NewMockEvidence(int64(1), evidenceTime, valAddr)
  216. )
  217. // load good evidence
  218. goodKey := keyPending(goodEvidence)
  219. evi, err := types.EvidenceToProto(goodEvidence)
  220. require.NoError(t, err)
  221. goodEvidenceBytes, err := proto.Marshal(evi)
  222. require.NoError(t, err)
  223. _ = evidenceDB.Set(goodKey, goodEvidenceBytes)
  224. // load expired evidence
  225. expiredKey := keyPending(expiredEvidence)
  226. eevi, err := types.EvidenceToProto(expiredEvidence)
  227. require.NoError(t, err)
  228. expiredEvidenceBytes, err := proto.Marshal(eevi)
  229. require.NoError(t, err)
  230. _ = evidenceDB.Set(expiredKey, expiredEvidenceBytes)
  231. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  232. require.NoError(t, err)
  233. assert.Equal(t, 1, pool.evidenceList.Len())
  234. assert.True(t, pool.IsPending(goodEvidence))
  235. }
  236. func TestPotentialAmnesiaEvidence(t *testing.T) {
  237. var (
  238. val = types.NewMockPV()
  239. pubKey = val.PrivKey.PubKey()
  240. valSet = &types.ValidatorSet{
  241. Validators: []*types.Validator{
  242. val.ExtractIntoValidator(0),
  243. },
  244. Proposer: val.ExtractIntoValidator(0),
  245. }
  246. height = int64(30)
  247. stateDB = initializeStateFromValidatorSet(valSet, height)
  248. evidenceDB = dbm.NewMemDB()
  249. blockStoreDB = dbm.NewMemDB()
  250. state = sm.LoadState(stateDB)
  251. blockStore = initializeBlockStore(blockStoreDB, state, pubKey.Address())
  252. //evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  253. firstBlockID = types.BlockID{
  254. Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
  255. PartSetHeader: types.PartSetHeader{
  256. Total: 1,
  257. Hash: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
  258. },
  259. }
  260. secondBlockID = types.BlockID{
  261. Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
  262. PartSetHeader: types.PartSetHeader{
  263. Total: 1,
  264. Hash: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
  265. },
  266. }
  267. evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
  268. )
  269. pool, err := NewPool(stateDB, evidenceDB, blockStore)
  270. require.NoError(t, err)
  271. pool.SetLogger(log.TestingLogger())
  272. polc := types.NewMockPOLC(25, evidenceTime, pubKey)
  273. err = pool.AddPOLC(polc)
  274. require.NoError(t, err)
  275. _, err = pool.RetrievePOLC(25, 1)
  276. require.NoError(t, err)
  277. voteA := makeVote(25, 0, 0, pubKey.Address(), firstBlockID)
  278. vA := voteA.ToProto()
  279. err = val.SignVote(evidenceChainID, vA)
  280. voteA.Signature = vA.Signature
  281. require.NoError(t, err)
  282. voteB := makeVote(25, 1, 0, pubKey.Address(), secondBlockID)
  283. vB := voteB.ToProto()
  284. err = val.SignVote(evidenceChainID, vB)
  285. voteB.Signature = vB.Signature
  286. require.NoError(t, err)
  287. voteC := makeVote(25, 0, 0, pubKey.Address(), firstBlockID)
  288. voteC.Timestamp.Add(1 * time.Second)
  289. vC := voteC.ToProto()
  290. err = val.SignVote(evidenceChainID, vC)
  291. voteC.Signature = vC.Signature
  292. require.NoError(t, err)
  293. ev := types.PotentialAmnesiaEvidence{
  294. VoteA: voteA,
  295. VoteB: voteB,
  296. }
  297. // we expect the evidence pool to find the polc but log an error as the polc is not valid -> vote was
  298. // not from a validator in this set. However, an error isn't thrown because the evidence pool
  299. // should still be able to save the regular potential amnesia evidence.
  300. err = pool.AddEvidence(ev)
  301. assert.NoError(t, err)
  302. // evidence requires trial period until it is available -> we expect no evidence to be returned
  303. assert.Equal(t, 0, len(pool.PendingEvidence(1)))
  304. nextHeight := pool.nextEvidenceTrialEndedHeight
  305. assert.Greater(t, nextHeight, int64(0))
  306. // evidence is not ready to be upgraded so we return the height we expect the evidence to be.
  307. nextHeight = pool.upgradePotentialAmnesiaEvidence()
  308. assert.Equal(t, height+pool.state.ConsensusParams.Evidence.ProofTrialPeriod, nextHeight)
  309. // now evidence is ready to be upgraded to amnesia evidence -> we expect -1 to be the next height as their is
  310. // no more pending potential amnesia evidence left
  311. pool.state.LastBlockHeight = nextHeight
  312. nextHeight = pool.upgradePotentialAmnesiaEvidence()
  313. assert.Equal(t, int64(-1), nextHeight)
  314. assert.Equal(t, 1, len(pool.PendingEvidence(1)))
  315. // evidence of voting back in the past which is instantly punishable -> amnesia evidence is made directly
  316. voteA.Timestamp.Add(1 * time.Second)
  317. ev2 := types.PotentialAmnesiaEvidence{
  318. VoteA: voteB,
  319. VoteB: voteC,
  320. }
  321. err = pool.AddEvidence(ev2)
  322. assert.NoError(t, err)
  323. assert.Equal(t, 2, len(pool.AllPendingEvidence()))
  324. }
  325. func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) dbm.DB {
  326. stateDB := dbm.NewMemDB()
  327. state := sm.State{
  328. ChainID: evidenceChainID,
  329. LastBlockHeight: height,
  330. LastBlockTime: tmtime.Now(),
  331. Validators: valSet,
  332. NextValidators: valSet.CopyIncrementProposerPriority(1),
  333. LastValidators: valSet,
  334. LastHeightValidatorsChanged: 1,
  335. ConsensusParams: tmproto.ConsensusParams{
  336. Block: tmproto.BlockParams{
  337. MaxBytes: 22020096,
  338. MaxGas: -1,
  339. },
  340. Evidence: tmproto.EvidenceParams{
  341. MaxAgeNumBlocks: 20,
  342. MaxAgeDuration: 48 * time.Hour,
  343. MaxNum: 50,
  344. ProofTrialPeriod: 1,
  345. },
  346. },
  347. }
  348. // save all states up to height
  349. for i := int64(0); i <= height; i++ {
  350. state.LastBlockHeight = i
  351. sm.SaveState(stateDB, state)
  352. }
  353. return stateDB
  354. }
  355. func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
  356. pubKey, _ := types.NewMockPV().GetPubKey()
  357. validator := &types.Validator{Address: valAddr, VotingPower: 0, PubKey: pubKey}
  358. // create validator set and state
  359. valSet := &types.ValidatorSet{
  360. Validators: []*types.Validator{validator},
  361. Proposer: validator,
  362. }
  363. return initializeStateFromValidatorSet(valSet, height)
  364. }
  365. // initializeBlockStore creates a block storage and populates it w/ a dummy
  366. // block at +height+.
  367. func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore {
  368. blockStore := store.NewBlockStore(db)
  369. for i := int64(1); i <= state.LastBlockHeight; i++ {
  370. lastCommit := makeCommit(i-1, valAddr)
  371. block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil,
  372. state.Validators.GetProposer().Address)
  373. const parts = 1
  374. partSet := block.MakePartSet(parts)
  375. seenCommit := makeCommit(i, valAddr)
  376. blockStore.SaveBlock(block, partSet, seenCommit)
  377. }
  378. return blockStore
  379. }
  380. func makeCommit(height int64, valAddr []byte) *types.Commit {
  381. commitSigs := []types.CommitSig{{
  382. BlockIDFlag: types.BlockIDFlagCommit,
  383. ValidatorAddress: valAddr,
  384. Timestamp: time.Now(),
  385. Signature: []byte("Signature"),
  386. }}
  387. return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
  388. }
  389. func makeVote(height int64, round, index int32, addr bytes.HexBytes, blockID types.BlockID) *types.Vote {
  390. return &types.Vote{
  391. Type: tmproto.SignedMsgType(2),
  392. Height: height,
  393. Round: round,
  394. BlockID: blockID,
  395. Timestamp: time.Now(),
  396. ValidatorAddress: addr,
  397. ValidatorIndex: index,
  398. }
  399. }