github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/consensus/wal_test.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 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 auto "github.com/gnolang/gno/tm2/pkg/autofile" 16 "github.com/gnolang/gno/tm2/pkg/bft/abci/example/kvstore" 17 "github.com/gnolang/gno/tm2/pkg/bft/appconn" 18 "github.com/gnolang/gno/tm2/pkg/bft/mempool/mock" 19 "github.com/gnolang/gno/tm2/pkg/bft/privval" 20 "github.com/gnolang/gno/tm2/pkg/bft/proxy" 21 sm "github.com/gnolang/gno/tm2/pkg/bft/state" 22 "github.com/gnolang/gno/tm2/pkg/bft/store" 23 "github.com/gnolang/gno/tm2/pkg/bft/types" 24 walm "github.com/gnolang/gno/tm2/pkg/bft/wal" 25 "github.com/gnolang/gno/tm2/pkg/db/memdb" 26 "github.com/gnolang/gno/tm2/pkg/errors" 27 "github.com/gnolang/gno/tm2/pkg/events" 28 "github.com/gnolang/gno/tm2/pkg/log" 29 ) 30 31 // ---------------------------------------- 32 // copied over from wal/wal_test.go 33 34 const maxTestMsgSize int64 = 64 * 1024 35 36 func makeTempWAL(t *testing.T, walChunkSize int64) (wal walm.WAL) { 37 t.Helper() 38 39 // Create WAL file. 40 walFile := filepath.Join(t.TempDir(), "wal") 41 42 // Create WAL. 43 wal, err := walm.NewWAL(walFile, maxTestMsgSize, auto.GroupHeadSizeLimit(walChunkSize)) 44 if err != nil { 45 panic(err) 46 } 47 err = wal.Start() 48 if err != nil { 49 panic(err) 50 } 51 52 t.Cleanup(func() { 53 // WAL cleanup. 54 wal.Stop() 55 // wait for the wal to finish shutting down so we 56 // can safely remove the directory 57 wal.Wait() 58 }) 59 60 return wal 61 } 62 63 // end copy from wal/wal_test.go 64 // ---------------------------------------- 65 66 func TestWALTruncate(t *testing.T) { 67 t.Parallel() 68 69 const walChunkSize = 409610 // 4KB 70 wal := makeTempWAL(t, walChunkSize) 71 72 wal.SetLogger(log.NewTestingLogger(t)) 73 74 type grouper interface { 75 Group() *auto.Group 76 } 77 78 // 60 block's size nearly 70K, greater than group's wal chunk filesize (4KB). 79 // When the headBuf is full, content will flush to the filesystem. 80 err := WALGenerateNBlocks(t, wal.(grouper).Group(), 60) 81 require.NoError(t, err) 82 83 time.Sleep(1 * time.Millisecond) // wait groupCheckDuration, make sure RotateFile run 84 85 wal.FlushAndSync() 86 87 h := int64(50) 88 gr, found, err := wal.SearchForHeight(h+1, &walm.WALSearchOptions{}) 89 assert.NoError(t, err, "expected not to err on height %d", h) 90 assert.True(t, found, "expected to find end height for %d", h) 91 assert.NotNil(t, gr) 92 defer gr.Close() 93 94 dec := walm.NewWALReader(gr, maxMsgSize) 95 msg, meta, err := dec.ReadMessage() 96 assert.NoError(t, err, "expected to decode a message") 97 rs, ok := msg.Msg.(newRoundStepInfo) 98 assert.Nil(t, meta, "expected no meta") 99 assert.True(t, ok, "expected message of type EventRoundState") 100 assert.Equal(t, rs.Height, h+1, "wrong height") 101 } 102 103 // XXX: WALGenerateNBlocks and WALWithNBlocks were removed from wal_generator.go 104 // as they are unused. 105 // If you intend to use them, please move them to a separate package. 106 107 // WALGenerateNBlocks generates a consensus WAL. It does this by spinning up a 108 // stripped down version of node (proxy app, event bus, consensus state) with a 109 // persistent kvstore application and special consensus wal instance 110 // (heightStopWAL) and waits until numBlocks are created. If the node fails to produce given numBlocks, it returns an error. 111 func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { 112 t.Helper() 113 114 config, genesisFile := getConfig(t) 115 116 app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator")) 117 defer app.Close() 118 119 logger := log.NewNoopLogger().With("wal_generator", "wal_generator") 120 logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks) 121 122 // ----------- 123 // COPY PASTE FROM node.go WITH A FEW MODIFICATIONS 124 // NOTE: we can't import node package because of circular dependency. 125 // NOTE: we don't do handshake so need to set state.Version.Consensus.App directly. 126 privValidatorKeyFile := config.PrivValidatorKeyFile() 127 privValidatorStateFile := config.PrivValidatorStateFile() 128 privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile) 129 genDoc, err := types.GenesisDocFromFile(genesisFile) 130 if err != nil { 131 return errors.Wrap(err, "failed to read genesis file") 132 } 133 blockStoreDB := memdb.NewMemDB() 134 stateDB := blockStoreDB 135 state, err := sm.MakeGenesisState(genDoc) 136 if err != nil { 137 return errors.Wrap(err, "failed to make genesis state") 138 } 139 state.AppVersion = kvstore.AppVersion 140 sm.SaveState(stateDB, state) 141 blockStore := store.NewBlockStore(blockStoreDB) 142 143 proxyApp := appconn.NewAppConns(proxy.NewLocalClientCreator(app)) 144 proxyApp.SetLogger(logger.With("module", "proxy")) 145 if err := proxyApp.Start(); err != nil { 146 return errors.Wrap(err, "failed to start proxy app connections") 147 } 148 defer proxyApp.Stop() 149 150 evsw := events.NewEventSwitch() 151 evsw.SetLogger(logger.With("module", "events")) 152 if err := evsw.Start(); err != nil { 153 return errors.Wrap(err, "failed to start event bus") 154 } 155 defer evsw.Stop() 156 mempool := mock.Mempool{} 157 blockExec := sm.NewBlockExecutor(stateDB, log.NewNoopLogger(), proxyApp.Consensus(), mempool) 158 consensusState := NewConsensusState(config.Consensus, state.Copy(), blockExec, blockStore, mempool) 159 consensusState.SetLogger(logger) 160 consensusState.SetEventSwitch(evsw) 161 if privValidator != nil { 162 consensusState.SetPrivValidator(privValidator) 163 } 164 // END OF COPY PASTE 165 // ----------- 166 167 // set consensus wal to buffered WAL, which will write all incoming msgs to buffer 168 numBlocksWritten := make(chan struct{}) 169 wal := newHeightStopWAL(logger, walm.NewWALWriter(wr, maxMsgSize), int64(numBlocks)+1, numBlocksWritten) 170 // See wal.go OnStart(). 171 // Since we separate the WALWriter from the WAL, we need to 172 // initialize ourself. 173 wal.WriteMetaSync(walm.MetaMessage{Height: 1}) 174 consensusState.wal = wal 175 176 if err := consensusState.Start(); err != nil { 177 return errors.Wrap(err, "failed to start consensus state") 178 } 179 180 select { 181 case <-numBlocksWritten: 182 consensusState.Stop() 183 return nil 184 case <-time.After(2 * time.Minute): 185 consensusState.Stop() 186 return fmt.Errorf("waited too long for tendermint to produce %d blocks (grep logs for `wal_generator`)", numBlocks) 187 } 188 } 189 190 // WALWithNBlocks returns a WAL content with numBlocks. 191 func WALWithNBlocks(t *testing.T, numBlocks int) (data []byte, err error) { 192 t.Helper() 193 194 var b bytes.Buffer 195 wr := bufio.NewWriter(&b) 196 197 if err := WALGenerateNBlocks(t, wr, numBlocks); err != nil { 198 return []byte{}, err 199 } 200 201 wr.Flush() 202 return b.Bytes(), nil 203 }