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

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/decred/dcrlnd/lnrpc"
     8  	"github.com/decred/dcrlnd/lnrpc/invoicesrpc"
     9  	"github.com/decred/dcrlnd/lnrpc/routerrpc"
    10  	"github.com/decred/dcrlnd/lntest"
    11  	"github.com/decred/dcrlnd/lntypes"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  // testMaxHtlcPathfind tests the case where we try to send a payment over a
    16  // channel where we have already reached the limit of the number of htlcs that
    17  // we may add to the remote party's commitment. This test asserts that we do
    18  // not attempt to use the full channel at all in our pathfinding.
    19  func testMaxHtlcPathfind(net *lntest.NetworkHarness, t *harnessTest) {
    20  	ctxb := context.Background()
    21  
    22  	// Setup a channel between Alice and Bob where Alice will only allow
    23  	// Bob to add a maximum of 5 htlcs to her commitment.
    24  	maxHtlcs := 5
    25  
    26  	chanPoint := openChannelAndAssert(
    27  		t, net, net.Alice, net.Bob,
    28  		lntest.OpenChannelParams{
    29  			Amt:            1000000,
    30  			PushAmt:        800000,
    31  			RemoteMaxHtlcs: uint16(maxHtlcs),
    32  		},
    33  	)
    34  
    35  	// Wait for Alice and Bob to receive the channel edge from the
    36  	// funding manager.
    37  	err := net.Alice.WaitForNetworkChannelOpen(chanPoint)
    38  	require.NoError(t.t, err, "alice does not have open channel")
    39  
    40  	err = net.Bob.WaitForNetworkChannelOpen(chanPoint)
    41  	require.NoError(t.t, err, "bob does not have open channel")
    42  
    43  	// Alice and bob should have one channel open with each other now.
    44  	assertNodeNumChannels(t, net.Alice, 1)
    45  	assertNodeNumChannels(t, net.Bob, 1)
    46  
    47  	// Send our maximum number of htlcs from Bob -> Alice so that we get
    48  	// to a point where Alice won't accept any more htlcs on the channel.
    49  	subscriptions := make([]*holdSubscription, maxHtlcs)
    50  	cancelCtxs := make([]func(), maxHtlcs)
    51  
    52  	for i := 0; i < maxHtlcs; i++ {
    53  		subCtx, cancel := context.WithTimeout(ctxb, defaultTimeout)
    54  		cancelCtxs[i] = cancel
    55  
    56  		subscriptions[i] = acceptHoldInvoice(
    57  			subCtx, t.t, i, net.Bob, net.Alice,
    58  		)
    59  	}
    60  
    61  	// Cancel all of our subscriptions on exit.
    62  	defer func() {
    63  		for _, cancel := range cancelCtxs {
    64  			cancel()
    65  		}
    66  	}()
    67  
    68  	err = assertNumActiveHtlcs([]*lntest.HarnessNode{
    69  		net.Alice, net.Bob,
    70  	}, maxHtlcs)
    71  	require.NoError(t.t, err, "htlcs not active")
    72  
    73  	// Now we send a payment from Alice -> Bob to sanity check that our
    74  	// commitment limit is not applied in the opposite direction.
    75  	subCtx, cancel := context.WithTimeout(ctxb, defaultTimeout)
    76  	defer cancel()
    77  	aliceBobSub := acceptHoldInvoice(
    78  		subCtx, t.t, maxHtlcs, net.Alice, net.Bob,
    79  	)
    80  	err = assertNumActiveHtlcs([]*lntest.HarnessNode{
    81  		net.Alice, net.Bob,
    82  	}, maxHtlcs+1)
    83  	require.NoError(t.t, err, "htlcs not active")
    84  
    85  	// Now, we're going to try to send another payment from Bob -> Alice.
    86  	// We've hit our max remote htlcs, so we expect this payment to spin
    87  	// out dramatically with pathfinding.
    88  	ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
    89  	payment, err := net.Bob.RouterClient.SendPaymentV2(
    90  		ctxt, &routerrpc.SendPaymentRequest{
    91  			Amt:            1000,
    92  			Dest:           net.Alice.PubKey[:],
    93  			TimeoutSeconds: 60,
    94  			FeeLimitAtoms:  1000000,
    95  			MaxParts:       10,
    96  			Amp:            true,
    97  		},
    98  	)
    99  	require.NoError(t.t, err, "send payment failed")
   100  
   101  	update, err := payment.Recv()
   102  	require.NoError(t.t, err, "no payment in flight update")
   103  	require.Equal(t.t, lnrpc.Payment_IN_FLIGHT, update.Status,
   104  		"payment not inflight")
   105  
   106  	update, err = payment.Recv()
   107  	require.NoError(t.t, err, "no payment failed update")
   108  	require.Equal(t.t, lnrpc.Payment_FAILED, update.Status)
   109  	require.Len(t.t, update.Htlcs, 0, "expected no htlcs dispatched")
   110  
   111  	// Now that we're done, we cancel all our pending htlcs so that we
   112  	// can cleanup the channel with a coop close.
   113  	for _, sub := range subscriptions {
   114  		ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   115  		sub.cancel(ctxt, t.t)
   116  	}
   117  
   118  	ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   119  	aliceBobSub.cancel(ctxt, t.t)
   120  
   121  	err = assertNumActiveHtlcs([]*lntest.HarnessNode{
   122  		net.Alice, net.Bob,
   123  	}, 0)
   124  	require.NoError(t.t, err, "expected all htlcs canceled")
   125  
   126  	closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
   127  }
   128  
   129  type holdSubscription struct {
   130  	recipient           invoicesrpc.InvoicesClient
   131  	hash                lntypes.Hash
   132  	invSubscription     invoicesrpc.Invoices_SubscribeSingleInvoiceClient
   133  	paymentSubscription routerrpc.Router_SendPaymentV2Client
   134  }
   135  
   136  // cancel updates a hold invoice to cancel from the recipient and consumes
   137  // updates from the payer until it has reached a final, failed state.
   138  func (h *holdSubscription) cancel(ctx context.Context, t *testing.T) {
   139  	_, err := h.recipient.CancelInvoice(ctx, &invoicesrpc.CancelInvoiceMsg{
   140  		PaymentHash: h.hash[:],
   141  	})
   142  	require.NoError(t, err, "invoice cancel failed")
   143  
   144  	invUpdate, err := h.invSubscription.Recv()
   145  	require.NoError(t, err, "cancel invoice subscribe failed")
   146  	require.Equal(t, lnrpc.Invoice_CANCELED, invUpdate.State,
   147  		"expected invoice canceled")
   148  
   149  	// We expect one in flight update when our htlc is canceled back, and
   150  	// another when we fail the payment as a whole.
   151  	payUpdate, err := h.paymentSubscription.Recv()
   152  	require.NoError(t, err, "cancel payment subscribe failed")
   153  	require.Len(t, payUpdate.Htlcs, 1)
   154  	require.Equal(t, lnrpc.Payment_IN_FLIGHT, payUpdate.Status)
   155  
   156  	payUpdate, err = h.paymentSubscription.Recv()
   157  	require.NoError(t, err, "cancel payment subscribe failed")
   158  	require.Equal(t, lnrpc.Payment_FAILED, payUpdate.Status,
   159  		"expected payment failed")
   160  	require.Equal(t, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS,
   161  		payUpdate.FailureReason, "expected unknown details")
   162  }
   163  
   164  // acceptHoldInvoice adds a hold invoice to the recipient node, pays it from
   165  // the sender and asserts that we have reached the accepted state where htlcs
   166  // are locked in for the payment.
   167  func acceptHoldInvoice(ctx context.Context, t *testing.T, idx int, sender,
   168  	receiver *lntest.HarnessNode) *holdSubscription {
   169  
   170  	hash := [lntypes.HashSize]byte{byte(idx + 1)}
   171  
   172  	invoice, err := receiver.AddHoldInvoice(
   173  		ctx, &invoicesrpc.AddHoldInvoiceRequest{
   174  			ValueMAtoms: 10000,
   175  			Hash:        hash[:],
   176  		},
   177  	)
   178  	require.NoError(t, err, "couldn't add invoice")
   179  
   180  	invStream, err := receiver.InvoicesClient.SubscribeSingleInvoice(
   181  		ctx, &invoicesrpc.SubscribeSingleInvoiceRequest{
   182  			RHash: hash[:],
   183  		},
   184  	)
   185  	require.NoError(t, err, "could not subscribe to invoice")
   186  
   187  	inv, err := invStream.Recv()
   188  	require.NoError(t, err, "invoice open stream failed")
   189  	require.Equal(t, lnrpc.Invoice_OPEN, inv.State,
   190  		"expected open")
   191  
   192  	payStream, err := sender.RouterClient.SendPaymentV2(
   193  		ctx, &routerrpc.SendPaymentRequest{
   194  			PaymentRequest: invoice.PaymentRequest,
   195  			TimeoutSeconds: 60,
   196  			FeeLimitAtoms:  1000000,
   197  		},
   198  	)
   199  	require.NoError(t, err, "send payment failed")
   200  
   201  	// Finally, assert that we progress to an accepted state. We expect
   202  	// the payer to get one update for the creation of the payment, and
   203  	// another when a htlc is dispatched.
   204  	payment, err := payStream.Recv()
   205  	require.NoError(t, err, "payment in flight stream failed")
   206  	require.Equal(t, lnrpc.Payment_IN_FLIGHT, payment.Status)
   207  	require.Len(t, payment.Htlcs, 0)
   208  
   209  	payment, err = payStream.Recv()
   210  	require.NoError(t, err, "payment in flight stream failed")
   211  	require.Equal(t, lnrpc.Payment_IN_FLIGHT, payment.Status)
   212  	require.Len(t, payment.Htlcs, 1)
   213  
   214  	inv, err = invStream.Recv()
   215  	require.NoError(t, err, "invoice accepted stream failed")
   216  	require.Equal(t, lnrpc.Invoice_ACCEPTED, inv.State,
   217  		"expected accepted invoice")
   218  
   219  	return &holdSubscription{
   220  		recipient:           receiver.InvoicesClient,
   221  		hash:                hash,
   222  		invSubscription:     invStream,
   223  		paymentSubscription: payStream,
   224  	}
   225  }