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.

233 lines
7.1 KiB

  1. package consensus
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "io"
  7. "path/filepath"
  8. "testing"
  9. "time"
  10. db "github.com/tendermint/tm-db"
  11. "github.com/tendermint/tendermint/abci/example/kvstore"
  12. cfg "github.com/tendermint/tendermint/config"
  13. tmcon "github.com/tendermint/tendermint/consensus"
  14. "github.com/tendermint/tendermint/libs/log"
  15. tmrand "github.com/tendermint/tendermint/libs/rand"
  16. "github.com/tendermint/tendermint/privval"
  17. "github.com/tendermint/tendermint/proxy"
  18. sm "github.com/tendermint/tendermint/state"
  19. "github.com/tendermint/tendermint/store"
  20. "github.com/tendermint/tendermint/types"
  21. )
  22. // WALGenerateNBlocks generates a consensus WAL. It does this by spinning up a
  23. // stripped down version of node (proxy app, event bus, consensus state) with a
  24. // persistent kvstore application and special consensus wal instance
  25. // (byteBufferWAL) and waits until numBlocks are created.
  26. // If the node fails to produce given numBlocks, it returns an error.
  27. func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) {
  28. config := getConfig(t)
  29. app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator"))
  30. logger := log.TestingLogger().With("wal_generator", "wal_generator")
  31. logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks)
  32. // ///////////////////////////////////////////////////////////////////////////
  33. // COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
  34. // NOTE: we can't import node package because of circular dependency.
  35. // NOTE: we don't do handshake so need to set state.Version.Consensus.App directly.
  36. privValidatorKeyFile := config.PrivValidatorKeyFile()
  37. privValidatorStateFile := config.PrivValidatorStateFile()
  38. privValidator, err := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
  39. if err != nil {
  40. return err
  41. }
  42. genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
  43. if err != nil {
  44. return fmt.Errorf("failed to read genesis file: %w", err)
  45. }
  46. blockStoreDB := db.NewMemDB()
  47. stateDB := blockStoreDB
  48. stateStore := sm.NewStore(stateDB)
  49. state, err := sm.MakeGenesisState(genDoc)
  50. if err != nil {
  51. return fmt.Errorf("failed to make genesis state: %w", err)
  52. }
  53. state.Version.Consensus.App = kvstore.ProtocolVersion
  54. if err = stateStore.Save(state); err != nil {
  55. t.Error(err)
  56. }
  57. blockStore := store.NewBlockStore(blockStoreDB)
  58. proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app))
  59. proxyApp.SetLogger(logger.With("module", "proxy"))
  60. if err := proxyApp.Start(); err != nil {
  61. return fmt.Errorf("failed to start proxy app connections: %w", err)
  62. }
  63. t.Cleanup(func() {
  64. if err := proxyApp.Stop(); err != nil {
  65. t.Error(err)
  66. }
  67. })
  68. eventBus := types.NewEventBus()
  69. eventBus.SetLogger(logger.With("module", "events"))
  70. if err := eventBus.Start(); err != nil {
  71. return fmt.Errorf("failed to start event bus: %w", err)
  72. }
  73. t.Cleanup(func() {
  74. if err := eventBus.Stop(); err != nil {
  75. t.Error(err)
  76. }
  77. })
  78. mempool := emptyMempool{}
  79. evpool := sm.EmptyEvidencePool{}
  80. blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
  81. consensusState := NewState(config.Consensus, state.Copy(),
  82. blockExec, blockStore, mempool, evpool, map[int64]Misbehavior{})
  83. consensusState.SetLogger(logger)
  84. consensusState.SetEventBus(eventBus)
  85. if privValidator != nil {
  86. consensusState.SetPrivValidator(privValidator)
  87. }
  88. // END OF COPY PASTE
  89. // ///////////////////////////////////////////////////////////////////////////
  90. // set consensus wal to buffered WAL, which will write all incoming msgs to buffer
  91. numBlocksWritten := make(chan struct{})
  92. wal := newByteBufferWAL(logger, NewWALEncoder(wr), int64(numBlocks), numBlocksWritten)
  93. // see wal.go#103
  94. if err := wal.Write(tmcon.EndHeightMessage{Height: 0}); err != nil {
  95. t.Error(err)
  96. }
  97. consensusState.wal = wal
  98. if err := consensusState.Start(); err != nil {
  99. return fmt.Errorf("failed to start consensus state: %w", err)
  100. }
  101. select {
  102. case <-numBlocksWritten:
  103. if err := consensusState.Stop(); err != nil {
  104. t.Error(err)
  105. }
  106. return nil
  107. case <-time.After(1 * time.Minute):
  108. if err := consensusState.Stop(); err != nil {
  109. t.Error(err)
  110. }
  111. return fmt.Errorf("waited too long for tendermint to produce %d blocks (grep logs for `wal_generator`)", numBlocks)
  112. }
  113. }
  114. // WALWithNBlocks returns a WAL content with numBlocks.
  115. func WALWithNBlocks(t *testing.T, numBlocks int) (data []byte, err error) {
  116. var b bytes.Buffer
  117. wr := bufio.NewWriter(&b)
  118. if err := WALGenerateNBlocks(t, wr, numBlocks); err != nil {
  119. return []byte{}, err
  120. }
  121. wr.Flush()
  122. return b.Bytes(), nil
  123. }
  124. func randPort() int {
  125. // returns between base and base + spread
  126. base, spread := 20000, 20000
  127. return base + tmrand.Intn(spread)
  128. }
  129. func makeAddrs() (string, string, string) {
  130. start := randPort()
  131. return fmt.Sprintf("tcp://127.0.0.1:%d", start),
  132. fmt.Sprintf("tcp://127.0.0.1:%d", start+1),
  133. fmt.Sprintf("tcp://127.0.0.1:%d", start+2)
  134. }
  135. // getConfig returns a config for test cases
  136. func getConfig(t *testing.T) *cfg.Config {
  137. c := cfg.ResetTestRoot(t.Name())
  138. // and we use random ports to run in parallel
  139. tm, rpc, grpc := makeAddrs()
  140. c.P2P.ListenAddress = tm
  141. c.RPC.ListenAddress = rpc
  142. c.RPC.GRPCListenAddress = grpc
  143. return c
  144. }
  145. // byteBufferWAL is a WAL which writes all msgs to a byte buffer. Writing stops
  146. // when the heightToStop is reached. Client will be notified via
  147. // signalWhenStopsTo channel.
  148. type byteBufferWAL struct {
  149. enc *WALEncoder
  150. stopped bool
  151. heightToStop int64
  152. signalWhenStopsTo chan<- struct{}
  153. logger log.Logger
  154. }
  155. // needed for determinism
  156. var fixedTime, _ = time.Parse(time.RFC3339, "2017-01-02T15:04:05Z")
  157. func newByteBufferWAL(logger log.Logger, enc *WALEncoder, nBlocks int64, signalStop chan<- struct{}) *byteBufferWAL {
  158. return &byteBufferWAL{
  159. enc: enc,
  160. heightToStop: nBlocks,
  161. signalWhenStopsTo: signalStop,
  162. logger: logger,
  163. }
  164. }
  165. // Save writes message to the internal buffer except when heightToStop is
  166. // reached, in which case it will signal the caller via signalWhenStopsTo and
  167. // skip writing.
  168. func (w *byteBufferWAL) Write(m tmcon.WALMessage) error {
  169. if w.stopped {
  170. w.logger.Debug("WAL already stopped. Not writing message", "msg", m)
  171. return nil
  172. }
  173. if endMsg, ok := m.(tmcon.EndHeightMessage); ok {
  174. w.logger.Debug("WAL write end height message", "height", endMsg.Height, "stopHeight", w.heightToStop)
  175. if endMsg.Height == w.heightToStop {
  176. w.logger.Debug("Stopping WAL at height", "height", endMsg.Height)
  177. w.signalWhenStopsTo <- struct{}{}
  178. w.stopped = true
  179. return nil
  180. }
  181. }
  182. w.logger.Debug("WAL Write Message", "msg", m)
  183. err := w.enc.Encode(&tmcon.TimedWALMessage{Time: fixedTime, Msg: m})
  184. if err != nil {
  185. panic(fmt.Sprintf("failed to encode the msg %v", m))
  186. }
  187. return nil
  188. }
  189. func (w *byteBufferWAL) WriteSync(m tmcon.WALMessage) error {
  190. return w.Write(m)
  191. }
  192. func (w *byteBufferWAL) FlushAndSync() error { return nil }
  193. func (w *byteBufferWAL) SearchForEndHeight(
  194. height int64,
  195. options *tmcon.WALSearchOptions) (rd io.ReadCloser, found bool, err error) {
  196. return nil, false, nil
  197. }
  198. func (w *byteBufferWAL) Start() error { return nil }
  199. func (w *byteBufferWAL) Stop() error { return nil }
  200. func (w *byteBufferWAL) Wait() {}