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

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/decred/dcrd/chaincfg/chainhash"
     9  	"github.com/decred/dcrd/dcrutil/v4"
    10  	"github.com/decred/dcrlnd/lnrpc"
    11  	"github.com/decred/dcrlnd/lnrpc/invoicesrpc"
    12  	"github.com/decred/dcrlnd/lnrpc/routerrpc"
    13  	"github.com/decred/dcrlnd/lntest"
    14  	"github.com/decred/dcrlnd/lntypes"
    15  	"github.com/stretchr/testify/require"
    16  	"matheusd.com/testctx"
    17  )
    18  
    19  // testOfflineHopInvoice tests whether trying to pay an invoice to an offline
    20  // node fails as expected.
    21  //
    22  // This test creates the following network of channels:
    23  //
    24  //	Dave -> Carol    Alice -> Bob
    25  //
    26  // Then tries to perform a payment from Dave -> to Bob. This should fail, since
    27  // there is no route connecting them. Carol and Alice are then connected,
    28  // payments are performed. And a final test disconnecting Alice and trying to
    29  // perform a new payment should also fail.
    30  func testOfflineHopInvoice(net *lntest.NetworkHarness, t *harnessTest) {
    31  	const chanAmt = dcrutil.Amount(100000)
    32  	ctxb := context.Background()
    33  
    34  	// Open a channel between Alice and Bob with Alice being the sole funder of
    35  	// the channel.
    36  	chanPointAlice := openChannelAndAssert(
    37  		t, net, net.Alice, net.Bob,
    38  		lntest.OpenChannelParams{
    39  			Amt: chanAmt,
    40  		},
    41  	)
    42  
    43  	// Create Dave's Node.
    44  	dave := net.NewNode(t.t, "Dave", nil)
    45  	defer shutdownAndAssert(net, t, dave)
    46  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, dave)
    47  
    48  	carol := net.NewNode(t.t, "Carol", []string{"--nolisten"})
    49  	defer shutdownAndAssert(net, t, carol)
    50  
    51  	net.ConnectNodes(t.t, carol, dave)
    52  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, carol)
    53  
    54  	chanPointDave := openChannelAndAssert(
    55  		t, net, dave, carol,
    56  		lntest.OpenChannelParams{
    57  			Amt: chanAmt,
    58  		},
    59  	)
    60  
    61  	// Generate 5 payment requests in Bob.
    62  	const numPayments = 5
    63  	const paymentAmt = 1000
    64  	payReqs, _, _, err := createPayReqs(
    65  		net.Bob, paymentAmt, numPayments,
    66  	)
    67  	if err != nil {
    68  		t.Fatalf("unable to create pay reqs: %v", err)
    69  	}
    70  
    71  	// tryPayment tries to pay the given invoice from srcNode. It will check
    72  	// if the returned error is the expected one.
    73  	tryPayment := func(payReq string, srcNode *lntest.HarnessNode, expectedErr string) {
    74  		sendReq := &lnrpc.SendRequest{
    75  			PaymentRequest:       payReq,
    76  			IgnoreMaxOutboundAmt: true,
    77  		}
    78  		ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
    79  		resp, err := srcNode.SendPaymentSync(ctxt, sendReq)
    80  		if err != nil {
    81  			t.Fatalf("unable to send payment: %v", err)
    82  		}
    83  		if resp.PaymentError != expectedErr {
    84  			t.Fatalf("payment error (%v) != expected (%v)",
    85  				resp.PaymentError, expectedErr)
    86  		}
    87  	}
    88  
    89  	// Constants to make our lives easier.
    90  	errNoPath := "unable to find a path to destination"
    91  	success := ""
    92  
    93  	// At this stage, our network looks like the following:
    94  	//    Dave -> Carol    Alice -> Bob
    95  
    96  	// Payment from Alice should work, given the Alice -> Bob link.
    97  	tryPayment(payReqs[0], net.Alice, success)
    98  
    99  	// Payments from Carol and Dave should _not_ work, given there is no route.
   100  	tryPayment(payReqs[1], carol, errNoPath)
   101  	tryPayment(payReqs[1], dave, errNoPath)
   102  
   103  	// Connect Carol to Alice (but don't create a channel yet).
   104  	net.EnsureConnected(t.t, carol, net.Alice)
   105  
   106  	// Try to perform the payments from Carol and Dave. They should still fail.
   107  	tryPayment(payReqs[1], carol, errNoPath)
   108  	tryPayment(payReqs[1], dave, errNoPath)
   109  
   110  	// Create a channel between Carol and Alice and wait for it to become valid.
   111  	chanPointCarol := openChannelAndAssert(
   112  		t, net, carol, net.Alice,
   113  		lntest.OpenChannelParams{
   114  			Amt: chanAmt,
   115  		},
   116  	)
   117  
   118  	// Ensure Dave knows about the Carol -> Alice channel
   119  	if err = dave.WaitForNetworkChannelOpen(chanPointCarol); err != nil {
   120  		t.Fatalf("carol didn't advertise channel before "+
   121  			"timeout: %v", err)
   122  	}
   123  
   124  	// TODO(decred): Fix this.
   125  	//
   126  	// This test fails after the upstream PR 2740 is merged due to network
   127  	// partitions now taking discovery.DefaultHistoricalSyncInterval (10
   128  	// minutes) to trigger a full graph re-discovery.
   129  	//
   130  	// This means dave doesn't get a gossip message describing the
   131  	// Alice->Bob channel for a long time and fails to find a route to
   132  	// perform the payments.
   133  	//
   134  	// We need some way of force triggering a historical graph sync in dave
   135  	// after connecting carol and alice (or better yet some way of reliably
   136  	// knowing that carol didn't previously relay that channel to him).
   137  	time.Sleep(time.Second * 10)
   138  
   139  	fundingTxId, _ := chainhash.NewHash(chanPointAlice.GetFundingTxidBytes())
   140  	fmt.Printf("looking for %s\n", fundingTxId)
   141  
   142  	resp, err := dave.DescribeGraph(testctx.New(t), &lnrpc.ChannelGraphRequest{})
   143  	if err != nil {
   144  		t.Fatalf("blergh: %v", err)
   145  	}
   146  	fmt.Println("Existing network graph")
   147  	for _, e := range resp.Edges {
   148  		fmt.Printf("edge %s\n   n1=%s\n   n2=%s\n", e.ChanPoint,
   149  			e.Node1Pub, e.Node2Pub)
   150  	}
   151  
   152  	if err = dave.WaitForNetworkChannelOpen(chanPointAlice); err != nil {
   153  		t.Fatalf("dave didn't receive the alice->bob channel before "+
   154  			"timeout: %v", err)
   155  	}
   156  	// At this stage, our network looks like the following:
   157  	//    Dave -> Carol -> Alice -> Bob
   158  
   159  	// Performing the payments should now work.
   160  	tryPayment(payReqs[1], carol, success)
   161  	tryPayment(payReqs[2], dave, success)
   162  
   163  	// Disconnect Carol from Alice & Dave (simulating a broken link, carol
   164  	// offline, etc)
   165  	if err := net.DisconnectNodes(carol, net.Alice); err != nil {
   166  		t.Fatalf("unable to disconnect carol from alice: %v", err)
   167  	}
   168  	if err := net.DisconnectNodes(carol, dave); err != nil {
   169  		t.Fatalf("unable to disconnect carol from dave: %v", err)
   170  	}
   171  
   172  	// Give some time for disconnection to finalize.
   173  	time.Sleep(time.Second)
   174  
   175  	// Starting payments from Carol and Dave should fail.
   176  	tryPayment(payReqs[3], carol, errNoPath)
   177  	tryPayment(payReqs[3], dave, errNoPath)
   178  
   179  	// Reconnect Carol to Alice & Dave
   180  	net.EnsureConnected(t.t, carol, net.Alice)
   181  	net.EnsureConnected(t.t, carol, dave)
   182  
   183  	// Give some time for reconnection to finalize.
   184  	time.Sleep(time.Second)
   185  
   186  	// Payments now succeed again.
   187  	tryPayment(payReqs[3], carol, success)
   188  	tryPayment(payReqs[4], dave, success)
   189  
   190  	// Close the channels.
   191  	closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false)
   192  	closeChannelAndAssert(t, net, carol, chanPointCarol, false)
   193  	closeChannelAndAssert(t, net, dave, chanPointDave, false)
   194  }
   195  
   196  // testCalcPayStats asserts the correctness of the CalcPaymentStats RPC.
   197  func testCalcPayStats(net *lntest.NetworkHarness, t *harnessTest) {
   198  	ctxb, cancel := context.WithCancel(context.Background())
   199  	defer cancel()
   200  
   201  	alice := net.NewNode(t.t, "Alice", nil)
   202  	defer shutdownAndAssert(net, t, alice)
   203  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, alice)
   204  
   205  	bob := net.NewNode(t.t, "Bob", nil)
   206  	defer shutdownAndAssert(net, t, bob)
   207  	net.ConnectNodes(t.t, alice, bob)
   208  
   209  	// Open a channel between alice and bob.
   210  	chanReq := lntest.OpenChannelParams{
   211  		Amt: defaultChanAmt,
   212  	}
   213  
   214  	chanPoint := openChannelAndAssert(t, net, alice, bob, chanReq)
   215  
   216  	// Complete 5 payments.
   217  	const numPayments = 5
   218  	const paymentAmt = 1000
   219  	payReqs, _, _, err := createPayReqs(
   220  		bob, paymentAmt, numPayments,
   221  	)
   222  	require.NoError(t.t, err)
   223  	err = completePaymentRequests(
   224  		alice, alice.RouterClient,
   225  		payReqs, true,
   226  	)
   227  	require.NoError(t.t, err)
   228  
   229  	// Create and fail a payment (by using a hold invoice).
   230  	var (
   231  		preimage = lntypes.Preimage{1, 2, 3, 4, 5}
   232  		payHash  = preimage.Hash()
   233  	)
   234  	invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{
   235  		Value:      30000,
   236  		CltvExpiry: 40,
   237  		Hash:       payHash[:],
   238  	}
   239  
   240  	bobInvoice, err := bob.AddHoldInvoice(testctx.New(t), invoiceReq)
   241  	require.NoError(t.t, err)
   242  	_, err = alice.RouterClient.SendPaymentV2(
   243  		ctxb, &routerrpc.SendPaymentRequest{
   244  			PaymentRequest: bobInvoice.PaymentRequest,
   245  			TimeoutSeconds: 60,
   246  			FeeLimitMAtoms: noFeeLimitMAtoms,
   247  		},
   248  	)
   249  	require.NoError(t.t, err)
   250  	waitForInvoiceAccepted(t, bob, payHash)
   251  	_, err = bob.CancelInvoice(testctx.New(t), &invoicesrpc.CancelInvoiceMsg{PaymentHash: payHash[:]})
   252  	require.NoError(t.t, err)
   253  
   254  	// Create but do not settle a payment (by using a hold invoice).
   255  	var (
   256  		preimage2 = lntypes.Preimage{1, 2, 3, 4, 5, 6}
   257  		payHash2  = preimage2.Hash()
   258  	)
   259  	invoiceReq2 := &invoicesrpc.AddHoldInvoiceRequest{
   260  		Value:      30000,
   261  		CltvExpiry: 40,
   262  		Hash:       payHash2[:],
   263  	}
   264  
   265  	bobInvoice2, err := bob.AddHoldInvoice(testctx.New(t), invoiceReq2)
   266  	require.NoError(t.t, err)
   267  	_, err = alice.RouterClient.SendPaymentV2(
   268  		ctxb, &routerrpc.SendPaymentRequest{
   269  			PaymentRequest: bobInvoice2.PaymentRequest,
   270  			TimeoutSeconds: 60,
   271  			FeeLimitMAtoms: noFeeLimitMAtoms,
   272  		},
   273  	)
   274  	require.NoError(t.t, err)
   275  	waitForInvoiceAccepted(t, bob, payHash2)
   276  
   277  	// Fetch the payment stats.
   278  	payStats, err := alice.CalcPaymentStats(testctx.WithTimeout(t, defaultTimeout), &lnrpc.CalcPaymentStatsRequest{})
   279  	require.NoError(t.t, err)
   280  	require.Equal(t.t, uint64(7), payStats.Total)
   281  	require.Equal(t.t, uint64(5), payStats.Succeeded)
   282  	require.Equal(t.t, uint64(1), payStats.Failed)
   283  	require.Equal(t.t, uint64(7), payStats.HtlcAttempts)
   284  	require.Equal(t.t, uint64(1), payStats.HtlcFailed)
   285  	require.Equal(t.t, uint64(5), payStats.HtlcSettled)
   286  	require.Equal(t.t, uint64(0), payStats.OldDupePayments)
   287  
   288  	// Clean up the channel.
   289  	_, err = bob.CancelInvoice(testctx.New(t), &invoicesrpc.CancelInvoiceMsg{PaymentHash: payHash2[:]})
   290  	require.NoError(t.t, err)
   291  	closeChannelAndAssert(t, net, alice, chanPoint, false)
   292  }