github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/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 blockStore := store.NewBlockStore(blockDB) 74 75 state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) 76 if err != nil { 77 panic(fmt.Errorf("error constructing state from genesis file: %w", err)) 78 } 79 80 // Make the BlockchainReactor itself. 81 // NOTE we have to create and commit the blocks first because 82 // pool.height is determined from the store. 83 fastSync := true 84 db := dbm.NewMemDB() 85 blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(), 86 mock.Mempool{}, sm.MockEvidencePool{}) 87 sm.SaveState(db, state) 88 89 // let's add some blocks in 90 for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { 91 lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil) 92 if blockHeight > 1 { 93 lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) 94 lastBlock := blockStore.LoadBlock(blockHeight - 1) 95 96 vote, err := types.MakeVote( 97 lastBlock.Header.Height, 98 lastBlockMeta.BlockID, 99 state.Validators, 100 privVals[0], 101 lastBlock.Header.ChainID, 102 time.Now(), 103 ) 104 if err != nil { 105 panic(err) 106 } 107 lastCommit = types.NewCommit(vote.Height, vote.Round, 108 lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) 109 } 110 111 thisBlock := makeBlock(blockHeight, state, lastCommit) 112 113 thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) 114 blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()} 115 116 state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) 117 if err != nil { 118 panic(fmt.Errorf("error apply block: %w", err)) 119 } 120 121 blockStore.SaveBlock(thisBlock, thisParts, lastCommit) 122 } 123 124 bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) 125 bcReactor.SetLogger(logger.With("module", "blockchain")) 126 127 return BlockchainReactorPair{bcReactor, proxyApp} 128 } 129 130 func TestNoBlockResponse(t *testing.T) { 131 config = cfg.ResetTestRoot("blockchain_reactor_test") 132 defer os.RemoveAll(config.RootDir) 133 genDoc, privVals := randGenesisDoc(1, false, 30) 134 135 maxBlockHeight := int64(65) 136 137 reactorPairs := make([]BlockchainReactorPair, 2) 138 139 reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) 140 reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 141 142 p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch { 143 s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) 144 return s 145 146 }, p2p.Connect2Switches) 147 148 defer func() { 149 for _, r := range reactorPairs { 150 err := r.reactor.Stop() 151 require.NoError(t, err) 152 err = r.app.Stop() 153 require.NoError(t, err) 154 } 155 }() 156 157 tests := []struct { 158 height int64 159 existent bool 160 }{ 161 {maxBlockHeight + 2, false}, 162 {10, true}, 163 {1, true}, 164 {100, false}, 165 } 166 167 for { 168 if reactorPairs[1].reactor.pool.IsCaughtUp() { 169 break 170 } 171 172 time.Sleep(10 * time.Millisecond) 173 } 174 175 assert.Equal(t, maxBlockHeight, reactorPairs[0].reactor.store.Height()) 176 177 for _, tt := range tests { 178 block := reactorPairs[1].reactor.store.LoadBlock(tt.height) 179 if tt.existent { 180 assert.True(t, block != nil) 181 } else { 182 assert.True(t, block == nil) 183 } 184 } 185 } 186 187 // NOTE: This is too hard to test without 188 // an easy way to add test peer to switch 189 // or without significant refactoring of the module. 190 // Alternatively we could actually dial a TCP conn but 191 // that seems extreme. 192 func TestBadBlockStopsPeer(t *testing.T) { 193 config = cfg.ResetTestRoot("blockchain_reactor_test") 194 defer os.RemoveAll(config.RootDir) 195 genDoc, privVals := randGenesisDoc(1, false, 30) 196 197 maxBlockHeight := int64(148) 198 199 otherChain := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) 200 defer func() { 201 err := otherChain.reactor.Stop() 202 require.Error(t, err) 203 err = otherChain.app.Stop() 204 require.NoError(t, err) 205 }() 206 207 reactorPairs := make([]BlockchainReactorPair, 4) 208 209 reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) 210 reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 211 reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 212 reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 213 214 switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch { 215 s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) 216 return s 217 218 }, p2p.Connect2Switches) 219 220 defer func() { 221 for _, r := range reactorPairs { 222 err := r.reactor.Stop() 223 require.NoError(t, err) 224 225 err = r.app.Stop() 226 require.NoError(t, err) 227 } 228 }() 229 230 for { 231 time.Sleep(1 * time.Second) 232 caughtUp := true 233 for _, r := range reactorPairs { 234 if !r.reactor.pool.IsCaughtUp() { 235 caughtUp = false 236 } 237 } 238 if caughtUp { 239 break 240 } 241 } 242 243 //at this time, reactors[0-3] is the newest 244 assert.Equal(t, 3, reactorPairs[1].reactor.Switch.Peers().Size()) 245 246 // Mark reactorPairs[3] as an invalid peer. Fiddling with .store without a mutex is a data 247 // race, but can't be easily avoided. 248 reactorPairs[3].reactor.store = otherChain.reactor.store 249 250 lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) 251 reactorPairs = append(reactorPairs, lastReactorPair) 252 253 switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch { 254 s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].reactor) 255 return s 256 257 }, p2p.Connect2Switches)...) 258 259 for i := 0; i < len(reactorPairs)-1; i++ { 260 p2p.Connect2Switches(switches, i, len(reactorPairs)-1) 261 } 262 263 for { 264 if lastReactorPair.reactor.pool.IsCaughtUp() || lastReactorPair.reactor.Switch.Peers().Size() == 0 { 265 break 266 } 267 268 time.Sleep(1 * time.Second) 269 } 270 271 assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1) 272 } 273 274 //---------------------------------------------- 275 // utility funcs 276 277 func makeTxs(height int64) (txs []types.Tx) { 278 for i := 0; i < 10; i++ { 279 txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) 280 } 281 return txs 282 } 283 284 func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { 285 block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) 286 return block 287 } 288 289 type testApp struct { 290 abci.BaseApplication 291 } 292 293 var _ abci.Application = (*testApp)(nil) 294 295 func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { 296 return abci.ResponseInfo{} 297 } 298 299 func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { 300 return abci.ResponseBeginBlock{} 301 } 302 303 func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { 304 return abci.ResponseEndBlock{} 305 } 306 307 func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { 308 return abci.ResponseDeliverTx{Events: []abci.Event{}} 309 } 310 311 func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { 312 return abci.ResponseCheckTx{} 313 } 314 315 func (app *testApp) Commit() abci.ResponseCommit { 316 return abci.ResponseCommit{} 317 } 318 319 func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { 320 return 321 }