github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/p2p/conn/connection_test.go (about)

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