github.com/number571/tendermint@v0.34.11-gost/internal/p2p/conn/connection_test.go (about) 1 package conn 2 3 import ( 4 "encoding/hex" 5 "net" 6 "testing" 7 "time" 8 9 "github.com/fortytw2/leaktest" 10 "github.com/gogo/protobuf/proto" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 "github.com/number571/tendermint/internal/libs/protoio" 15 "github.com/number571/tendermint/libs/log" 16 tmp2p "github.com/number571/tendermint/proto/tendermint/p2p" 17 "github.com/number571/tendermint/proto/tendermint/types" 18 ) 19 20 const maxPingPongPacketSize = 1024 // bytes 21 22 func createTestMConnection(conn net.Conn) *MConnection { 23 onReceive := func(chID byte, msgBytes []byte) { 24 } 25 onError := func(r interface{}) { 26 } 27 c := createMConnectionWithCallbacks(conn, onReceive, onError) 28 c.SetLogger(log.TestingLogger()) 29 return c 30 } 31 32 func createMConnectionWithCallbacks( 33 conn net.Conn, 34 onReceive func(chID byte, msgBytes []byte), 35 onError func(r interface{}), 36 ) *MConnection { 37 cfg := DefaultMConnConfig() 38 cfg.PingInterval = 90 * time.Millisecond 39 cfg.PongTimeout = 45 * time.Millisecond 40 chDescs := []*ChannelDescriptor{{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} 41 c := NewMConnectionWithConfig(conn, chDescs, onReceive, onError, cfg) 42 c.SetLogger(log.TestingLogger()) 43 return c 44 } 45 46 func TestMConnectionSendFlushStop(t *testing.T) { 47 server, client := NetPipe() 48 t.Cleanup(closeAll(t, client, server)) 49 50 clientConn := createTestMConnection(client) 51 err := clientConn.Start() 52 require.Nil(t, err) 53 t.Cleanup(stopAll(t, clientConn)) 54 55 msg := []byte("abc") 56 assert.True(t, clientConn.Send(0x01, msg)) 57 58 msgLength := 14 59 60 // start the reader in a new routine, so we can flush 61 errCh := make(chan error) 62 go func() { 63 msgB := make([]byte, msgLength) 64 _, err := server.Read(msgB) 65 if err != nil { 66 t.Error(err) 67 return 68 } 69 errCh <- err 70 }() 71 72 // stop the conn - it should flush all conns 73 clientConn.FlushStop() 74 75 timer := time.NewTimer(3 * time.Second) 76 select { 77 case <-errCh: 78 case <-timer.C: 79 t.Error("timed out waiting for msgs to be read") 80 } 81 } 82 83 func TestMConnectionSend(t *testing.T) { 84 server, client := NetPipe() 85 t.Cleanup(closeAll(t, client, server)) 86 87 mconn := createTestMConnection(client) 88 err := mconn.Start() 89 require.Nil(t, err) 90 t.Cleanup(stopAll(t, mconn)) 91 92 msg := []byte("Ant-Man") 93 assert.True(t, mconn.Send(0x01, msg)) 94 // Note: subsequent Send/TrySend calls could pass because we are reading from 95 // the send queue in a separate goroutine. 96 _, err = server.Read(make([]byte, len(msg))) 97 if err != nil { 98 t.Error(err) 99 } 100 assert.True(t, mconn.CanSend(0x01)) 101 102 msg = []byte("Spider-Man") 103 assert.True(t, mconn.TrySend(0x01, msg)) 104 _, err = server.Read(make([]byte, len(msg))) 105 if err != nil { 106 t.Error(err) 107 } 108 109 assert.False(t, mconn.CanSend(0x05), "CanSend should return false because channel is unknown") 110 assert.False(t, mconn.Send(0x05, []byte("Absorbing Man")), "Send should return false because channel is unknown") 111 } 112 113 func TestMConnectionReceive(t *testing.T) { 114 server, client := NetPipe() 115 t.Cleanup(closeAll(t, client, server)) 116 117 receivedCh := make(chan []byte) 118 errorsCh := make(chan interface{}) 119 onReceive := func(chID byte, msgBytes []byte) { 120 receivedCh <- msgBytes 121 } 122 onError := func(r interface{}) { 123 errorsCh <- r 124 } 125 mconn1 := createMConnectionWithCallbacks(client, onReceive, onError) 126 err := mconn1.Start() 127 require.Nil(t, err) 128 t.Cleanup(stopAll(t, mconn1)) 129 130 mconn2 := createTestMConnection(server) 131 err = mconn2.Start() 132 require.Nil(t, err) 133 t.Cleanup(stopAll(t, mconn2)) 134 135 msg := []byte("Cyclops") 136 assert.True(t, mconn2.Send(0x01, msg)) 137 138 select { 139 case receivedBytes := <-receivedCh: 140 assert.Equal(t, msg, receivedBytes) 141 case err := <-errorsCh: 142 t.Fatalf("Expected %s, got %+v", msg, err) 143 case <-time.After(500 * time.Millisecond): 144 t.Fatalf("Did not receive %s message in 500ms", msg) 145 } 146 } 147 148 func TestMConnectionStatus(t *testing.T) { 149 server, client := NetPipe() 150 t.Cleanup(closeAll(t, client, server)) 151 152 mconn := createTestMConnection(client) 153 err := mconn.Start() 154 require.Nil(t, err) 155 t.Cleanup(stopAll(t, mconn)) 156 157 status := mconn.Status() 158 assert.NotNil(t, status) 159 assert.Zero(t, status.Channels[0].SendQueueSize) 160 } 161 162 func TestMConnectionPongTimeoutResultsInError(t *testing.T) { 163 server, client := net.Pipe() 164 t.Cleanup(closeAll(t, client, server)) 165 166 receivedCh := make(chan []byte) 167 errorsCh := make(chan interface{}) 168 onReceive := func(chID byte, msgBytes []byte) { 169 receivedCh <- msgBytes 170 } 171 onError := func(r interface{}) { 172 errorsCh <- r 173 } 174 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 175 err := mconn.Start() 176 require.Nil(t, err) 177 t.Cleanup(stopAll(t, mconn)) 178 179 serverGotPing := make(chan struct{}) 180 go func() { 181 // read ping 182 var pkt tmp2p.Packet 183 _, err := protoio.NewDelimitedReader(server, maxPingPongPacketSize).ReadMsg(&pkt) 184 require.NoError(t, err) 185 serverGotPing <- struct{}{} 186 }() 187 <-serverGotPing 188 189 pongTimerExpired := mconn.config.PongTimeout + 200*time.Millisecond 190 select { 191 case msgBytes := <-receivedCh: 192 t.Fatalf("Expected error, but got %v", msgBytes) 193 case err := <-errorsCh: 194 assert.NotNil(t, err) 195 case <-time.After(pongTimerExpired): 196 t.Fatalf("Expected to receive error after %v", pongTimerExpired) 197 } 198 } 199 200 func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) { 201 server, client := net.Pipe() 202 t.Cleanup(closeAll(t, client, server)) 203 204 receivedCh := make(chan []byte) 205 errorsCh := make(chan interface{}) 206 onReceive := func(chID byte, msgBytes []byte) { 207 receivedCh <- msgBytes 208 } 209 onError := func(r interface{}) { 210 errorsCh <- r 211 } 212 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 213 err := mconn.Start() 214 require.Nil(t, err) 215 t.Cleanup(stopAll(t, mconn)) 216 217 // sending 3 pongs in a row (abuse) 218 protoWriter := protoio.NewDelimitedWriter(server) 219 220 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 221 require.NoError(t, err) 222 223 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 224 require.NoError(t, err) 225 226 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 227 require.NoError(t, err) 228 229 serverGotPing := make(chan struct{}) 230 go func() { 231 // read ping (one byte) 232 var packet tmp2p.Packet 233 _, err := protoio.NewDelimitedReader(server, maxPingPongPacketSize).ReadMsg(&packet) 234 require.NoError(t, err) 235 serverGotPing <- struct{}{} 236 237 // respond with pong 238 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 239 require.NoError(t, err) 240 }() 241 <-serverGotPing 242 243 pongTimerExpired := mconn.config.PongTimeout + 20*time.Millisecond 244 select { 245 case msgBytes := <-receivedCh: 246 t.Fatalf("Expected no data, but got %v", msgBytes) 247 case err := <-errorsCh: 248 t.Fatalf("Expected no error, but got %v", err) 249 case <-time.After(pongTimerExpired): 250 assert.True(t, mconn.IsRunning()) 251 } 252 } 253 254 func TestMConnectionMultiplePings(t *testing.T) { 255 server, client := net.Pipe() 256 t.Cleanup(closeAll(t, client, server)) 257 258 receivedCh := make(chan []byte) 259 errorsCh := make(chan interface{}) 260 onReceive := func(chID byte, msgBytes []byte) { 261 receivedCh <- msgBytes 262 } 263 onError := func(r interface{}) { 264 errorsCh <- r 265 } 266 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 267 err := mconn.Start() 268 require.Nil(t, err) 269 t.Cleanup(stopAll(t, mconn)) 270 271 // sending 3 pings in a row (abuse) 272 // see https://github.com/number571/tendermint/issues/1190 273 protoReader := protoio.NewDelimitedReader(server, maxPingPongPacketSize) 274 protoWriter := protoio.NewDelimitedWriter(server) 275 var pkt tmp2p.Packet 276 277 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) 278 require.NoError(t, err) 279 280 _, err = protoReader.ReadMsg(&pkt) 281 require.NoError(t, err) 282 283 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) 284 require.NoError(t, err) 285 286 _, err = protoReader.ReadMsg(&pkt) 287 require.NoError(t, err) 288 289 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) 290 require.NoError(t, err) 291 292 _, err = protoReader.ReadMsg(&pkt) 293 require.NoError(t, err) 294 295 assert.True(t, mconn.IsRunning()) 296 } 297 298 func TestMConnectionPingPongs(t *testing.T) { 299 // check that we are not leaking any go-routines 300 t.Cleanup(leaktest.CheckTimeout(t, 10*time.Second)) 301 302 server, client := net.Pipe() 303 t.Cleanup(closeAll(t, client, server)) 304 305 receivedCh := make(chan []byte) 306 errorsCh := make(chan interface{}) 307 onReceive := func(chID byte, msgBytes []byte) { 308 receivedCh <- msgBytes 309 } 310 onError := func(r interface{}) { 311 errorsCh <- r 312 } 313 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 314 err := mconn.Start() 315 require.Nil(t, err) 316 t.Cleanup(stopAll(t, mconn)) 317 318 serverGotPing := make(chan struct{}) 319 go func() { 320 protoReader := protoio.NewDelimitedReader(server, maxPingPongPacketSize) 321 protoWriter := protoio.NewDelimitedWriter(server) 322 var pkt tmp2p.PacketPing 323 324 // read ping 325 _, err = protoReader.ReadMsg(&pkt) 326 require.NoError(t, err) 327 serverGotPing <- struct{}{} 328 329 // respond with pong 330 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 331 require.NoError(t, err) 332 333 time.Sleep(mconn.config.PingInterval) 334 335 // read ping 336 _, err = protoReader.ReadMsg(&pkt) 337 require.NoError(t, err) 338 serverGotPing <- struct{}{} 339 340 // respond with pong 341 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 342 require.NoError(t, err) 343 }() 344 <-serverGotPing 345 <-serverGotPing 346 347 pongTimerExpired := (mconn.config.PongTimeout + 20*time.Millisecond) * 2 348 select { 349 case msgBytes := <-receivedCh: 350 t.Fatalf("Expected no data, but got %v", msgBytes) 351 case err := <-errorsCh: 352 t.Fatalf("Expected no error, but got %v", err) 353 case <-time.After(2 * pongTimerExpired): 354 assert.True(t, mconn.IsRunning()) 355 } 356 } 357 358 func TestMConnectionStopsAndReturnsError(t *testing.T) { 359 server, client := NetPipe() 360 t.Cleanup(closeAll(t, client, server)) 361 362 receivedCh := make(chan []byte) 363 errorsCh := make(chan interface{}) 364 onReceive := func(chID byte, msgBytes []byte) { 365 receivedCh <- msgBytes 366 } 367 onError := func(r interface{}) { 368 errorsCh <- r 369 } 370 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 371 err := mconn.Start() 372 require.Nil(t, err) 373 t.Cleanup(stopAll(t, mconn)) 374 375 if err := client.Close(); err != nil { 376 t.Error(err) 377 } 378 379 select { 380 case receivedBytes := <-receivedCh: 381 t.Fatalf("Expected error, got %v", receivedBytes) 382 case err := <-errorsCh: 383 assert.NotNil(t, err) 384 assert.False(t, mconn.IsRunning()) 385 case <-time.After(500 * time.Millisecond): 386 t.Fatal("Did not receive error in 500ms") 387 } 388 } 389 390 func newClientAndServerConnsForReadErrors(t *testing.T, chOnErr chan struct{}) (*MConnection, *MConnection) { 391 server, client := NetPipe() 392 393 onReceive := func(chID byte, msgBytes []byte) {} 394 onError := func(r interface{}) {} 395 396 // create client conn with two channels 397 chDescs := []*ChannelDescriptor{ 398 {ID: 0x01, Priority: 1, SendQueueCapacity: 1}, 399 {ID: 0x02, Priority: 1, SendQueueCapacity: 1}, 400 } 401 mconnClient := NewMConnection(client, chDescs, onReceive, onError) 402 mconnClient.SetLogger(log.TestingLogger().With("module", "client")) 403 err := mconnClient.Start() 404 require.Nil(t, err) 405 406 // create server conn with 1 channel 407 // it fires on chOnErr when there's an error 408 serverLogger := log.TestingLogger().With("module", "server") 409 onError = func(r interface{}) { 410 chOnErr <- struct{}{} 411 } 412 mconnServer := createMConnectionWithCallbacks(server, onReceive, onError) 413 mconnServer.SetLogger(serverLogger) 414 err = mconnServer.Start() 415 require.Nil(t, err) 416 return mconnClient, mconnServer 417 } 418 419 func expectSend(ch chan struct{}) bool { 420 after := time.After(time.Second * 5) 421 select { 422 case <-ch: 423 return true 424 case <-after: 425 return false 426 } 427 } 428 429 func TestMConnectionReadErrorBadEncoding(t *testing.T) { 430 chOnErr := make(chan struct{}) 431 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 432 433 client := mconnClient.conn 434 435 // Write it. 436 _, err := client.Write([]byte{1, 2, 3, 4, 5}) 437 require.NoError(t, err) 438 assert.True(t, expectSend(chOnErr), "badly encoded msgPacket") 439 t.Cleanup(stopAll(t, mconnClient, mconnServer)) 440 } 441 442 func TestMConnectionReadErrorUnknownChannel(t *testing.T) { 443 chOnErr := make(chan struct{}) 444 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 445 446 msg := []byte("Ant-Man") 447 448 // fail to send msg on channel unknown by client 449 assert.False(t, mconnClient.Send(0x03, msg)) 450 451 // send msg on channel unknown by the server. 452 // should cause an error 453 assert.True(t, mconnClient.Send(0x02, msg)) 454 assert.True(t, expectSend(chOnErr), "unknown channel") 455 t.Cleanup(stopAll(t, mconnClient, mconnServer)) 456 } 457 458 func TestMConnectionReadErrorLongMessage(t *testing.T) { 459 chOnErr := make(chan struct{}) 460 chOnRcv := make(chan struct{}) 461 462 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 463 t.Cleanup(stopAll(t, mconnClient, mconnServer)) 464 465 mconnServer.onReceive = func(chID byte, msgBytes []byte) { 466 chOnRcv <- struct{}{} 467 } 468 469 client := mconnClient.conn 470 protoWriter := protoio.NewDelimitedWriter(client) 471 472 // send msg thats just right 473 var packet = tmp2p.PacketMsg{ 474 ChannelID: 0x01, 475 EOF: true, 476 Data: make([]byte, mconnClient.config.MaxPacketMsgPayloadSize), 477 } 478 479 _, err := protoWriter.WriteMsg(mustWrapPacket(&packet)) 480 require.NoError(t, err) 481 assert.True(t, expectSend(chOnRcv), "msg just right") 482 483 // send msg thats too long 484 packet = tmp2p.PacketMsg{ 485 ChannelID: 0x01, 486 EOF: true, 487 Data: make([]byte, mconnClient.config.MaxPacketMsgPayloadSize+100), 488 } 489 490 _, err = protoWriter.WriteMsg(mustWrapPacket(&packet)) 491 require.Error(t, err) 492 assert.True(t, expectSend(chOnErr), "msg too long") 493 } 494 495 func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { 496 chOnErr := make(chan struct{}) 497 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 498 t.Cleanup(stopAll(t, mconnClient, mconnServer)) 499 500 // send msg with unknown msg type 501 _, err := protoio.NewDelimitedWriter(mconnClient.conn).WriteMsg(&types.Header{ChainID: "x"}) 502 require.NoError(t, err) 503 assert.True(t, expectSend(chOnErr), "unknown msg type") 504 } 505 506 func TestMConnectionTrySend(t *testing.T) { 507 server, client := NetPipe() 508 t.Cleanup(closeAll(t, client, server)) 509 510 mconn := createTestMConnection(client) 511 err := mconn.Start() 512 require.Nil(t, err) 513 t.Cleanup(stopAll(t, mconn)) 514 515 msg := []byte("Semicolon-Woman") 516 resultCh := make(chan string, 2) 517 assert.True(t, mconn.TrySend(0x01, msg)) 518 _, err = server.Read(make([]byte, len(msg))) 519 require.NoError(t, err) 520 assert.True(t, mconn.CanSend(0x01)) 521 assert.True(t, mconn.TrySend(0x01, msg)) 522 assert.False(t, mconn.CanSend(0x01)) 523 go func() { 524 mconn.TrySend(0x01, msg) 525 resultCh <- "TrySend" 526 }() 527 assert.False(t, mconn.CanSend(0x01)) 528 assert.False(t, mconn.TrySend(0x01, msg)) 529 assert.Equal(t, "TrySend", <-resultCh) 530 } 531 532 // nolint:lll //ignore line length for tests 533 func TestConnVectors(t *testing.T) { 534 535 testCases := []struct { 536 testName string 537 msg proto.Message 538 expBytes string 539 }{ 540 {"PacketPing", &tmp2p.PacketPing{}, "0a00"}, 541 {"PacketPong", &tmp2p.PacketPong{}, "1200"}, 542 {"PacketMsg", &tmp2p.PacketMsg{ChannelID: 1, EOF: false, Data: []byte("data transmitted over the wire")}, "1a2208011a1e64617461207472616e736d6974746564206f766572207468652077697265"}, 543 } 544 545 for _, tc := range testCases { 546 tc := tc 547 548 pm := mustWrapPacket(tc.msg) 549 bz, err := pm.Marshal() 550 require.NoError(t, err, tc.testName) 551 552 require.Equal(t, tc.expBytes, hex.EncodeToString(bz), tc.testName) 553 } 554 } 555 556 func TestMConnectionChannelOverflow(t *testing.T) { 557 chOnErr := make(chan struct{}) 558 chOnRcv := make(chan struct{}) 559 560 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 561 t.Cleanup(stopAll(t, mconnClient, mconnServer)) 562 563 mconnServer.onReceive = func(chID byte, msgBytes []byte) { 564 chOnRcv <- struct{}{} 565 } 566 567 client := mconnClient.conn 568 protoWriter := protoio.NewDelimitedWriter(client) 569 570 var packet = tmp2p.PacketMsg{ 571 ChannelID: 0x01, 572 EOF: true, 573 Data: []byte(`42`), 574 } 575 _, err := protoWriter.WriteMsg(mustWrapPacket(&packet)) 576 require.NoError(t, err) 577 assert.True(t, expectSend(chOnRcv)) 578 579 packet.ChannelID = int32(1025) 580 _, err = protoWriter.WriteMsg(mustWrapPacket(&packet)) 581 require.NoError(t, err) 582 assert.False(t, expectSend(chOnRcv)) 583 584 } 585 586 type stopper interface { 587 Stop() error 588 } 589 590 func stopAll(t *testing.T, stoppers ...stopper) func() { 591 return func() { 592 for _, s := range stoppers { 593 if err := s.Stop(); err != nil { 594 t.Log(err) 595 } 596 } 597 } 598 } 599 600 type closer interface { 601 Close() error 602 } 603 604 func closeAll(t *testing.T, closers ...closer) func() { 605 return func() { 606 for _, s := range closers { 607 if err := s.Close(); err != nil { 608 t.Log(err) 609 } 610 } 611 } 612 }