github.com/noirx94/tendermintmp@v0.0.1/blockchain/v0/reactor_test.go (about) 1 package v0 2 3 import ( 4 "fmt" 5 "os" 6 "sort" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 dbm "github.com/tendermint/tm-db" 14 15 abci "github.com/tendermint/tendermint/abci/types" 16 cfg "github.com/tendermint/tendermint/config" 17 "github.com/tendermint/tendermint/libs/log" 18 "github.com/tendermint/tendermint/mempool/mock" 19 "github.com/tendermint/tendermint/p2p" 20 "github.com/tendermint/tendermint/proxy" 21 sm "github.com/tendermint/tendermint/state" 22 "github.com/tendermint/tendermint/store" 23 "github.com/tendermint/tendermint/types" 24 tmtime "github.com/tendermint/tendermint/types/time" 25 ) 26 27 var config *cfg.Config 28 29 func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) { 30 validators := make([]types.GenesisValidator, numValidators) 31 privValidators := make([]types.PrivValidator, numValidators) 32 for i := 0; i < numValidators; i++ { 33 val, privVal := types.RandValidator(randPower, minPower) 34 validators[i] = types.GenesisValidator{ 35 PubKey: val.PubKey, 36 Power: val.VotingPower, 37 } 38 privValidators[i] = privVal 39 } 40 sort.Sort(types.PrivValidatorsByAddress(privValidators)) 41 42 return &types.GenesisDoc{ 43 GenesisTime: tmtime.Now(), 44 ChainID: config.ChainID(), 45 Validators: validators, 46 }, privValidators 47 } 48 49 type BlockchainReactorPair struct { 50 reactor *BlockchainReactor 51 app proxy.AppConns 52 } 53 54 func newBlockchainReactor( 55 logger log.Logger, 56 genDoc *types.GenesisDoc, 57 privVals []types.PrivValidator, 58 maxBlockHeight int64) BlockchainReactorPair { 59 if len(privVals) != 1 { 60 panic("only support one validator") 61 } 62 63 app := &testApp{} 64 cc := proxy.NewLocalClientCreator(app) 65 proxyApp := proxy.NewAppConns(cc) 66 err := proxyApp.Start() 67 if err != nil { 68 panic(fmt.Errorf("error start app: %w", err)) 69 } 70 71 blockDB := dbm.NewMemDB() 72 stateDB := dbm.NewMemDB() 73 stateStore := sm.NewStore(stateDB) 74 blockStore := store.NewBlockStore(blockDB) 75 76 state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) 77 if err != nil { 78 panic(fmt.Errorf("error constructing state from genesis file: %w", err)) 79 } 80 81 // Make the BlockchainReactor itself. 82 // NOTE we have to create and commit the blocks first because 83 // pool.height is determined from the store. 84 fastSync := true 85 db := dbm.NewMemDB() 86 stateStore = sm.NewStore(db) 87 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), 88 mock.Mempool{}, sm.EmptyEvidencePool{}) 89 if err = stateStore.Save(state); err != nil { 90 panic(err) 91 } 92 93 // let's add some blocks in 94 for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { 95 lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil) 96 if blockHeight > 1 { 97 lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) 98 lastBlock := blockStore.LoadBlock(blockHeight - 1) 99 100 vote, err := types.MakeVote( 101 lastBlock.Header.Height, 102 lastBlockMeta.BlockID, 103 state.Validators, 104 privVals[0], 105 lastBlock.Header.ChainID, 106 time.Now(), 107 ) 108 if err != nil { 109 panic(err) 110 } 111 lastCommit = types.NewCommit(vote.Height, vote.Round, 112 lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) 113 } 114 115 thisBlock := makeBlock(blockHeight, state, lastCommit) 116 117 thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) 118 blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()} 119 120 state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) 121 if err != nil { 122 panic(fmt.Errorf("error apply block: %w", err)) 123 } 124 125 blockStore.SaveBlock(thisBlock, thisParts, lastCommit) 126 } 127 128 bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) 129 bcReactor.SetLogger(logger.With("module", "blockchain")) 130 131 return BlockchainReactorPair{bcReactor, proxyApp} 132 } 133 134 func TestNoBlockResponse(t *testing.T) { 135 config = cfg.ResetTestRoot("blockchain_reactor_test") 136 defer os.RemoveAll(config.RootDir) 137 genDoc, privVals := randGenesisDoc(1, false, 30) 138 139 maxBlockHeight := int64(65) 140 141 reactorPairs := make([]BlockchainReactorPair, 2) 142 143 reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) 144 reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 145 146 p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch { 147 s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) 148 return s 149 150 }, p2p.Connect2Switches) 151 152 defer func() { 153 for _, r := range reactorPairs { 154 err := r.reactor.Stop() 155 require.NoError(t, err) 156 err = r.app.Stop() 157 require.NoError(t, err) 158 } 159 }() 160 161 tests := []struct { 162 height int64 163 existent bool 164 }{ 165 {maxBlockHeight + 2, false}, 166 {10, true}, 167 {1, true}, 168 {100, false}, 169 } 170 171 for { 172 if reactorPairs[1].reactor.pool.IsCaughtUp() { 173 break 174 } 175 176 time.Sleep(10 * time.Millisecond) 177 } 178 179 assert.Equal(t, maxBlockHeight, reactorPairs[0].reactor.store.Height()) 180 181 for _, tt := range tests { 182 block := reactorPairs[1].reactor.store.LoadBlock(tt.height) 183 if tt.existent { 184 assert.True(t, block != nil) 185 } else { 186 assert.True(t, block == nil) 187 } 188 } 189 } 190 191 // NOTE: This is too hard to test without 192 // an easy way to add test peer to switch 193 // or without significant refactoring of the module. 194 // Alternatively we could actually dial a TCP conn but 195 // that seems extreme. 196 func TestBadBlockStopsPeer(t *testing.T) { 197 config = cfg.ResetTestRoot("blockchain_reactor_test") 198 defer os.RemoveAll(config.RootDir) 199 genDoc, privVals := randGenesisDoc(1, false, 30) 200 201 maxBlockHeight := int64(148) 202 203 // Other chain needs a different validator set 204 otherGenDoc, otherPrivVals := randGenesisDoc(1, false, 30) 205 otherChain := newBlockchainReactor(log.TestingLogger(), otherGenDoc, otherPrivVals, maxBlockHeight) 206 207 defer func() { 208 err := otherChain.reactor.Stop() 209 require.Error(t, err) 210 err = otherChain.app.Stop() 211 require.NoError(t, err) 212 }() 213 214 reactorPairs := make([]BlockchainReactorPair, 4) 215 216 reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) 217 reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 218 reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 219 reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 220 221 switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch { 222 s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) 223 return s 224 225 }, p2p.Connect2Switches) 226 227 defer func() { 228 for _, r := range reactorPairs { 229 err := r.reactor.Stop() 230 require.NoError(t, err) 231 232 err = r.app.Stop() 233 require.NoError(t, err) 234 } 235 }() 236 237 for { 238 time.Sleep(1 * time.Second) 239 caughtUp := true 240 for _, r := range reactorPairs { 241 if !r.reactor.pool.IsCaughtUp() { 242 caughtUp = false 243 } 244 } 245 if caughtUp { 246 break 247 } 248 } 249 250 // at this time, reactors[0-3] is the newest 251 assert.Equal(t, 3, reactorPairs[1].reactor.Switch.Peers().Size()) 252 253 // Mark reactorPairs[3] as an invalid peer. Fiddling with .store without a mutex is a data 254 // race, but can't be easily avoided. 255 reactorPairs[3].reactor.store = otherChain.reactor.store 256 257 lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 258 reactorPairs = append(reactorPairs, lastReactorPair) 259 260 switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch { 261 s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].reactor) 262 return s 263 264 }, p2p.Connect2Switches)...) 265 266 for i := 0; i < len(reactorPairs)-1; i++ { 267 p2p.Connect2Switches(switches, i, len(reactorPairs)-1) 268 } 269 270 for { 271 if lastReactorPair.reactor.pool.IsCaughtUp() || lastReactorPair.reactor.Switch.Peers().Size() == 0 { 272 break 273 } 274 275 time.Sleep(1 * time.Second) 276 } 277 278 assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1) 279 } 280 281 //---------------------------------------------- 282 // utility funcs 283 284 func makeTxs(height int64) (txs []types.Tx) { 285 for i := 0; i < 10; i++ { 286 txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) 287 } 288 return txs 289 } 290 291 func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { 292 block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) 293 return block 294 } 295 296 type testApp struct { 297 abci.BaseApplication 298 } 299 300 var _ abci.Application = (*testApp)(nil) 301 302 func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { 303 return abci.ResponseInfo{} 304 } 305 306 func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { 307 return abci.ResponseBeginBlock{} 308 } 309 310 func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { 311 return abci.ResponseEndBlock{} 312 } 313 314 func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { 315 return abci.ResponseDeliverTx{Events: []abci.Event{}} 316 } 317 318 func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { 319 return abci.ResponseCheckTx{} 320 } 321 322 func (app *testApp) Commit() abci.ResponseCommit { 323 return abci.ResponseCommit{} 324 } 325 326 func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { 327 return 328 }