github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/blockchain/v1/reactor_test.go (about) 1 package v1 2 3 import ( 4 "fmt" 5 "os" 6 "sort" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 dbm "github.com/tendermint/tm-db" 15 16 abci "github.com/tendermint/tendermint/abci/types" 17 cfg "github.com/tendermint/tendermint/config" 18 "github.com/tendermint/tendermint/libs/log" 19 "github.com/tendermint/tendermint/mempool/mock" 20 "github.com/tendermint/tendermint/p2p" 21 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 22 "github.com/tendermint/tendermint/proxy" 23 sm "github.com/tendermint/tendermint/state" 24 "github.com/tendermint/tendermint/store" 25 "github.com/tendermint/tendermint/types" 26 tmtime "github.com/tendermint/tendermint/types/time" 27 ) 28 29 var config *cfg.Config 30 31 func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) { 32 validators := make([]types.GenesisValidator, numValidators) 33 privValidators := make([]types.PrivValidator, numValidators) 34 for i := 0; i < numValidators; i++ { 35 val, privVal := types.RandValidator(randPower, minPower) 36 validators[i] = types.GenesisValidator{ 37 PubKey: val.PubKey, 38 Power: val.VotingPower, 39 } 40 privValidators[i] = privVal 41 } 42 sort.Sort(types.PrivValidatorsByAddress(privValidators)) 43 44 return &types.GenesisDoc{ 45 GenesisTime: tmtime.Now(), 46 ChainID: config.ChainID(), 47 Validators: validators, 48 }, privValidators 49 } 50 51 func makeVote( 52 t *testing.T, 53 header *types.Header, 54 blockID types.BlockID, 55 valset *types.ValidatorSet, 56 privVal types.PrivValidator) *types.Vote { 57 58 pubKey, err := privVal.GetPubKey() 59 require.NoError(t, err) 60 61 valIdx, _ := valset.GetByAddress(pubKey.Address()) 62 vote := &types.Vote{ 63 ValidatorAddress: pubKey.Address(), 64 ValidatorIndex: valIdx, 65 Height: header.Height, 66 Round: 1, 67 Timestamp: tmtime.Now(), 68 Type: tmproto.PrecommitType, 69 BlockID: blockID, 70 } 71 72 vpb := vote.ToProto() 73 74 _ = privVal.SignVote(header.ChainID, vpb) 75 vote.Signature = vpb.Signature 76 77 return vote 78 } 79 80 type BlockchainReactorPair struct { 81 bcR *BlockchainReactor 82 conR *consensusReactorTest 83 } 84 85 func newBlockchainReactor( 86 t *testing.T, 87 logger log.Logger, 88 genDoc *types.GenesisDoc, 89 privVals []types.PrivValidator, 90 maxBlockHeight int64) *BlockchainReactor { 91 if len(privVals) != 1 { 92 panic("only support one validator") 93 } 94 95 app := &testApp{} 96 cc := proxy.NewLocalClientCreator(app) 97 proxyApp := proxy.NewAppConns(cc) 98 err := proxyApp.Start() 99 if err != nil { 100 panic(fmt.Errorf("error start app: %w", err)) 101 } 102 103 blockDB := dbm.NewMemDB() 104 stateDB := dbm.NewMemDB() 105 blockStore := store.NewBlockStore(blockDB) 106 107 state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) 108 if err != nil { 109 panic(fmt.Errorf("error constructing state from genesis file: %w", err)) 110 } 111 112 // Make the BlockchainReactor itself. 113 // NOTE we have to create and commit the blocks first because 114 // pool.height is determined from the store. 115 fastSync := true 116 db := dbm.NewMemDB() 117 blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(), 118 mock.Mempool{}, sm.MockEvidencePool{}) 119 sm.SaveState(db, state) 120 121 // let's add some blocks in 122 for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { 123 lastCommit := types.NewCommit(blockHeight-1, 1, types.BlockID{}, nil) 124 if blockHeight > 1 { 125 lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) 126 lastBlock := blockStore.LoadBlock(blockHeight - 1) 127 128 vote := makeVote(t, &lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]) 129 lastCommit = types.NewCommit(vote.Height, vote.Round, lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) 130 } 131 132 thisBlock := makeBlock(blockHeight, state, lastCommit) 133 134 thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) 135 blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()} 136 137 state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) 138 if err != nil { 139 panic(fmt.Errorf("error apply block: %w", err)) 140 } 141 142 blockStore.SaveBlock(thisBlock, thisParts, lastCommit) 143 } 144 145 bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) 146 bcReactor.SetLogger(logger.With("module", "blockchain")) 147 148 return bcReactor 149 } 150 151 func newBlockchainReactorPair( 152 t *testing.T, 153 logger log.Logger, 154 genDoc *types.GenesisDoc, 155 privVals []types.PrivValidator, 156 maxBlockHeight int64) BlockchainReactorPair { 157 158 consensusReactor := &consensusReactorTest{} 159 consensusReactor.BaseReactor = *p2p.NewBaseReactor("Consensus reactor", consensusReactor) 160 161 return BlockchainReactorPair{ 162 newBlockchainReactor(t, logger, genDoc, privVals, maxBlockHeight), 163 consensusReactor} 164 } 165 166 type consensusReactorTest struct { 167 p2p.BaseReactor // BaseService + p2p.Switch 168 switchedToConsensus bool 169 mtx sync.Mutex 170 } 171 172 func (conR *consensusReactorTest) SwitchToConsensus(state sm.State, blocksSynced bool) { 173 conR.mtx.Lock() 174 defer conR.mtx.Unlock() 175 conR.switchedToConsensus = true 176 } 177 178 func TestFastSyncNoBlockResponse(t *testing.T) { 179 180 config = cfg.ResetTestRoot("blockchain_new_reactor_test") 181 defer os.RemoveAll(config.RootDir) 182 genDoc, privVals := randGenesisDoc(1, false, 30) 183 184 maxBlockHeight := int64(65) 185 186 reactorPairs := make([]BlockchainReactorPair, 2) 187 188 logger := log.TestingLogger() 189 reactorPairs[0] = newBlockchainReactorPair(t, logger, genDoc, privVals, maxBlockHeight) 190 reactorPairs[1] = newBlockchainReactorPair(t, logger, genDoc, privVals, 0) 191 192 p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch { 193 s.AddReactor("BLOCKCHAIN", reactorPairs[i].bcR) 194 s.AddReactor("CONSENSUS", reactorPairs[i].conR) 195 moduleName := fmt.Sprintf("blockchain-%v", i) 196 reactorPairs[i].bcR.SetLogger(logger.With("module", moduleName)) 197 198 return s 199 200 }, p2p.Connect2Switches) 201 202 defer func() { 203 for _, r := range reactorPairs { 204 _ = r.bcR.Stop() 205 _ = r.conR.Stop() 206 } 207 }() 208 209 tests := []struct { 210 height int64 211 existent bool 212 }{ 213 {maxBlockHeight + 2, false}, 214 {10, true}, 215 {1, true}, 216 {maxBlockHeight + 100, false}, 217 } 218 219 for { 220 time.Sleep(10 * time.Millisecond) 221 reactorPairs[1].conR.mtx.Lock() 222 if reactorPairs[1].conR.switchedToConsensus { 223 reactorPairs[1].conR.mtx.Unlock() 224 break 225 } 226 reactorPairs[1].conR.mtx.Unlock() 227 } 228 229 assert.Equal(t, maxBlockHeight, reactorPairs[0].bcR.store.Height()) 230 231 for _, tt := range tests { 232 block := reactorPairs[1].bcR.store.LoadBlock(tt.height) 233 if tt.existent { 234 assert.True(t, block != nil) 235 } else { 236 assert.True(t, block == nil) 237 } 238 } 239 } 240 241 // NOTE: This is too hard to test without 242 // an easy way to add test peer to switch 243 // or without significant refactoring of the module. 244 // Alternatively we could actually dial a TCP conn but 245 // that seems extreme. 246 func TestFastSyncBadBlockStopsPeer(t *testing.T) { 247 numNodes := 4 248 maxBlockHeight := int64(148) 249 250 config = cfg.ResetTestRoot("blockchain_reactor_test") 251 defer os.RemoveAll(config.RootDir) 252 genDoc, privVals := randGenesisDoc(1, false, 30) 253 254 otherChain := newBlockchainReactorPair(t, log.TestingLogger(), genDoc, privVals, maxBlockHeight) 255 defer func() { 256 _ = otherChain.bcR.Stop() 257 _ = otherChain.conR.Stop() 258 }() 259 260 reactorPairs := make([]BlockchainReactorPair, numNodes) 261 logger := make([]log.Logger, numNodes) 262 263 for i := 0; i < numNodes; i++ { 264 logger[i] = log.TestingLogger() 265 height := int64(0) 266 if i == 0 { 267 height = maxBlockHeight 268 } 269 reactorPairs[i] = newBlockchainReactorPair(t, logger[i], genDoc, privVals, height) 270 } 271 272 switches := p2p.MakeConnectedSwitches(config.P2P, numNodes, func(i int, s *p2p.Switch) *p2p.Switch { 273 reactorPairs[i].conR.mtx.Lock() 274 s.AddReactor("BLOCKCHAIN", reactorPairs[i].bcR) 275 s.AddReactor("CONSENSUS", reactorPairs[i].conR) 276 moduleName := fmt.Sprintf("blockchain-%v", i) 277 reactorPairs[i].bcR.SetLogger(logger[i].With("module", moduleName)) 278 reactorPairs[i].conR.mtx.Unlock() 279 return s 280 281 }, p2p.Connect2Switches) 282 283 defer func() { 284 for _, r := range reactorPairs { 285 _ = r.bcR.Stop() 286 _ = r.conR.Stop() 287 } 288 }() 289 290 outerFor: 291 for { 292 time.Sleep(10 * time.Millisecond) 293 for i := 0; i < numNodes; i++ { 294 reactorPairs[i].conR.mtx.Lock() 295 if !reactorPairs[i].conR.switchedToConsensus { 296 reactorPairs[i].conR.mtx.Unlock() 297 continue outerFor 298 } 299 reactorPairs[i].conR.mtx.Unlock() 300 } 301 break 302 } 303 304 //at this time, reactors[0-3] is the newest 305 assert.Equal(t, numNodes-1, reactorPairs[1].bcR.Switch.Peers().Size()) 306 307 //mark last reactorPair as an invalid peer 308 reactorPairs[numNodes-1].bcR.store = otherChain.bcR.store 309 310 lastLogger := log.TestingLogger() 311 lastReactorPair := newBlockchainReactorPair(t, lastLogger, genDoc, privVals, 0) 312 reactorPairs = append(reactorPairs, lastReactorPair) 313 314 switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch { 315 s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].bcR) 316 s.AddReactor("CONSENSUS", reactorPairs[len(reactorPairs)-1].conR) 317 moduleName := fmt.Sprintf("blockchain-%v", len(reactorPairs)-1) 318 reactorPairs[len(reactorPairs)-1].bcR.SetLogger(lastLogger.With("module", moduleName)) 319 return s 320 321 }, p2p.Connect2Switches)...) 322 323 for i := 0; i < len(reactorPairs)-1; i++ { 324 p2p.Connect2Switches(switches, i, len(reactorPairs)-1) 325 } 326 327 for { 328 time.Sleep(1 * time.Second) 329 lastReactorPair.conR.mtx.Lock() 330 if lastReactorPair.conR.switchedToConsensus { 331 lastReactorPair.conR.mtx.Unlock() 332 break 333 } 334 lastReactorPair.conR.mtx.Unlock() 335 336 if lastReactorPair.bcR.Switch.Peers().Size() == 0 { 337 break 338 } 339 } 340 341 assert.True(t, lastReactorPair.bcR.Switch.Peers().Size() < len(reactorPairs)-1) 342 } 343 344 //---------------------------------------------- 345 // utility funcs 346 347 func makeTxs(height int64) (txs []types.Tx) { 348 for i := 0; i < 10; i++ { 349 txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) 350 } 351 return txs 352 } 353 354 func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { 355 block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) 356 return block 357 } 358 359 type testApp struct { 360 abci.BaseApplication 361 }