package itest import ( "context" "fmt" "strings" "time" "github.com/lightningnetwork/lnd" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" ) // testNetworkConnectionTimeout checks that the connectiontimeout is taking // effect. It creates a node with a small connection timeout value, and connects // it to a non-routable IP address. func testNetworkConnectionTimeout(net *lntest.NetworkHarness, t *harnessTest) { var ( ctxt, _ = context.WithTimeout( context.Background(), defaultTimeout, ) // testPub is a random public key for testing only. testPub = "0332bda7da70fefe4b6ab92f53b3c4f4ee7999" + "f312284a8e89c8670bb3f67dbee2" // testHost is a non-routable IP address. It's used to cause a // connection timeout. testHost = "10.255.255.255" ) // First, test the global timeout settings. // Create Carol with a connection timeout of 1 millisecond. carol, err := net.NewNode("Carol", []string{"--connectiontimeout=1ms"}) if err != nil { t.Fatalf("unable to create new node carol: %v", err) } defer shutdownAndAssert(net, t, carol) // Try to connect Carol to a non-routable IP address, which should give // us a timeout error. req := &lnrpc.ConnectPeerRequest{ Addr: &lnrpc.LightningAddress{ Pubkey: testPub, Host: testHost, }, } assertTimeoutError(ctxt, t, carol, req) // Second, test timeout on the connect peer request. // Create Dave with the default timeout setting. dave, err := net.NewNode("Dave", nil) if err != nil { t.Fatalf("unable to create new node dave: %v", err) } defer shutdownAndAssert(net, t, dave) // Try to connect Dave to a non-routable IP address, using a timeout // value of 1ms, which should give us a timeout error immediately. req = &lnrpc.ConnectPeerRequest{ Addr: &lnrpc.LightningAddress{ Pubkey: testPub, Host: testHost, }, Timeout: 1, } assertTimeoutError(ctxt, t, dave, req) } // assertTimeoutError asserts that a connection timeout error is raised. A // context with a default timeout is used to make the request. If our customized // connection timeout is less than the default, we won't see the request context // times out, instead a network connection timeout will be returned. func assertTimeoutError(ctxt context.Context, t *harnessTest, node *lntest.HarnessNode, req *lnrpc.ConnectPeerRequest) { t.t.Helper() // Create a context with a timeout value. ctxt, cancel := context.WithTimeout(ctxt, defaultTimeout) defer cancel() err := connect(ctxt, node, req) // a DeadlineExceeded error will appear in the context if the above // ctxtTimeout value is reached. require.NoError(t.t, ctxt.Err(), "context time out") // Check that the network returns a timeout error. require.Containsf( t.t, err.Error(), "i/o timeout", "expected to get a timeout error, instead got: %v", err, ) } func connect(ctxt context.Context, node *lntest.HarnessNode, req *lnrpc.ConnectPeerRequest) error { syncTimeout := time.After(15 * time.Second) ticker := time.NewTicker(time.Millisecond * 100) defer ticker.Stop() for { select { case <-ticker.C: _, err := node.ConnectPeer(ctxt, req) // If there's no error, return nil if err == nil { return err } // If the error is no ErrServerNotActive, return it. // Otherwise, we will retry until timeout. if !strings.Contains(err.Error(), lnd.ErrServerNotActive.Error()) { return err } case <-syncTimeout: return fmt.Errorf("chain backend did not " + "finish syncing") } } return nil }