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 }