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.

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