bitbucket.org/number571/tendermint@v0.8.14/internal/consensus/wal_generator.go (about)

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