package blockchain import ( "bytes" "testing" wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) func newBlockchainReactor(logger log.Logger, maxBlockHeight int) *BlockchainReactor { config := cfg.ResetTestRoot("node_node_test") blockStoreDB := db.NewDB("blockstore", config.DBBackend, config.DBDir()) blockStore := NewBlockStore(blockStoreDB) stateLogger := logger.With("module", "state") // Get State stateDB := db.NewDB("state", config.DBBackend, config.DBDir()) state, _ := sm.GetState(stateDB, config.GenesisFile()) state.SetLogger(stateLogger) state.Save() // Make the blockchainReactor itself fastSync := true bcReactor := NewBlockchainReactor(state.Copy(), nil, blockStore, fastSync) // Next: we need to set a switch in order for peers to be added in bcReactor.Switch = p2p.NewSwitch(cfg.DefaultP2PConfig()) bcReactor.SetLogger(logger.With("module", "blockchain")) // Lastly: let's add some blocks in for blockHeight := 1; blockHeight <= maxBlockHeight; blockHeight++ { firstBlock := makeBlock(blockHeight, state) secondBlock := makeBlock(blockHeight+1, state) firstParts := firstBlock.MakePartSet(state.Params().BlockGossipParams.BlockPartSizeBytes) blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit) } return bcReactor } func TestNoBlockMessageResponse(t *testing.T) { logBuf := new(bytes.Buffer) logger := log.NewTMLogger(logBuf) maxBlockHeight := 20 bcr := newBlockchainReactor(logger, maxBlockHeight) go bcr.OnStart() defer bcr.Stop() // Add some peers in peer := newbcrTestPeer(cmn.RandStr(12)) bcr.AddPeer(peer) chID := byte(0x01) tests := []struct { height int existent bool }{ {maxBlockHeight + 2, false}, {10, true}, {1, true}, {100, false}, } for _, tt := range tests { reqBlockMsg := &bcBlockRequestMessage{tt.height} reqBlockBytes := wire.BinaryBytes(struct{ BlockchainMessage }{reqBlockMsg}) bcr.Receive(chID, peer, reqBlockBytes) value := peer.lastValue() msg := value.(struct{ BlockchainMessage }).BlockchainMessage if tt.existent { if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok { t.Fatalf("Expected to receive a block response for height %d", tt.height) } else if blockMsg.Block.Height != tt.height { t.Fatalf("Expected response to be for height %d, got %d", tt.height, blockMsg.Block.Height) } } else { if noBlockMsg, ok := msg.(*bcNoBlockResponseMessage); !ok { t.Fatalf("Expected to receive a no block response for height %d", tt.height) } else if noBlockMsg.Height != tt.height { t.Fatalf("Expected response to be for height %d, got %d", tt.height, noBlockMsg.Height) } } } } //---------------------------------------------- // utility funcs func makeTxs(blockNumber int) (txs []types.Tx) { for i := 0; i < 10; i++ { txs = append(txs, types.Tx([]byte{byte(blockNumber), byte(i)})) } return txs } func makeBlock(blockNumber int, state *sm.State) *types.Block { prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} valHash := state.Validators.Hash() prevBlockID := types.BlockID{prevHash, prevParts} block, _ := types.MakeBlock(blockNumber, "test_chain", makeTxs(blockNumber), new(types.Commit), prevBlockID, valHash, state.AppHash, state.Params().BlockGossipParams.BlockPartSizeBytes) return block } // The Test peer type bcrTestPeer struct { cmn.Service key string ch chan interface{} } var _ p2p.Peer = (*bcrTestPeer)(nil) func newbcrTestPeer(key string) *bcrTestPeer { return &bcrTestPeer{ Service: cmn.NewBaseService(nil, "bcrTestPeer", nil), key: key, ch: make(chan interface{}, 2), } } func (tp *bcrTestPeer) lastValue() interface{} { return <-tp.ch } func (tp *bcrTestPeer) TrySend(chID byte, value interface{}) bool { if _, ok := value.(struct{ BlockchainMessage }).BlockchainMessage.(*bcStatusResponseMessage); ok { // Discard status response messages since they skew our results // We only want to deal with: // + bcBlockResponseMessage // + bcNoBlockResponseMessage } else { tp.ch <- value } return true } func (tp *bcrTestPeer) Send(chID byte, data interface{}) bool { return tp.TrySend(chID, data) } func (tp *bcrTestPeer) NodeInfo() *p2p.NodeInfo { return nil } func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} } func (tp *bcrTestPeer) Key() string { return tp.key } func (tp *bcrTestPeer) IsOutbound() bool { return false } func (tp *bcrTestPeer) IsPersistent() bool { return true } func (tp *bcrTestPeer) Get(s string) interface{} { return s } func (tp *bcrTestPeer) Set(string, interface{}) {}