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