github.com/decred/dcrlnd@v0.7.6/lntest/itest/dcrlnd_misc_test.go (about)

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/decred/dcrlnd/lnrpc"
    10  	"github.com/decred/dcrlnd/lntest"
    11  )
    12  
    13  // testConcurrentNodeConnection tests whether we can repeatedly connect and
    14  // disconnect two nodes and that trying to connect "at the same time" will not
    15  // cause problems.
    16  //
    17  // "At the same time" is used in scare quotes, since at the level of abstraction
    18  // used in these tests it's not possible to really enforce that the connection
    19  // attempts are sent in any specific order or parallelism. So we resort to doing
    20  // a number of repeated number of trial runs, with as much concurrency as
    21  // possible.
    22  func testConcurrentNodeConnection(net *lntest.NetworkHarness, t *harnessTest) {
    23  	ctxb := context.Background()
    24  
    25  	aliceToBobReq := &lnrpc.ConnectPeerRequest{
    26  		Addr: &lnrpc.LightningAddress{
    27  			Pubkey: net.Bob.PubKeyStr,
    28  			Host:   net.Bob.P2PAddr(),
    29  		},
    30  		Perm: false,
    31  	}
    32  
    33  	bobToAliceReq := &lnrpc.ConnectPeerRequest{
    34  		Addr: &lnrpc.LightningAddress{
    35  			Pubkey: net.Alice.PubKeyStr,
    36  			Host:   net.Alice.P2PAddr(),
    37  		},
    38  		Perm: false,
    39  	}
    40  
    41  	connect := func(node *lntest.HarnessNode, req *lnrpc.ConnectPeerRequest, wg *sync.WaitGroup) error {
    42  		_, err := node.ConnectPeer(ctxb, req)
    43  		wg.Done()
    44  		return err
    45  	}
    46  
    47  	// Initially disconnect Alice and Bob. Several connection attempts will
    48  	// be performed later on. Ignore errors if they are not connected and
    49  	// give some time for the disconnection to clear all resources.
    50  	net.DisconnectNodes(net.Alice, net.Bob)
    51  	time.Sleep(50 * time.Millisecond)
    52  
    53  	// Perform a number of trial runs in sequence, so we have some reasonable
    54  	// chance actually performing connections "at the same time".
    55  	nbAttempts := 10
    56  	for i := 0; i < nbAttempts; i++ {
    57  		// Sanity check that neither node has a connection.
    58  		assertNotConnected(t, net.Alice, net.Bob)
    59  
    60  		logLine := fmt.Sprintf("=== %s: Starting connection iteration %d\n",
    61  			time.Now(), i)
    62  		net.Alice.AddToLog(logLine)
    63  		net.Bob.AddToLog(logLine)
    64  
    65  		var aliceReply, bobReply error
    66  		wg := new(sync.WaitGroup)
    67  
    68  		// Start two go routines which will try to connect "at the same
    69  		// time".
    70  		wg.Add(2)
    71  		go func() { aliceReply = connect(net.Alice, aliceToBobReq, wg) }()
    72  		go func() { bobReply = connect(net.Bob, bobToAliceReq, wg) }()
    73  
    74  		wgWaitChan := make(chan struct{})
    75  		go func() {
    76  			wg.Wait()
    77  			close(wgWaitChan)
    78  		}()
    79  
    80  		select {
    81  		case <-wgWaitChan:
    82  			if aliceReply != nil && bobReply != nil {
    83  				// Depending on exact timings, one of the replies might fail
    84  				// due to the nodes already being connected, but not both.
    85  				t.Fatalf("Both replies should not error out")
    86  			}
    87  		case <-time.After(15 * time.Second):
    88  			t.Fatalf("Timeout while waiting for connection reply")
    89  		}
    90  
    91  		// Give the nodes time to settle their connections and background
    92  		// processes.
    93  		time.Sleep(50 * time.Millisecond)
    94  
    95  		logLine = fmt.Sprintf("=== %s: Connections requests sent. Will check on status\n",
    96  			time.Now())
    97  		net.Alice.AddToLog(logLine)
    98  		net.Bob.AddToLog(logLine)
    99  
   100  		// Sanity check connection number.
   101  		assertConnected(t, net.Alice, net.Bob)
   102  
   103  		// Check whether the connection was made alice -> bob or bob ->
   104  		// alice.  The assert above ensures we can safely access
   105  		// alicePeers[0].
   106  		alicePeers, err := net.Alice.ListPeers(ctxb, &lnrpc.ListPeersRequest{})
   107  		if err != nil {
   108  			t.Fatalf("unable to fetch carol's peers %v", err)
   109  		}
   110  		if !alicePeers.Peers[0].Inbound {
   111  			// Connection was made in the alice -> bob direction.
   112  			net.DisconnectNodes(net.Alice, net.Bob)
   113  		} else {
   114  			// Connection was made in the alice <- bob direction.
   115  			net.DisconnectNodes(net.Alice, net.Bob)
   116  		}
   117  	}
   118  	logLine := fmt.Sprintf("=== %s: Reconnection tests successful\n",
   119  		time.Now())
   120  	net.Alice.AddToLog(logLine)
   121  	net.Bob.AddToLog(logLine)
   122  
   123  	// Wait for the final disconnection to release all resources, then
   124  	// ensure both nodes are connected again.
   125  	assertNotConnected(t, net.Alice, net.Bob)
   126  	net.EnsureConnected(t.t, net.Alice, net.Bob)
   127  }