github.com/DFWallet/tendermint-cosmos@v0.0.2/blockchain/v1/peer_test.go (about)

     1  package v1
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/DFWallet/tendermint-cosmos/libs/log"
    12  	tmrand "github.com/DFWallet/tendermint-cosmos/libs/rand"
    13  	"github.com/DFWallet/tendermint-cosmos/p2p"
    14  	"github.com/DFWallet/tendermint-cosmos/types"
    15  )
    16  
    17  func TestPeerMonitor(t *testing.T) {
    18  	peer := NewBpPeer(
    19  		p2p.ID(tmrand.Str(12)), 0, 10,
    20  		func(err error, _ p2p.ID) {},
    21  		nil)
    22  	peer.SetLogger(log.TestingLogger())
    23  	peer.startMonitor()
    24  	assert.NotNil(t, peer.recvMonitor)
    25  	peer.stopMonitor()
    26  	assert.Nil(t, peer.recvMonitor)
    27  }
    28  
    29  func TestPeerResetBlockResponseTimer(t *testing.T) {
    30  	var (
    31  		numErrFuncCalls int        // number of calls to the errFunc
    32  		lastErr         error      // last generated error
    33  		peerTestMtx     sync.Mutex // modifications of ^^ variables are also done from timer handler goroutine
    34  	)
    35  	params := &BpPeerParams{timeout: 20 * time.Millisecond}
    36  
    37  	peer := NewBpPeer(
    38  		p2p.ID(tmrand.Str(12)), 0, 10,
    39  		func(err error, _ p2p.ID) {
    40  			peerTestMtx.Lock()
    41  			defer peerTestMtx.Unlock()
    42  			lastErr = err
    43  			numErrFuncCalls++
    44  		},
    45  		params)
    46  
    47  	peer.SetLogger(log.TestingLogger())
    48  	checkByStoppingPeerTimer(t, peer, false)
    49  
    50  	// initial reset call with peer having a nil timer
    51  	peer.resetBlockResponseTimer()
    52  	assert.NotNil(t, peer.blockResponseTimer)
    53  	// make sure timer is running and stop it
    54  	checkByStoppingPeerTimer(t, peer, true)
    55  
    56  	// reset with running timer
    57  	peer.resetBlockResponseTimer()
    58  	time.Sleep(5 * time.Millisecond)
    59  	peer.resetBlockResponseTimer()
    60  	assert.NotNil(t, peer.blockResponseTimer)
    61  
    62  	// let the timer expire and ...
    63  	time.Sleep(50 * time.Millisecond)
    64  	// ... check timer is not running
    65  	checkByStoppingPeerTimer(t, peer, false)
    66  
    67  	peerTestMtx.Lock()
    68  	// ... check errNoPeerResponse has been sent
    69  	assert.Equal(t, 1, numErrFuncCalls)
    70  	assert.Equal(t, lastErr, errNoPeerResponse)
    71  	peerTestMtx.Unlock()
    72  }
    73  
    74  func TestPeerRequestSent(t *testing.T) {
    75  	params := &BpPeerParams{timeout: 2 * time.Millisecond}
    76  
    77  	peer := NewBpPeer(
    78  		p2p.ID(tmrand.Str(12)), 0, 10,
    79  		func(err error, _ p2p.ID) {},
    80  		params)
    81  
    82  	peer.SetLogger(log.TestingLogger())
    83  
    84  	peer.RequestSent(1)
    85  	assert.NotNil(t, peer.recvMonitor)
    86  	assert.NotNil(t, peer.blockResponseTimer)
    87  	assert.Equal(t, 1, peer.NumPendingBlockRequests)
    88  
    89  	peer.RequestSent(1)
    90  	assert.NotNil(t, peer.recvMonitor)
    91  	assert.NotNil(t, peer.blockResponseTimer)
    92  	assert.Equal(t, 2, peer.NumPendingBlockRequests)
    93  }
    94  
    95  func TestPeerGetAndRemoveBlock(t *testing.T) {
    96  	peer := NewBpPeer(
    97  		p2p.ID(tmrand.Str(12)), 0, 100,
    98  		func(err error, _ p2p.ID) {},
    99  		nil)
   100  
   101  	// Change peer height
   102  	peer.Height = int64(10)
   103  	assert.Equal(t, int64(10), peer.Height)
   104  
   105  	// request some blocks and receive few of them
   106  	for i := 1; i <= 10; i++ {
   107  		peer.RequestSent(int64(i))
   108  		if i > 5 {
   109  			// only receive blocks 1..5
   110  			continue
   111  		}
   112  		_ = peer.AddBlock(makeSmallBlock(i), 10)
   113  	}
   114  
   115  	tests := []struct {
   116  		name         string
   117  		height       int64
   118  		wantErr      error
   119  		blockPresent bool
   120  	}{
   121  		{"no request", 100, errMissingBlock, false},
   122  		{"no block", 6, errMissingBlock, false},
   123  		{"block 1 present", 1, nil, true},
   124  		{"block max present", 5, nil, true},
   125  	}
   126  
   127  	for _, tt := range tests {
   128  		tt := tt
   129  		t.Run(tt.name, func(t *testing.T) {
   130  			// try to get the block
   131  			b, err := peer.BlockAtHeight(tt.height)
   132  			assert.Equal(t, tt.wantErr, err)
   133  			assert.Equal(t, tt.blockPresent, b != nil)
   134  
   135  			// remove the block
   136  			peer.RemoveBlock(tt.height)
   137  			_, err = peer.BlockAtHeight(tt.height)
   138  			assert.Equal(t, errMissingBlock, err)
   139  		})
   140  	}
   141  }
   142  
   143  func TestPeerAddBlock(t *testing.T) {
   144  	peer := NewBpPeer(
   145  		p2p.ID(tmrand.Str(12)), 0, 100,
   146  		func(err error, _ p2p.ID) {},
   147  		nil)
   148  
   149  	// request some blocks, receive one
   150  	for i := 1; i <= 10; i++ {
   151  		peer.RequestSent(int64(i))
   152  		if i == 5 {
   153  			// receive block 5
   154  			_ = peer.AddBlock(makeSmallBlock(i), 10)
   155  		}
   156  	}
   157  
   158  	tests := []struct {
   159  		name         string
   160  		height       int64
   161  		wantErr      error
   162  		blockPresent bool
   163  	}{
   164  		{"no request", 50, errMissingBlock, false},
   165  		{"duplicate block", 5, errDuplicateBlock, true},
   166  		{"block 1 successfully received", 1, nil, true},
   167  		{"block max successfully received", 10, nil, true},
   168  	}
   169  
   170  	for _, tt := range tests {
   171  		tt := tt
   172  		t.Run(tt.name, func(t *testing.T) {
   173  			// try to get the block
   174  			err := peer.AddBlock(makeSmallBlock(int(tt.height)), 10)
   175  			assert.Equal(t, tt.wantErr, err)
   176  			_, err = peer.BlockAtHeight(tt.height)
   177  			assert.Equal(t, tt.blockPresent, err == nil)
   178  		})
   179  	}
   180  }
   181  
   182  func TestPeerOnErrFuncCalledDueToExpiration(t *testing.T) {
   183  
   184  	params := &BpPeerParams{timeout: 10 * time.Millisecond}
   185  	var (
   186  		numErrFuncCalls int        // number of calls to the onErr function
   187  		lastErr         error      // last generated error
   188  		peerTestMtx     sync.Mutex // modifications of ^^ variables are also done from timer handler goroutine
   189  	)
   190  
   191  	peer := NewBpPeer(
   192  		p2p.ID(tmrand.Str(12)), 0, 10,
   193  		func(err error, _ p2p.ID) {
   194  			peerTestMtx.Lock()
   195  			defer peerTestMtx.Unlock()
   196  			lastErr = err
   197  			numErrFuncCalls++
   198  		},
   199  		params)
   200  
   201  	peer.SetLogger(log.TestingLogger())
   202  
   203  	peer.RequestSent(1)
   204  	time.Sleep(50 * time.Millisecond)
   205  	// timer should have expired by now, check that the on error function was called
   206  	peerTestMtx.Lock()
   207  	assert.Equal(t, 1, numErrFuncCalls)
   208  	assert.Equal(t, errNoPeerResponse, lastErr)
   209  	peerTestMtx.Unlock()
   210  }
   211  
   212  func TestPeerCheckRate(t *testing.T) {
   213  	params := &BpPeerParams{
   214  		timeout:     time.Second,
   215  		minRecvRate: int64(100), // 100 bytes/sec exponential moving average
   216  	}
   217  	peer := NewBpPeer(
   218  		p2p.ID(tmrand.Str(12)), 0, 10,
   219  		func(err error, _ p2p.ID) {},
   220  		params)
   221  	peer.SetLogger(log.TestingLogger())
   222  
   223  	require.Nil(t, peer.CheckRate())
   224  
   225  	for i := 0; i < 40; i++ {
   226  		peer.RequestSent(int64(i))
   227  	}
   228  
   229  	// monitor starts with a higher rEMA (~ 2*minRecvRate), wait for it to go down
   230  	time.Sleep(900 * time.Millisecond)
   231  
   232  	// normal peer - send a bit more than 100 bytes/sec, > 10 bytes/100msec, check peer is not considered slow
   233  	for i := 0; i < 10; i++ {
   234  		_ = peer.AddBlock(makeSmallBlock(i), 11)
   235  		time.Sleep(100 * time.Millisecond)
   236  		require.Nil(t, peer.CheckRate())
   237  	}
   238  
   239  	// slow peer - send a bit less than 10 bytes/100msec
   240  	for i := 10; i < 20; i++ {
   241  		_ = peer.AddBlock(makeSmallBlock(i), 9)
   242  		time.Sleep(100 * time.Millisecond)
   243  	}
   244  	// check peer is considered slow
   245  	assert.Equal(t, errSlowPeer, peer.CheckRate())
   246  }
   247  
   248  func TestPeerCleanup(t *testing.T) {
   249  	params := &BpPeerParams{timeout: 2 * time.Millisecond}
   250  
   251  	peer := NewBpPeer(
   252  		p2p.ID(tmrand.Str(12)), 0, 10,
   253  		func(err error, _ p2p.ID) {},
   254  		params)
   255  	peer.SetLogger(log.TestingLogger())
   256  
   257  	assert.Nil(t, peer.blockResponseTimer)
   258  	peer.RequestSent(1)
   259  	assert.NotNil(t, peer.blockResponseTimer)
   260  
   261  	peer.Cleanup()
   262  	checkByStoppingPeerTimer(t, peer, false)
   263  }
   264  
   265  // Check if peer timer is running or not (a running timer can be successfully stopped).
   266  // Note: stops the timer.
   267  func checkByStoppingPeerTimer(t *testing.T, peer *BpPeer, running bool) {
   268  	assert.NotPanics(t, func() {
   269  		stopped := peer.stopBlockResponseTimer()
   270  		if running {
   271  			assert.True(t, stopped)
   272  		} else {
   273  			assert.False(t, stopped)
   274  		}
   275  	})
   276  }
   277  
   278  func makeSmallBlock(height int) *types.Block {
   279  	return types.MakeBlock(int64(height), []types.Tx{types.Tx("foo")}, nil, nil)
   280  }