github.com/number571/tendermint@v0.34.11-gost/internal/blockchain/v2/reactor_test.go (about)

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