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 }