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