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 }