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.

94 lines
2.8 KiB

  1. package consensus
  2. import (
  3. "testing"
  4. "github.com/tendermint/tendermint/libs/bytes"
  5. "github.com/tendermint/tendermint/libs/log"
  6. tmrand "github.com/tendermint/tendermint/libs/rand"
  7. "github.com/tendermint/tendermint/p2p"
  8. "github.com/tendermint/tendermint/types"
  9. )
  10. //----------------------------------------------
  11. // byzantine failures
  12. // one byz val sends a precommit for a random block at each height
  13. // Ensure a testnet makes blocks
  14. func TestReactorInvalidPrecommit(t *testing.T) {
  15. N := 4
  16. css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter)
  17. defer cleanup()
  18. for i := 0; i < 4; i++ {
  19. ticker := NewTimeoutTicker()
  20. ticker.SetLogger(css[i].Logger)
  21. css[i].SetTimeoutTicker(ticker)
  22. }
  23. reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N)
  24. // this val sends a random precommit at each height
  25. byzValIdx := 0
  26. byzVal := css[byzValIdx]
  27. byzR := reactors[byzValIdx]
  28. // update the doPrevote function to just send a valid precommit for a random block
  29. // and otherwise disable the priv validator
  30. byzVal.mtx.Lock()
  31. pv := byzVal.privValidator
  32. byzVal.doPrevote = func(height int64, round int) {
  33. invalidDoPrevoteFunc(t, height, round, byzVal, byzR.Switch, pv)
  34. }
  35. byzVal.mtx.Unlock()
  36. defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
  37. // wait for a bunch of blocks
  38. // TODO: make this tighter by ensuring the halt happens by block 2
  39. for i := 0; i < 10; i++ {
  40. timeoutWaitGroup(t, N, func(j int) {
  41. <-blocksSubs[j].Out()
  42. }, css)
  43. }
  44. }
  45. func invalidDoPrevoteFunc(t *testing.T, height int64, round int, cs *State, sw *p2p.Switch, pv types.PrivValidator) {
  46. // routine to:
  47. // - precommit for a random block
  48. // - send precommit to all peers
  49. // - disable privValidator (so we don't do normal precommits)
  50. go func() {
  51. cs.mtx.Lock()
  52. cs.privValidator = pv
  53. pubKey, err := cs.privValidator.GetPubKey()
  54. if err != nil {
  55. panic(err)
  56. }
  57. addr := pubKey.Address()
  58. valIndex, _ := cs.Validators.GetByAddress(addr)
  59. // precommit a random block
  60. blockHash := bytes.HexBytes(tmrand.Bytes(32))
  61. precommit := &types.Vote{
  62. ValidatorAddress: addr,
  63. ValidatorIndex: valIndex,
  64. Height: cs.Height,
  65. Round: cs.Round,
  66. Timestamp: cs.voteTime(),
  67. Type: types.PrecommitType,
  68. BlockID: types.BlockID{
  69. Hash: blockHash,
  70. PartsHeader: types.PartSetHeader{Total: 1, Hash: tmrand.Bytes(32)}},
  71. }
  72. cs.privValidator.SignVote(cs.state.ChainID, precommit)
  73. cs.privValidator = nil // disable priv val so we don't do normal votes
  74. cs.mtx.Unlock()
  75. peers := sw.Peers().List()
  76. for _, peer := range peers {
  77. cs.Logger.Info("Sending bad vote", "block", blockHash, "peer", peer)
  78. peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{precommit}))
  79. }
  80. }()
  81. }