github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_multi-hop-error-propagation_test.go (about) 1 package itest 2 3 import ( 4 "context" 5 "strings" 6 "time" 7 8 "github.com/decred/dcrlnd" 9 "github.com/decred/dcrlnd/lnrpc" 10 "github.com/decred/dcrlnd/lnrpc/routerrpc" 11 "github.com/decred/dcrlnd/lntest" 12 "github.com/decred/dcrlnd/lnwire" 13 ) 14 15 func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) { 16 ctxb := context.Background() 17 18 // In this test we wish to exercise the daemon's correct parsing, 19 // handling, and propagation of errors that occur while processing a 20 // multi-hop payment. 21 const chanAmt = defaultChanAmt 22 23 // First establish a channel with a capacity of 0.5 DCR between Alice 24 // and Bob. 25 chanPointAlice := openChannelAndAssert( 26 t, net, net.Alice, net.Bob, 27 lntest.OpenChannelParams{ 28 Amt: chanAmt, 29 }, 30 ) 31 if err := net.Alice.WaitForNetworkChannelOpen(chanPointAlice); err != nil { 32 t.Fatalf("channel not seen by alice before timeout: %v", err) 33 } 34 35 cType, err := channelCommitType(net.Alice, chanPointAlice) 36 if err != nil { 37 t.Fatalf("unable to get channel type: %v", err) 38 } 39 40 commitFee := calcStaticFee(cType, 0) 41 assertBaseBalance := func() { 42 // Alice has opened a channel with Bob with zero push amount, so 43 // it's remote balance is zero. 44 expBalanceAlice := &lnrpc.ChannelBalanceResponse{ 45 LocalBalance: &lnrpc.Amount{ 46 Atoms: uint64(chanAmt - commitFee), 47 Matoms: uint64(lnwire.NewMAtomsFromAtoms( 48 chanAmt - commitFee, 49 )), 50 }, 51 RemoteBalance: &lnrpc.Amount{}, 52 UnsettledLocalBalance: &lnrpc.Amount{}, 53 UnsettledRemoteBalance: &lnrpc.Amount{}, 54 PendingOpenLocalBalance: &lnrpc.Amount{}, 55 PendingOpenRemoteBalance: &lnrpc.Amount{}, 56 // Deprecated fields. 57 Balance: int64(chanAmt - commitFee), 58 } 59 assertChannelBalanceResp(t, net.Alice, expBalanceAlice) 60 61 // Bob has a channel with Alice and another with Carol, so it's 62 // local and remote balances are both chanAmt - commitFee. 63 expBalanceBob := &lnrpc.ChannelBalanceResponse{ 64 LocalBalance: &lnrpc.Amount{ 65 Atoms: uint64(chanAmt - commitFee), 66 Matoms: uint64(lnwire.NewMAtomsFromAtoms( 67 chanAmt - commitFee, 68 )), 69 }, 70 RemoteBalance: &lnrpc.Amount{ 71 Atoms: uint64(chanAmt - commitFee), 72 Matoms: uint64(lnwire.NewMAtomsFromAtoms( 73 chanAmt - commitFee, 74 )), 75 }, 76 UnsettledLocalBalance: &lnrpc.Amount{}, 77 UnsettledRemoteBalance: &lnrpc.Amount{}, 78 PendingOpenLocalBalance: &lnrpc.Amount{}, 79 PendingOpenRemoteBalance: &lnrpc.Amount{}, 80 // Deprecated fields. 81 Balance: int64(chanAmt - commitFee), 82 } 83 assertChannelBalanceResp(t, net.Bob, expBalanceBob) 84 } 85 86 // Since we'd like to test some multi-hop failure scenarios, we'll 87 // introduce another node into our test network: Carol. 88 carol := net.NewNode(t.t, "Carol", nil) 89 90 // Next, we'll create a connection from Bob to Carol, and open a 91 // channel between them so we have the topology: Alice -> Bob -> Carol. 92 // The channel created will be of lower capacity that the one created 93 // above. 94 net.ConnectNodes(t.t, net.Bob, carol) 95 const bobChanAmt = defaultChanAmt 96 chanPointBob := openChannelAndAssert( 97 t, net, net.Bob, carol, 98 lntest.OpenChannelParams{ 99 Amt: chanAmt, 100 }, 101 ) 102 103 // Ensure that Alice has Carol in her routing table before proceeding. 104 nodeInfoReq := &lnrpc.NodeInfoRequest{ 105 PubKey: carol.PubKeyStr, 106 } 107 checkTableTimeout := time.After(time.Second * 10) 108 checkTableTicker := time.NewTicker(100 * time.Millisecond) 109 defer checkTableTicker.Stop() 110 111 out: 112 // TODO(roasbeef): make into async hook for node announcements 113 for { 114 select { 115 case <-checkTableTicker.C: 116 ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) 117 _, err := net.Alice.GetNodeInfo(ctxt, nodeInfoReq) 118 if err != nil && strings.Contains(err.Error(), 119 "unable to find") { 120 121 continue 122 } 123 124 break out 125 case <-checkTableTimeout: 126 t.Fatalf("carol's node announcement didn't propagate within " + 127 "the timeout period") 128 } 129 } 130 131 // With the channels, open we can now start to test our multi-hop error 132 // scenarios. First, we'll generate an invoice from carol that we'll 133 // use to test some error cases. 134 const payAmt = 10000 135 invoiceReq := &lnrpc.Invoice{ 136 Memo: "kek99", 137 Value: payAmt, 138 } 139 ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) 140 carolInvoice, err := carol.AddInvoice(ctxt, invoiceReq) 141 if err != nil { 142 t.Fatalf("unable to generate carol invoice: %v", err) 143 } 144 145 carolPayReq, err := carol.DecodePayReq(ctxb, 146 &lnrpc.PayReqString{ 147 PayReq: carolInvoice.PaymentRequest, 148 }) 149 if err != nil { 150 t.Fatalf("unable to decode generated payment request: %v", err) 151 } 152 153 // Before we send the payment, ensure that the announcement of the new 154 // channel has been processed by Alice. 155 if err := net.Alice.WaitForNetworkChannelOpen(chanPointBob); err != nil { 156 t.Fatalf("channel not seen by alice before timeout: %v", err) 157 } 158 159 // Before we start sending payments, subscribe to htlc events for each 160 // node. 161 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 162 defer cancel() 163 164 aliceEvents, err := net.Alice.RouterClient.SubscribeHtlcEvents( 165 ctxt, &routerrpc.SubscribeHtlcEventsRequest{}, 166 ) 167 if err != nil { 168 t.Fatalf("could not subscribe events: %v", err) 169 } 170 171 bobEvents, err := net.Bob.RouterClient.SubscribeHtlcEvents( 172 ctxt, &routerrpc.SubscribeHtlcEventsRequest{}, 173 ) 174 if err != nil { 175 t.Fatalf("could not subscribe events: %v", err) 176 } 177 178 carolEvents, err := carol.RouterClient.SubscribeHtlcEvents( 179 ctxt, &routerrpc.SubscribeHtlcEventsRequest{}, 180 ) 181 if err != nil { 182 t.Fatalf("could not subscribe events: %v", err) 183 } 184 185 // For the first scenario, we'll test the cancellation of an HTLC with 186 // an unknown payment hash. 187 // TODO(roasbeef): return failure response rather than failing entire 188 // stream on payment error. 189 sendReq := &routerrpc.SendPaymentRequest{ 190 PaymentHash: makeFakePayHash(t), 191 Dest: carol.PubKey[:], 192 Amt: payAmt, 193 FinalCltvDelta: int32(carolPayReq.CltvExpiry), 194 TimeoutSeconds: 60, 195 FeeLimitMAtoms: noFeeLimitMAtoms, 196 MaxParts: 1, 197 } 198 sendAndAssertFailure( 199 t, net.Alice, 200 sendReq, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, 201 ) 202 assertLastHTLCError( 203 t, net.Alice, 204 lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, 205 ) 206 207 // We expect alice and bob to each have one forward and one forward 208 // fail event at this stage. 209 assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) 210 assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_FORWARD, bobEvents) 211 212 // Carol should have a link failure because the htlc failed on her 213 // incoming link. 214 assertLinkFailure( 215 t, routerrpc.HtlcEvent_RECEIVE, 216 routerrpc.FailureDetail_UNKNOWN_INVOICE, carolEvents, 217 ) 218 219 // The balances of all parties should be the same as initially since 220 // the HTLC was canceled. 221 assertBaseBalance() 222 223 // Next, we'll test the case of a recognized payHash but, an incorrect 224 // value on the extended HTLC. 225 htlcAmt := lnwire.NewMAtomsFromAtoms(1000) 226 sendReq = &routerrpc.SendPaymentRequest{ 227 PaymentHash: carolInvoice.RHash, 228 Dest: carol.PubKey[:], 229 Amt: int64(htlcAmt.ToAtoms()), // 10k satoshis are expected. 230 FinalCltvDelta: int32(carolPayReq.CltvExpiry), 231 TimeoutSeconds: 60, 232 FeeLimitMAtoms: noFeeLimitMAtoms, 233 MaxParts: 1, 234 } 235 sendAndAssertFailure( 236 t, net.Alice, 237 sendReq, lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, 238 ) 239 assertLastHTLCError( 240 t, net.Alice, 241 lnrpc.Failure_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, 242 ) 243 244 // We expect alice and bob to each have one forward and one forward 245 // fail event at this stage. 246 assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) 247 assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_FORWARD, bobEvents) 248 249 // Carol should have a link failure because the htlc failed on her 250 // incoming link. 251 assertLinkFailure( 252 t, routerrpc.HtlcEvent_RECEIVE, 253 routerrpc.FailureDetail_INVOICE_UNDERPAID, carolEvents, 254 ) 255 256 // The balances of all parties should be the same as initially since 257 // the HTLC was canceled. 258 assertBaseBalance() 259 260 // Next we'll test an error that occurs mid-route due to an outgoing 261 // link having insufficient capacity. In order to do so, we'll first 262 // need to unbalance the link connecting Bob<->Carol. 263 // 264 // To do so, we'll push most of the funds in the channel over to 265 // Alice's side, leaving on 10k atoms of available balance for bob. 266 // There's a max payment amount, so we'll have to do this 267 // incrementally. 268 chanReserve := int64(chanAmt / 100) 269 amtToSend := int64(chanAmt) - chanReserve - 20000 270 amtSent := int64(0) 271 for amtSent != amtToSend { 272 // We'll send in chunks of the max payment amount. If we're 273 // about to send too much, then we'll only send the amount 274 // remaining. 275 toSend := int64(dcrlnd.MaxPaymentMAtoms.ToAtoms()) 276 if toSend+amtSent > amtToSend { 277 toSend = amtToSend - amtSent 278 } 279 280 invoiceReq = &lnrpc.Invoice{ 281 Value: toSend, 282 } 283 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 284 carolInvoice2, err := carol.AddInvoice(ctxt, invoiceReq) 285 if err != nil { 286 t.Fatalf("unable to generate carol invoice: %v", err) 287 } 288 289 sendAndAssertSuccess( 290 t, net.Bob, &routerrpc.SendPaymentRequest{ 291 PaymentRequest: carolInvoice2.PaymentRequest, 292 TimeoutSeconds: 60, 293 FeeLimitMAtoms: noFeeLimitMAtoms, 294 MaxParts: 1, 295 }, 296 ) 297 298 // For each send bob makes, we need to check that bob has a 299 // forward and settle event for his send, and carol has a 300 // settle event for her receive. 301 assertHtlcEvents( 302 t, 1, 0, 1, routerrpc.HtlcEvent_SEND, bobEvents, 303 ) 304 assertHtlcEvents( 305 t, 0, 0, 1, routerrpc.HtlcEvent_RECEIVE, carolEvents, 306 ) 307 308 amtSent += toSend 309 } 310 311 // At this point, Alice has 50mil atoms on her side of the channel, 312 // but Bob only has 10k available on his side of the channel. So a 313 // payment from Alice to Carol worth 100k atoms should fail. 314 invoiceReq = &lnrpc.Invoice{ 315 Value: 100000, 316 IgnoreMaxInboundAmt: true, 317 } 318 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 319 carolInvoice3, err := carol.AddInvoice(ctxt, invoiceReq) 320 if err != nil { 321 t.Fatalf("unable to generate carol invoice: %v", err) 322 } 323 324 sendReq = &routerrpc.SendPaymentRequest{ 325 PaymentRequest: carolInvoice3.PaymentRequest, 326 TimeoutSeconds: 60, 327 FeeLimitMAtoms: noFeeLimitMAtoms, 328 MaxParts: 1, 329 } 330 sendAndAssertFailure( 331 t, net.Alice, 332 sendReq, lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE, 333 ) 334 assertLastHTLCError( 335 t, net.Alice, lnrpc.Failure_TEMPORARY_CHANNEL_FAILURE, 336 ) 337 338 // Alice should have a forwarding event and a forwarding failure. 339 assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) 340 341 // Bob should have a link failure because the htlc failed on his 342 // outgoing link. 343 assertLinkFailure( 344 t, routerrpc.HtlcEvent_FORWARD, 345 routerrpc.FailureDetail_INSUFFICIENT_BALANCE, bobEvents, 346 ) 347 348 // Generate new invoice to not pay same invoice twice. 349 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 350 carolInvoice, err = carol.AddInvoice(ctxt, invoiceReq) 351 if err != nil { 352 t.Fatalf("unable to generate carol invoice: %v", err) 353 } 354 355 // For our final test, we'll ensure that if a target link isn't 356 // available for what ever reason then the payment fails accordingly. 357 // 358 // We'll attempt to complete the original invoice we created with Carol 359 // above, but before we do so, Carol will go offline, resulting in a 360 // failed payment. 361 shutdownAndAssert(net, t, carol) 362 363 // Reset mission control to forget the temporary channel failure above. 364 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 365 _, err = net.Alice.RouterClient.ResetMissionControl( 366 ctxt, &routerrpc.ResetMissionControlRequest{}, 367 ) 368 if err != nil { 369 t.Fatalf("unable to reset mission control: %v", err) 370 } 371 372 sendAndAssertFailure( 373 t, net.Alice, 374 &routerrpc.SendPaymentRequest{ 375 PaymentRequest: carolInvoice.PaymentRequest, 376 TimeoutSeconds: 60, 377 FeeLimitMAtoms: noFeeLimitMAtoms, 378 MaxParts: 1, 379 }, 380 lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE, 381 ) 382 assertLastHTLCError(t, net.Alice, lnrpc.Failure_UNKNOWN_NEXT_PEER) 383 384 // Alice should have a forwarding event and subsequent fail. 385 assertHtlcEvents(t, 1, 1, 0, routerrpc.HtlcEvent_SEND, aliceEvents) 386 387 // Bob should have a link failure because he could not find the next 388 // peer. 389 assertLinkFailure( 390 t, routerrpc.HtlcEvent_FORWARD, 391 routerrpc.FailureDetail_NO_DETAIL, bobEvents, 392 ) 393 394 // Finally, immediately close the channel. This function will also 395 // block until the channel is closed and will additionally assert the 396 // relevant channel closing post conditions. 397 closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false) 398 399 // Force close Bob's final channel. 400 closeChannelAndAssert(t, net, net.Bob, chanPointBob, true) 401 402 // Cleanup by mining the force close and sweep transaction. 403 cleanupForceClose(t, net, net.Bob, chanPointBob) 404 } 405 406 // assertLinkFailure checks that the stream provided has a single link failure 407 // the the failure detail provided. 408 func assertLinkFailure(t *harnessTest, 409 eventType routerrpc.HtlcEvent_EventType, 410 failureDetail routerrpc.FailureDetail, 411 client routerrpc.Router_SubscribeHtlcEventsClient) { 412 413 event := assertEventAndType(t, eventType, client) 414 415 linkFail, ok := event.Event.(*routerrpc.HtlcEvent_LinkFailEvent) 416 if !ok { 417 t.Fatalf("expected forwarding failure, got: %T", linkFail) 418 } 419 420 if linkFail.LinkFailEvent.FailureDetail != failureDetail { 421 t.Fatalf("expected: %v, got: %v", failureDetail, 422 linkFail.LinkFailEvent.FailureDetail) 423 } 424 }