github.com/DFWallet/tendermint-cosmos@v0.0.2/privval/signer_listener_endpoint_test.go (about)

     1  package privval
     2  
     3  import (
     4  	"net"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/DFWallet/tendermint-cosmos/crypto/ed25519"
    12  	"github.com/DFWallet/tendermint-cosmos/libs/log"
    13  	tmnet "github.com/DFWallet/tendermint-cosmos/libs/net"
    14  	tmrand "github.com/DFWallet/tendermint-cosmos/libs/rand"
    15  	"github.com/DFWallet/tendermint-cosmos/types"
    16  )
    17  
    18  var (
    19  	testTimeoutAccept = defaultTimeoutAcceptSeconds * time.Second
    20  
    21  	testTimeoutReadWrite    = 100 * time.Millisecond
    22  	testTimeoutReadWrite2o3 = 60 * time.Millisecond // 2/3 of the other one
    23  )
    24  
    25  type dialerTestCase struct {
    26  	addr   string
    27  	dialer SocketDialer
    28  }
    29  
    30  // TestSignerRemoteRetryTCPOnly will test connection retry attempts over TCP. We
    31  // don't need this for Unix sockets because the OS instantly knows the state of
    32  // both ends of the socket connection. This basically causes the
    33  // SignerDialerEndpoint.dialer() call inside SignerDialerEndpoint.acceptNewConnection() to return
    34  // successfully immediately, putting an instant stop to any retry attempts.
    35  func TestSignerRemoteRetryTCPOnly(t *testing.T) {
    36  	var (
    37  		attemptCh = make(chan int)
    38  		retries   = 10
    39  	)
    40  
    41  	ln, err := net.Listen("tcp", "127.0.0.1:0")
    42  	require.NoError(t, err)
    43  
    44  	// Continuously Accept connection and close {attempts} times
    45  	go func(ln net.Listener, attemptCh chan<- int) {
    46  		attempts := 0
    47  		for {
    48  			conn, err := ln.Accept()
    49  			require.NoError(t, err)
    50  
    51  			err = conn.Close()
    52  			require.NoError(t, err)
    53  
    54  			attempts++
    55  
    56  			if attempts == retries {
    57  				attemptCh <- attempts
    58  				break
    59  			}
    60  		}
    61  	}(ln, attemptCh)
    62  
    63  	dialerEndpoint := NewSignerDialerEndpoint(
    64  		log.TestingLogger(),
    65  		DialTCPFn(ln.Addr().String(), testTimeoutReadWrite, ed25519.GenPrivKey()),
    66  	)
    67  	SignerDialerEndpointTimeoutReadWrite(time.Millisecond)(dialerEndpoint)
    68  	SignerDialerEndpointConnRetries(retries)(dialerEndpoint)
    69  
    70  	chainID := tmrand.Str(12)
    71  	mockPV := types.NewMockPV()
    72  	signerServer := NewSignerServer(dialerEndpoint, chainID, mockPV)
    73  
    74  	err = signerServer.Start()
    75  	require.NoError(t, err)
    76  	t.Cleanup(func() {
    77  		if err := signerServer.Stop(); err != nil {
    78  			t.Error(err)
    79  		}
    80  	})
    81  
    82  	select {
    83  	case attempts := <-attemptCh:
    84  		assert.Equal(t, retries, attempts)
    85  	case <-time.After(1500 * time.Millisecond):
    86  		t.Error("expected remote to observe connection attempts")
    87  	}
    88  }
    89  
    90  func TestRetryConnToRemoteSigner(t *testing.T) {
    91  	for _, tc := range getDialerTestCases(t) {
    92  		var (
    93  			logger           = log.TestingLogger()
    94  			chainID          = tmrand.Str(12)
    95  			mockPV           = types.NewMockPV()
    96  			endpointIsOpenCh = make(chan struct{})
    97  			thisConnTimeout  = testTimeoutReadWrite
    98  			listenerEndpoint = newSignerListenerEndpoint(logger, tc.addr, thisConnTimeout)
    99  		)
   100  
   101  		dialerEndpoint := NewSignerDialerEndpoint(
   102  			logger,
   103  			tc.dialer,
   104  		)
   105  		SignerDialerEndpointTimeoutReadWrite(testTimeoutReadWrite)(dialerEndpoint)
   106  		SignerDialerEndpointConnRetries(10)(dialerEndpoint)
   107  
   108  		signerServer := NewSignerServer(dialerEndpoint, chainID, mockPV)
   109  
   110  		startListenerEndpointAsync(t, listenerEndpoint, endpointIsOpenCh)
   111  		t.Cleanup(func() {
   112  			if err := listenerEndpoint.Stop(); err != nil {
   113  				t.Error(err)
   114  			}
   115  		})
   116  
   117  		require.NoError(t, signerServer.Start())
   118  		assert.True(t, signerServer.IsRunning())
   119  		<-endpointIsOpenCh
   120  		if err := signerServer.Stop(); err != nil {
   121  			t.Error(err)
   122  		}
   123  
   124  		dialerEndpoint2 := NewSignerDialerEndpoint(
   125  			logger,
   126  			tc.dialer,
   127  		)
   128  		signerServer2 := NewSignerServer(dialerEndpoint2, chainID, mockPV)
   129  
   130  		// let some pings pass
   131  		require.NoError(t, signerServer2.Start())
   132  		assert.True(t, signerServer2.IsRunning())
   133  		t.Cleanup(func() {
   134  			if err := signerServer2.Stop(); err != nil {
   135  				t.Error(err)
   136  			}
   137  		})
   138  
   139  		// give the client some time to re-establish the conn to the remote signer
   140  		// should see sth like this in the logs:
   141  		//
   142  		// E[10016-01-10|17:12:46.128] Ping                                         err="remote signer timed out"
   143  		// I[10016-01-10|17:16:42.447] Re-created connection to remote signer       impl=SocketVal
   144  		time.Sleep(testTimeoutReadWrite * 2)
   145  	}
   146  }
   147  
   148  func newSignerListenerEndpoint(logger log.Logger, addr string, timeoutReadWrite time.Duration) *SignerListenerEndpoint {
   149  	proto, address := tmnet.ProtocolAndAddress(addr)
   150  
   151  	ln, err := net.Listen(proto, address)
   152  	logger.Info("SignerListener: Listening", "proto", proto, "address", address)
   153  	if err != nil {
   154  		panic(err)
   155  	}
   156  
   157  	var listener net.Listener
   158  
   159  	if proto == "unix" {
   160  		unixLn := NewUnixListener(ln)
   161  		UnixListenerTimeoutAccept(testTimeoutAccept)(unixLn)
   162  		UnixListenerTimeoutReadWrite(timeoutReadWrite)(unixLn)
   163  		listener = unixLn
   164  	} else {
   165  		tcpLn := NewTCPListener(ln, ed25519.GenPrivKey())
   166  		TCPListenerTimeoutAccept(testTimeoutAccept)(tcpLn)
   167  		TCPListenerTimeoutReadWrite(timeoutReadWrite)(tcpLn)
   168  		listener = tcpLn
   169  	}
   170  
   171  	return NewSignerListenerEndpoint(
   172  		logger,
   173  		listener,
   174  		SignerListenerEndpointTimeoutReadWrite(testTimeoutReadWrite),
   175  	)
   176  }
   177  
   178  func startListenerEndpointAsync(t *testing.T, sle *SignerListenerEndpoint, endpointIsOpenCh chan struct{}) {
   179  	go func(sle *SignerListenerEndpoint) {
   180  		require.NoError(t, sle.Start())
   181  		assert.True(t, sle.IsRunning())
   182  		close(endpointIsOpenCh)
   183  	}(sle)
   184  }
   185  
   186  func getMockEndpoints(
   187  	t *testing.T,
   188  	addr string,
   189  	socketDialer SocketDialer,
   190  ) (*SignerListenerEndpoint, *SignerDialerEndpoint) {
   191  
   192  	var (
   193  		logger           = log.TestingLogger()
   194  		endpointIsOpenCh = make(chan struct{})
   195  
   196  		dialerEndpoint = NewSignerDialerEndpoint(
   197  			logger,
   198  			socketDialer,
   199  		)
   200  
   201  		listenerEndpoint = newSignerListenerEndpoint(logger, addr, testTimeoutReadWrite)
   202  	)
   203  
   204  	SignerDialerEndpointTimeoutReadWrite(testTimeoutReadWrite)(dialerEndpoint)
   205  	SignerDialerEndpointConnRetries(1e6)(dialerEndpoint)
   206  
   207  	startListenerEndpointAsync(t, listenerEndpoint, endpointIsOpenCh)
   208  
   209  	require.NoError(t, dialerEndpoint.Start())
   210  	assert.True(t, dialerEndpoint.IsRunning())
   211  
   212  	<-endpointIsOpenCh
   213  
   214  	return listenerEndpoint, dialerEndpoint
   215  }