github.com/Finschia/ostracon@v1.1.5/consensus/wal_generator.go (about)

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