github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/tendermint/consensus/wal_generator.go (about)

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