github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/blockchain/v2/reactor_test.go (about)

     1  package v2
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"sort"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	dbm "github.com/tendermint/tm-db"
    15  
    16  	abci "github.com/tendermint/tendermint/abci/types"
    17  	"github.com/tendermint/tendermint/behaviour"
    18  	bc "github.com/tendermint/tendermint/blockchain"
    19  	cfg "github.com/tendermint/tendermint/config"
    20  	"github.com/tendermint/tendermint/libs/log"
    21  	"github.com/tendermint/tendermint/libs/service"
    22  	"github.com/tendermint/tendermint/mempool/mock"
    23  	"github.com/tendermint/tendermint/p2p"
    24  	"github.com/tendermint/tendermint/p2p/conn"
    25  	bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
    26  	"github.com/tendermint/tendermint/proxy"
    27  	sm "github.com/tendermint/tendermint/state"
    28  	"github.com/tendermint/tendermint/store"
    29  	"github.com/tendermint/tendermint/types"
    30  	tmtime "github.com/tendermint/tendermint/types/time"
    31  )
    32  
    33  type mockPeer struct {
    34  	service.Service
    35  	id p2p.ID
    36  }
    37  
    38  func (mp mockPeer) FlushStop()           {}
    39  func (mp mockPeer) ID() p2p.ID           { return mp.id }
    40  func (mp mockPeer) RemoteIP() net.IP     { return net.IP{} }
    41  func (mp mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.RemoteIP(), Port: 8800} }
    42  
    43  func (mp mockPeer) IsOutbound() bool   { return true }
    44  func (mp mockPeer) IsPersistent() bool { return true }
    45  func (mp mockPeer) CloseConn() error   { return nil }
    46  
    47  func (mp mockPeer) NodeInfo() p2p.NodeInfo {
    48  	return p2p.DefaultNodeInfo{
    49  		DefaultNodeID: "",
    50  		ListenAddr:    "",
    51  	}
    52  }
    53  func (mp mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} }
    54  func (mp mockPeer) SocketAddr() *p2p.NetAddress   { return &p2p.NetAddress{} }
    55  
    56  func (mp mockPeer) Send(byte, []byte) bool    { return true }
    57  func (mp mockPeer) TrySend(byte, []byte) bool { return true }
    58  
    59  func (mp mockPeer) Set(string, interface{}) {}
    60  func (mp mockPeer) Get(string) interface{}  { return struct{}{} }
    61  
    62  //nolint:unused
    63  type mockBlockStore struct {
    64  	blocks map[int64]*types.Block
    65  }
    66  
    67  func (ml *mockBlockStore) Height() int64 {
    68  	return int64(len(ml.blocks))
    69  }
    70  
    71  func (ml *mockBlockStore) LoadBlock(height int64) *types.Block {
    72  	return ml.blocks[height]
    73  }
    74  
    75  func (ml *mockBlockStore) SaveBlock(block *types.Block, part *types.PartSet, commit *types.Commit) {
    76  	ml.blocks[block.Height] = block
    77  }
    78  
    79  type mockBlockApplier struct {
    80  }
    81  
    82  // XXX: Add whitelist/blacklist?
    83  func (mba *mockBlockApplier) ApplyBlock(
    84  	state sm.State, blockID types.BlockID, block *types.Block,
    85  ) (sm.State, int64, error) {
    86  	state.LastBlockHeight++
    87  	return state, 0, nil
    88  }
    89  
    90  type mockSwitchIo struct {
    91  	mtx                 sync.Mutex
    92  	switchedToConsensus bool
    93  	numStatusResponse   int
    94  	numBlockResponse    int
    95  	numNoBlockResponse  int
    96  }
    97  
    98  func (sio *mockSwitchIo) sendBlockRequest(peerID p2p.ID, height int64) error {
    99  	return nil
   100  }
   101  
   102  func (sio *mockSwitchIo) sendStatusResponse(base, height int64, peerID p2p.ID) error {
   103  	sio.mtx.Lock()
   104  	defer sio.mtx.Unlock()
   105  	sio.numStatusResponse++
   106  	return nil
   107  }
   108  
   109  func (sio *mockSwitchIo) sendBlockToPeer(block *types.Block, peerID p2p.ID) error {
   110  	sio.mtx.Lock()
   111  	defer sio.mtx.Unlock()
   112  	sio.numBlockResponse++
   113  	return nil
   114  }
   115  
   116  func (sio *mockSwitchIo) sendBlockNotFound(height int64, peerID p2p.ID) error {
   117  	sio.mtx.Lock()
   118  	defer sio.mtx.Unlock()
   119  	sio.numNoBlockResponse++
   120  	return nil
   121  }
   122  
   123  func (sio *mockSwitchIo) trySwitchToConsensus(state sm.State, skipWAL bool) bool {
   124  	sio.mtx.Lock()
   125  	defer sio.mtx.Unlock()
   126  	sio.switchedToConsensus = true
   127  	return true
   128  }
   129  
   130  func (sio *mockSwitchIo) broadcastStatusRequest() error {
   131  	return nil
   132  }
   133  
   134  type testReactorParams struct {
   135  	logger      log.Logger
   136  	genDoc      *types.GenesisDoc
   137  	privVals    []types.PrivValidator
   138  	startHeight int64
   139  	mockA       bool
   140  }
   141  
   142  func newTestReactor(p testReactorParams) *BlockchainReactor {
   143  	store, state, _ := newReactorStore(p.genDoc, p.privVals, p.startHeight)
   144  	reporter := behaviour.NewMockReporter()
   145  
   146  	var appl blockApplier
   147  
   148  	if p.mockA {
   149  		appl = &mockBlockApplier{}
   150  	} else {
   151  		app := &testApp{}
   152  		cc := proxy.NewLocalClientCreator(app)
   153  		proxyApp := proxy.NewAppConns(cc)
   154  		err := proxyApp.Start()
   155  		if err != nil {
   156  			panic(fmt.Errorf("error start app: %w", err))
   157  		}
   158  		db := dbm.NewMemDB()
   159  		appl = sm.NewBlockExecutor(db, p.logger, proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
   160  		sm.SaveState(db, state)
   161  	}
   162  
   163  	r := newReactor(state, store, reporter, appl, true)
   164  	logger := log.TestingLogger()
   165  	r.SetLogger(logger.With("module", "blockchain"))
   166  
   167  	return r
   168  }
   169  
   170  // This test is left here and not deleted to retain the termination cases for
   171  // future improvement in [#4482](https://github.com/tendermint/tendermint/issues/4482).
   172  // func TestReactorTerminationScenarios(t *testing.T) {
   173  
   174  // 	config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
   175  // 	defer os.RemoveAll(config.RootDir)
   176  // 	genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
   177  // 	refStore, _, _ := newReactorStore(genDoc, privVals, 20)
   178  
   179  // 	params := testReactorParams{
   180  // 		logger:      log.TestingLogger(),
   181  // 		genDoc:      genDoc,
   182  // 		privVals:    privVals,
   183  // 		startHeight: 10,
   184  // 		bufferSize:  100,
   185  // 		mockA:       true,
   186  // 	}
   187  
   188  // 	type testEvent struct {
   189  // 		evType string
   190  // 		peer   string
   191  // 		height int64
   192  // 	}
   193  
   194  // 	tests := []struct {
   195  // 		name   string
   196  // 		params testReactorParams
   197  // 		msgs   []testEvent
   198  // 	}{
   199  // 		{
   200  // 			name:   "simple termination on max peer height - one peer",
   201  // 			params: params,
   202  // 			msgs: []testEvent{
   203  // 				{evType: "AddPeer", peer: "P1"},
   204  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   205  // 				{evType: "BlockReq"},
   206  // 				{evType: "ReceiveB", peer: "P1", height: 11},
   207  // 				{evType: "BlockReq"},
   208  // 				{evType: "BlockReq"},
   209  // 				{evType: "ReceiveB", peer: "P1", height: 12},
   210  // 				{evType: "Process"},
   211  // 				{evType: "ReceiveB", peer: "P1", height: 13},
   212  // 				{evType: "Process"},
   213  // 			},
   214  // 		},
   215  // 		{
   216  // 			name:   "simple termination on max peer height - two peers",
   217  // 			params: params,
   218  // 			msgs: []testEvent{
   219  // 				{evType: "AddPeer", peer: "P1"},
   220  // 				{evType: "AddPeer", peer: "P2"},
   221  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   222  // 				{evType: "ReceiveS", peer: "P2", height: 15},
   223  // 				{evType: "BlockReq"},
   224  // 				{evType: "BlockReq"},
   225  // 				{evType: "ReceiveB", peer: "P1", height: 11},
   226  // 				{evType: "ReceiveB", peer: "P2", height: 12},
   227  // 				{evType: "Process"},
   228  // 				{evType: "BlockReq"},
   229  // 				{evType: "BlockReq"},
   230  // 				{evType: "ReceiveB", peer: "P1", height: 13},
   231  // 				{evType: "Process"},
   232  // 				{evType: "ReceiveB", peer: "P2", height: 14},
   233  // 				{evType: "Process"},
   234  // 				{evType: "BlockReq"},
   235  // 				{evType: "ReceiveB", peer: "P2", height: 15},
   236  // 				{evType: "Process"},
   237  // 			},
   238  // 		},
   239  // 		{
   240  // 			name:   "termination on max peer height - two peers, noBlock error",
   241  // 			params: params,
   242  // 			msgs: []testEvent{
   243  // 				{evType: "AddPeer", peer: "P1"},
   244  // 				{evType: "AddPeer", peer: "P2"},
   245  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   246  // 				{evType: "ReceiveS", peer: "P2", height: 15},
   247  // 				{evType: "BlockReq"},
   248  // 				{evType: "BlockReq"},
   249  // 				{evType: "ReceiveNB", peer: "P1", height: 11},
   250  // 				{evType: "BlockReq"},
   251  // 				{evType: "ReceiveB", peer: "P2", height: 12},
   252  // 				{evType: "ReceiveB", peer: "P2", height: 11},
   253  // 				{evType: "Process"},
   254  // 				{evType: "BlockReq"},
   255  // 				{evType: "BlockReq"},
   256  // 				{evType: "ReceiveB", peer: "P2", height: 13},
   257  // 				{evType: "Process"},
   258  // 				{evType: "ReceiveB", peer: "P2", height: 14},
   259  // 				{evType: "Process"},
   260  // 				{evType: "BlockReq"},
   261  // 				{evType: "ReceiveB", peer: "P2", height: 15},
   262  // 				{evType: "Process"},
   263  // 			},
   264  // 		},
   265  // 		{
   266  // 			name:   "termination on max peer height - two peers, remove one peer",
   267  // 			params: params,
   268  // 			msgs: []testEvent{
   269  // 				{evType: "AddPeer", peer: "P1"},
   270  // 				{evType: "AddPeer", peer: "P2"},
   271  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   272  // 				{evType: "ReceiveS", peer: "P2", height: 15},
   273  // 				{evType: "BlockReq"},
   274  // 				{evType: "BlockReq"},
   275  // 				{evType: "RemovePeer", peer: "P1"},
   276  // 				{evType: "BlockReq"},
   277  // 				{evType: "ReceiveB", peer: "P2", height: 12},
   278  // 				{evType: "ReceiveB", peer: "P2", height: 11},
   279  // 				{evType: "Process"},
   280  // 				{evType: "BlockReq"},
   281  // 				{evType: "BlockReq"},
   282  // 				{evType: "ReceiveB", peer: "P2", height: 13},
   283  // 				{evType: "Process"},
   284  // 				{evType: "ReceiveB", peer: "P2", height: 14},
   285  // 				{evType: "Process"},
   286  // 				{evType: "BlockReq"},
   287  // 				{evType: "ReceiveB", peer: "P2", height: 15},
   288  // 				{evType: "Process"},
   289  // 			},
   290  // 		},
   291  // 	}
   292  
   293  // 	for _, tt := range tests {
   294  // 		tt := tt
   295  // 		t.Run(tt.name, func(t *testing.T) {
   296  // 			reactor := newTestReactor(params)
   297  // 			reactor.Start()
   298  // 			reactor.reporter = behaviour.NewMockReporter()
   299  // 			mockSwitch := &mockSwitchIo{switchedToConsensus: false}
   300  // 			reactor.io = mockSwitch
   301  // 			// time for go routines to start
   302  // 			time.Sleep(time.Millisecond)
   303  
   304  // 			for _, step := range tt.msgs {
   305  // 				switch step.evType {
   306  // 				case "AddPeer":
   307  // 					reactor.scheduler.send(bcAddNewPeer{peerID: p2p.ID(step.peer)})
   308  // 				case "RemovePeer":
   309  // 					reactor.scheduler.send(bcRemovePeer{peerID: p2p.ID(step.peer)})
   310  // 				case "ReceiveS":
   311  // 					reactor.scheduler.send(bcStatusResponse{
   312  // 						peerID: p2p.ID(step.peer),
   313  // 						height: step.height,
   314  // 						time:   time.Now(),
   315  // 					})
   316  // 				case "ReceiveB":
   317  // 					reactor.scheduler.send(bcBlockResponse{
   318  // 						peerID: p2p.ID(step.peer),
   319  // 						block:  refStore.LoadBlock(step.height),
   320  // 						size:   10,
   321  // 						time:   time.Now(),
   322  // 					})
   323  // 				case "ReceiveNB":
   324  // 					reactor.scheduler.send(bcNoBlockResponse{
   325  // 						peerID: p2p.ID(step.peer),
   326  // 						height: step.height,
   327  // 						time:   time.Now(),
   328  // 					})
   329  // 				case "BlockReq":
   330  // 					reactor.scheduler.send(rTrySchedule{time: time.Now()})
   331  // 				case "Process":
   332  // 					reactor.processor.send(rProcessBlock{})
   333  // 				}
   334  // 				// give time for messages to propagate between routines
   335  // 				time.Sleep(time.Millisecond)
   336  // 			}
   337  
   338  // 			// time for processor to finish and reactor to switch to consensus
   339  // 			time.Sleep(20 * time.Millisecond)
   340  // 			assert.True(t, mockSwitch.hasSwitchedToConsensus())
   341  // 			reactor.Stop()
   342  // 		})
   343  // 	}
   344  // }
   345  
   346  func TestReactorHelperMode(t *testing.T) {
   347  	var (
   348  		channelID = byte(0x40)
   349  	)
   350  
   351  	config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
   352  	defer os.RemoveAll(config.RootDir)
   353  	genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
   354  
   355  	params := testReactorParams{
   356  		logger:      log.TestingLogger(),
   357  		genDoc:      genDoc,
   358  		privVals:    privVals,
   359  		startHeight: 20,
   360  		mockA:       true,
   361  	}
   362  
   363  	type testEvent struct {
   364  		peer  string
   365  		event interface{}
   366  	}
   367  
   368  	tests := []struct {
   369  		name   string
   370  		params testReactorParams
   371  		msgs   []testEvent
   372  	}{
   373  		{
   374  			name:   "status request",
   375  			params: params,
   376  			msgs: []testEvent{
   377  				{"P1", bcproto.StatusRequest{}},
   378  				{"P1", bcproto.BlockRequest{Height: 13}},
   379  				{"P1", bcproto.BlockRequest{Height: 20}},
   380  				{"P1", bcproto.BlockRequest{Height: 22}},
   381  			},
   382  		},
   383  	}
   384  
   385  	for _, tt := range tests {
   386  		tt := tt
   387  		t.Run(tt.name, func(t *testing.T) {
   388  			reactor := newTestReactor(params)
   389  			mockSwitch := &mockSwitchIo{switchedToConsensus: false}
   390  			reactor.io = mockSwitch
   391  			err := reactor.Start()
   392  			require.NoError(t, err)
   393  
   394  			for i := 0; i < len(tt.msgs); i++ {
   395  				step := tt.msgs[i]
   396  				switch ev := step.event.(type) {
   397  				case bcproto.StatusRequest:
   398  					old := mockSwitch.numStatusResponse
   399  					msg, err := bc.EncodeMsg(&ev)
   400  					assert.NoError(t, err)
   401  					reactor.Receive(channelID, mockPeer{id: p2p.ID(step.peer)}, msg)
   402  					assert.Equal(t, old+1, mockSwitch.numStatusResponse)
   403  				case bcproto.BlockRequest:
   404  					if ev.Height > params.startHeight {
   405  						old := mockSwitch.numNoBlockResponse
   406  						msg, err := bc.EncodeMsg(&ev)
   407  						assert.NoError(t, err)
   408  						reactor.Receive(channelID, mockPeer{id: p2p.ID(step.peer)}, msg)
   409  						assert.Equal(t, old+1, mockSwitch.numNoBlockResponse)
   410  					} else {
   411  						old := mockSwitch.numBlockResponse
   412  						msg, err := bc.EncodeMsg(&ev)
   413  						assert.NoError(t, err)
   414  						assert.NoError(t, err)
   415  						reactor.Receive(channelID, mockPeer{id: p2p.ID(step.peer)}, msg)
   416  						assert.Equal(t, old+1, mockSwitch.numBlockResponse)
   417  					}
   418  				}
   419  			}
   420  			err = reactor.Stop()
   421  			require.NoError(t, err)
   422  		})
   423  	}
   424  }
   425  
   426  func TestReactorSetSwitchNil(t *testing.T) {
   427  	config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
   428  	defer os.RemoveAll(config.RootDir)
   429  	genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
   430  
   431  	reactor := newTestReactor(testReactorParams{
   432  		logger:   log.TestingLogger(),
   433  		genDoc:   genDoc,
   434  		privVals: privVals,
   435  	})
   436  	reactor.SetSwitch(nil)
   437  
   438  	assert.Nil(t, reactor.Switch)
   439  	assert.Nil(t, reactor.io)
   440  }
   441  
   442  //----------------------------------------------
   443  // utility funcs
   444  
   445  func makeTxs(height int64) (txs []types.Tx) {
   446  	for i := 0; i < 10; i++ {
   447  		txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
   448  	}
   449  	return txs
   450  }
   451  
   452  func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
   453  	block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
   454  	return block
   455  }
   456  
   457  type testApp struct {
   458  	abci.BaseApplication
   459  }
   460  
   461  func randGenesisDoc(chainID string, numValidators int, randPower bool, minPower int64) (
   462  	*types.GenesisDoc, []types.PrivValidator) {
   463  	validators := make([]types.GenesisValidator, numValidators)
   464  	privValidators := make([]types.PrivValidator, numValidators)
   465  	for i := 0; i < numValidators; i++ {
   466  		val, privVal := types.RandValidator(randPower, minPower)
   467  		validators[i] = types.GenesisValidator{
   468  			PubKey: val.PubKey,
   469  			Power:  val.VotingPower,
   470  		}
   471  		privValidators[i] = privVal
   472  	}
   473  	sort.Sort(types.PrivValidatorsByAddress(privValidators))
   474  
   475  	return &types.GenesisDoc{
   476  		GenesisTime: tmtime.Now(),
   477  		ChainID:     chainID,
   478  		Validators:  validators,
   479  	}, privValidators
   480  }
   481  
   482  // Why are we importing the entire blockExecutor dependency graph here
   483  // when we have the facilities to
   484  func newReactorStore(
   485  	genDoc *types.GenesisDoc,
   486  	privVals []types.PrivValidator,
   487  	maxBlockHeight int64) (*store.BlockStore, sm.State, *sm.BlockExecutor) {
   488  	if len(privVals) != 1 {
   489  		panic("only support one validator")
   490  	}
   491  	app := &testApp{}
   492  	cc := proxy.NewLocalClientCreator(app)
   493  	proxyApp := proxy.NewAppConns(cc)
   494  	err := proxyApp.Start()
   495  	if err != nil {
   496  		panic(fmt.Errorf("error start app: %w", err))
   497  	}
   498  
   499  	stateDB := dbm.NewMemDB()
   500  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   501  
   502  	state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
   503  	if err != nil {
   504  		panic(fmt.Errorf("error constructing state from genesis file: %w", err))
   505  	}
   506  
   507  	db := dbm.NewMemDB()
   508  	blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(),
   509  		mock.Mempool{}, sm.MockEvidencePool{})
   510  	sm.SaveState(db, state)
   511  
   512  	// add blocks in
   513  	for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
   514  		lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil)
   515  		if blockHeight > 1 {
   516  			lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
   517  			lastBlock := blockStore.LoadBlock(blockHeight - 1)
   518  			vote, err := types.MakeVote(
   519  				lastBlock.Header.Height,
   520  				lastBlockMeta.BlockID,
   521  				state.Validators,
   522  				privVals[0],
   523  				lastBlock.Header.ChainID,
   524  				time.Now(),
   525  			)
   526  			if err != nil {
   527  				panic(err)
   528  			}
   529  			lastCommit = types.NewCommit(vote.Height, vote.Round,
   530  				lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
   531  		}
   532  
   533  		thisBlock := makeBlock(blockHeight, state, lastCommit)
   534  
   535  		thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
   536  		blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
   537  
   538  		state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock)
   539  		if err != nil {
   540  			panic(fmt.Errorf("error apply block: %w", err))
   541  		}
   542  
   543  		blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
   544  	}
   545  	return blockStore, state, blockExec
   546  }