github.com/decred/dcrlnd@v0.7.6/invoices/invoiceregistry_test.go (about) 1 package invoices 2 3 import ( 4 "crypto/rand" 5 "math" 6 "testing" 7 "time" 8 9 "github.com/decred/dcrlnd/amp" 10 "github.com/decred/dcrlnd/chainntnfs" 11 "github.com/decred/dcrlnd/channeldb" 12 "github.com/decred/dcrlnd/clock" 13 "github.com/decred/dcrlnd/lntypes" 14 "github.com/decred/dcrlnd/lnwire" 15 "github.com/decred/dcrlnd/record" 16 "github.com/stretchr/testify/require" 17 ) 18 19 // TestSettleInvoice tests settling of an invoice and related notifications. 20 func TestSettleInvoice(t *testing.T) { 21 ctx := newTestContext(t) 22 defer ctx.cleanup() 23 24 allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) 25 require.Nil(t, err) 26 defer allSubscriptions.Cancel() 27 28 // Subscribe to the not yet existing invoice. 29 subscription, err := ctx.registry.SubscribeSingleInvoice(testInvoicePaymentHash) 30 if err != nil { 31 t.Fatal(err) 32 } 33 defer subscription.Cancel() 34 35 require.Equal(t, subscription.invoiceRef.PayHash(), &testInvoicePaymentHash) 36 37 // Give enough time for the invoice ntnf goroutine to complete 38 time.Sleep(time.Millisecond * 5) 39 40 // Add the invoice. 41 addIdx, err := ctx.registry.AddInvoice(testInvoice, testInvoicePaymentHash) 42 if err != nil { 43 t.Fatal(err) 44 } 45 46 if addIdx != 1 { 47 t.Fatalf("expected addIndex to start with 1, but got %v", 48 addIdx) 49 } 50 51 // We expect the open state to be sent to the single invoice subscriber. 52 select { 53 case update := <-subscription.Updates: 54 if update.State != channeldb.ContractOpen { 55 t.Fatalf("expected state ContractOpen, but got %v", 56 update.State) 57 } 58 case <-time.After(testTimeout): 59 t.Fatal("no update received") 60 } 61 62 // We expect a new invoice notification to be sent out. 63 select { 64 case newInvoice := <-allSubscriptions.NewInvoices: 65 if newInvoice.State != channeldb.ContractOpen { 66 t.Fatalf("expected state ContractOpen, but got %v", 67 newInvoice.State) 68 } 69 case <-time.After(testTimeout): 70 t.Fatal("no update received") 71 } 72 73 hodlChan := make(chan interface{}, 1) 74 75 // Try to settle invoice with an htlc that expires too soon. 76 resolution, err := ctx.registry.NotifyExitHopHtlc( 77 testInvoicePaymentHash, testInvoice.Terms.Value, 78 uint32(testCurrentHeight)+testInvoiceCltvDelta-1, 79 testCurrentHeight, getCircuitKey(10), hodlChan, testPayload, 80 ) 81 if err != nil { 82 t.Fatal(err) 83 } 84 require.NotNil(t, resolution) 85 failResolution := checkFailResolution( 86 t, resolution, ResultExpiryTooSoon, 87 ) 88 require.Equal(t, testCurrentHeight, failResolution.AcceptHeight) 89 90 // Settle invoice with a slightly higher amount. 91 amtPaid := lnwire.MilliAtom(100500) 92 resolution, err = ctx.registry.NotifyExitHopHtlc( 93 testInvoicePaymentHash, amtPaid, testHtlcExpiry, 94 testCurrentHeight, getCircuitKey(0), hodlChan, 95 testPayload, 96 ) 97 if err != nil { 98 t.Fatal(err) 99 } 100 require.NotNil(t, resolution) 101 settleResolution := checkSettleResolution( 102 t, resolution, testInvoicePreimage, 103 ) 104 require.Equal(t, ResultSettled, settleResolution.Outcome) 105 106 // We expect the settled state to be sent to the single invoice 107 // subscriber. 108 select { 109 case update := <-subscription.Updates: 110 if update.State != channeldb.ContractSettled { 111 t.Fatalf("expected state ContractOpen, but got %v", 112 update.State) 113 } 114 if update.AmtPaid != amtPaid { 115 t.Fatal("invoice AmtPaid incorrect") 116 } 117 case <-time.After(testTimeout): 118 t.Fatal("no update received") 119 } 120 121 // We expect a settled notification to be sent out. 122 select { 123 case settledInvoice := <-allSubscriptions.SettledInvoices: 124 if settledInvoice.State != channeldb.ContractSettled { 125 t.Fatalf("expected state ContractOpen, but got %v", 126 settledInvoice.State) 127 } 128 case <-time.After(testTimeout): 129 t.Fatal("no update received") 130 } 131 132 // Try to settle again with the same htlc id. We need this idempotent 133 // behaviour after a restart. 134 resolution, err = ctx.registry.NotifyExitHopHtlc( 135 testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight, 136 getCircuitKey(0), hodlChan, testPayload, 137 ) 138 if err != nil { 139 t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err) 140 } 141 require.NotNil(t, resolution) 142 settleResolution = checkSettleResolution( 143 t, resolution, testInvoicePreimage, 144 ) 145 require.Equal(t, ResultReplayToSettled, settleResolution.Outcome) 146 147 // Try to settle again with a new higher-valued htlc. This payment 148 // should also be accepted, to prevent any change in behaviour for a 149 // paid invoice that may open up a probe vector. 150 resolution, err = ctx.registry.NotifyExitHopHtlc( 151 testInvoicePaymentHash, amtPaid+600, testHtlcExpiry, testCurrentHeight, 152 getCircuitKey(1), hodlChan, testPayload, 153 ) 154 if err != nil { 155 t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err) 156 } 157 require.NotNil(t, resolution) 158 settleResolution = checkSettleResolution( 159 t, resolution, testInvoicePreimage, 160 ) 161 require.Equal(t, ResultDuplicateToSettled, settleResolution.Outcome) 162 163 // Try to settle again with a lower amount. This should fail just as it 164 // would have failed if it were the first payment. 165 resolution, err = ctx.registry.NotifyExitHopHtlc( 166 testInvoicePaymentHash, amtPaid-600, testHtlcExpiry, testCurrentHeight, 167 getCircuitKey(2), hodlChan, testPayload, 168 ) 169 if err != nil { 170 t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err) 171 } 172 require.NotNil(t, resolution) 173 checkFailResolution(t, resolution, ResultAmountTooLow) 174 175 // Check that settled amount is equal to the sum of values of the htlcs 176 // 0 and 1. 177 inv, err := ctx.registry.LookupInvoice(testInvoicePaymentHash) 178 if err != nil { 179 t.Fatal(err) 180 } 181 if inv.AmtPaid != amtPaid+amtPaid+600 { 182 t.Fatalf("amount incorrect: expected %v got %v", 183 amtPaid+amtPaid+600, inv.AmtPaid) 184 } 185 186 // Try to cancel. 187 err = ctx.registry.CancelInvoice(testInvoicePaymentHash) 188 if err != channeldb.ErrInvoiceAlreadySettled { 189 t.Fatal("expected cancelation of a settled invoice to fail") 190 } 191 192 // As this is a direct sette, we expect nothing on the hodl chan. 193 select { 194 case <-hodlChan: 195 t.Fatal("unexpected resolution") 196 default: 197 } 198 } 199 200 func testCancelInvoice(t *testing.T, gc bool) { 201 ctx := newTestContext(t) 202 defer ctx.cleanup() 203 204 // If set to true, then also delete the invoice from the DB after 205 // cancellation. 206 ctx.registry.cfg.GcCanceledInvoicesOnTheFly = gc 207 208 allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) 209 require.Nil(t, err) 210 defer allSubscriptions.Cancel() 211 212 // Try to cancel the not yet existing invoice. This should fail. 213 err = ctx.registry.CancelInvoice(testInvoicePaymentHash) 214 if err != channeldb.ErrInvoiceNotFound { 215 t.Fatalf("expected ErrInvoiceNotFound, but got %v", err) 216 } 217 218 // Subscribe to the not yet existing invoice. 219 subscription, err := ctx.registry.SubscribeSingleInvoice(testInvoicePaymentHash) 220 if err != nil { 221 t.Fatal(err) 222 } 223 defer subscription.Cancel() 224 225 require.Equal(t, subscription.invoiceRef.PayHash(), &testInvoicePaymentHash) 226 227 // Give enough time for the invoice ntnf goroutine to complete 228 time.Sleep(time.Millisecond * 5) 229 230 // Add the invoice. 231 amt := lnwire.MilliAtom(100000) 232 _, err = ctx.registry.AddInvoice(testInvoice, testInvoicePaymentHash) 233 if err != nil { 234 t.Fatal(err) 235 } 236 237 // We expect the open state to be sent to the single invoice subscriber. 238 select { 239 case update := <-subscription.Updates: 240 if update.State != channeldb.ContractOpen { 241 t.Fatalf( 242 "expected state ContractOpen, but got %v", 243 update.State, 244 ) 245 } 246 case <-time.After(testTimeout): 247 t.Fatal("no update received") 248 } 249 250 // We expect a new invoice notification to be sent out. 251 select { 252 case newInvoice := <-allSubscriptions.NewInvoices: 253 if newInvoice.State != channeldb.ContractOpen { 254 t.Fatalf( 255 "expected state ContractOpen, but got %v", 256 newInvoice.State, 257 ) 258 } 259 case <-time.After(testTimeout): 260 t.Fatal("no update received") 261 } 262 263 // Cancel invoice. 264 err = ctx.registry.CancelInvoice(testInvoicePaymentHash) 265 if err != nil { 266 t.Fatal(err) 267 } 268 269 // We expect the canceled state to be sent to the single invoice 270 // subscriber. 271 select { 272 case update := <-subscription.Updates: 273 if update.State != channeldb.ContractCanceled { 274 t.Fatalf( 275 "expected state ContractCanceled, but got %v", 276 update.State, 277 ) 278 } 279 case <-time.After(testTimeout): 280 t.Fatal("no update received") 281 } 282 283 if gc { 284 // Check that the invoice has been deleted from the db. 285 _, err = ctx.cdb.LookupInvoice( 286 channeldb.InvoiceRefByHash(testInvoicePaymentHash), 287 ) 288 require.Error(t, err) 289 } 290 291 // We expect no cancel notification to be sent to all invoice 292 // subscribers (backwards compatibility). 293 294 // Try to cancel again. Expect that we report ErrInvoiceNotFound if the 295 // invoice has been garbage collected (since the invoice has been 296 // deleted when it was canceled), and no error otherwise. 297 err = ctx.registry.CancelInvoice(testInvoicePaymentHash) 298 299 if gc { 300 require.Error(t, err, channeldb.ErrInvoiceNotFound) 301 } else { 302 require.NoError(t, err) 303 } 304 305 // Notify arrival of a new htlc paying to this invoice. This should 306 // result in a cancel resolution. 307 hodlChan := make(chan interface{}) 308 resolution, err := ctx.registry.NotifyExitHopHtlc( 309 testInvoicePaymentHash, amt, testHtlcExpiry, testCurrentHeight, 310 getCircuitKey(0), hodlChan, testPayload, 311 ) 312 if err != nil { 313 t.Fatal("expected settlement of a canceled invoice to succeed") 314 } 315 require.NotNil(t, resolution) 316 317 // If the invoice has been deleted (or not present) then we expect the 318 // outcome to be ResultInvoiceNotFound instead of when the invoice is 319 // in our database in which case we expect ResultInvoiceAlreadyCanceled. 320 var failResolution *HtlcFailResolution 321 if gc { 322 failResolution = checkFailResolution( 323 t, resolution, ResultInvoiceNotFound, 324 ) 325 } else { 326 failResolution = checkFailResolution( 327 t, resolution, ResultInvoiceAlreadyCanceled, 328 ) 329 } 330 331 require.Equal(t, testCurrentHeight, failResolution.AcceptHeight) 332 } 333 334 // TestCancelInvoice tests cancelation of an invoice and related notifications. 335 func TestCancelInvoice(t *testing.T) { 336 // Test cancellation both with garbage collection (meaning that canceled 337 // invoice will be deleted) and without (meain it'll be kept). 338 t.Run("garbage collect", func(t *testing.T) { 339 testCancelInvoice(t, true) 340 }) 341 342 t.Run("no garbage collect", func(t *testing.T) { 343 testCancelInvoice(t, false) 344 }) 345 } 346 347 // TestSettleHoldInvoice tests settling of a hold invoice and related 348 // notifications. 349 func TestSettleHoldInvoice(t *testing.T) { 350 defer timeout()() 351 352 cdb, cleanup, err := newTestChannelDB(clock.NewTestClock(time.Time{})) 353 if err != nil { 354 t.Fatalf("unable to create db: %v", err) 355 } 356 defer cleanup() 357 358 // Instantiate and start the invoice ctx.registry. 359 cfg := RegistryConfig{ 360 FinalCltvRejectDelta: testFinalCltvRejectDelta, 361 Clock: clock.NewTestClock(testTime), 362 } 363 364 expiryWatcher := NewInvoiceExpiryWatcher( 365 cfg.Clock, 0, uint32(testCurrentHeight), nil, newMockNotifier(), 366 ) 367 registry := NewRegistry(cdb, expiryWatcher, &cfg) 368 369 err = registry.Start() 370 if err != nil { 371 t.Fatal(err) 372 } 373 defer registry.Stop() 374 375 allSubscriptions, err := registry.SubscribeNotifications(0, 0) 376 require.Nil(t, err) 377 defer allSubscriptions.Cancel() 378 379 // Subscribe to the not yet existing invoice. 380 subscription, err := registry.SubscribeSingleInvoice(testInvoicePaymentHash) 381 if err != nil { 382 t.Fatal(err) 383 } 384 defer subscription.Cancel() 385 386 require.Equal(t, subscription.invoiceRef.PayHash(), &testInvoicePaymentHash) 387 388 // Give some time for the subscription to be processed. 389 time.Sleep(time.Millisecond * 5) 390 391 // Add the invoice. 392 _, err = registry.AddInvoice(testHodlInvoice, testInvoicePaymentHash) 393 if err != nil { 394 t.Fatal(err) 395 } 396 397 // We expect the open state to be sent to the single invoice subscriber. 398 update := <-subscription.Updates 399 if update.State != channeldb.ContractOpen { 400 t.Fatalf("expected state ContractOpen, but got %v", 401 update.State) 402 } 403 404 // We expect a new invoice notification to be sent out. 405 newInvoice := <-allSubscriptions.NewInvoices 406 if newInvoice.State != channeldb.ContractOpen { 407 t.Fatalf("expected state ContractOpen, but got %v", 408 newInvoice.State) 409 } 410 411 // Use slightly higher amount for accept/settle. 412 amtPaid := lnwire.MilliAtom(100500) 413 414 hodlChan := make(chan interface{}, 1) 415 416 // NotifyExitHopHtlc without a preimage present in the invoice registry 417 // should be possible. 418 resolution, err := registry.NotifyExitHopHtlc( 419 testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight, 420 getCircuitKey(0), hodlChan, testPayload, 421 ) 422 if err != nil { 423 t.Fatalf("expected settle to succeed but got %v", err) 424 } 425 if resolution != nil { 426 t.Fatalf("expected htlc to be held") 427 } 428 429 // Test idempotency. 430 resolution, err = registry.NotifyExitHopHtlc( 431 testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight, 432 getCircuitKey(0), hodlChan, testPayload, 433 ) 434 if err != nil { 435 t.Fatalf("expected settle to succeed but got %v", err) 436 } 437 if resolution != nil { 438 t.Fatalf("expected htlc to be held") 439 } 440 441 // Test replay at a higher height. We expect the same result because it 442 // is a replay. 443 resolution, err = registry.NotifyExitHopHtlc( 444 testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight+10, 445 getCircuitKey(0), hodlChan, testPayload, 446 ) 447 if err != nil { 448 t.Fatalf("expected settle to succeed but got %v", err) 449 } 450 if resolution != nil { 451 t.Fatalf("expected htlc to be held") 452 } 453 454 // Test a new htlc coming in that doesn't meet the final cltv delta 455 // requirement. It should be rejected. 456 resolution, err = registry.NotifyExitHopHtlc( 457 testInvoicePaymentHash, amtPaid, 1, testCurrentHeight, 458 getCircuitKey(1), hodlChan, testPayload, 459 ) 460 if err != nil { 461 t.Fatalf("expected settle to succeed but got %v", err) 462 } 463 require.NotNil(t, resolution) 464 checkFailResolution(t, resolution, ResultExpiryTooSoon) 465 466 // We expect the accepted state to be sent to the single invoice 467 // subscriber. For all invoice subscribers, we don't expect an update. 468 // Those only get notified on settle. 469 update = <-subscription.Updates 470 if update.State != channeldb.ContractAccepted { 471 t.Fatalf("expected state ContractAccepted, but got %v", 472 update.State) 473 } 474 if update.AmtPaid != amtPaid { 475 t.Fatal("invoice AmtPaid incorrect") 476 } 477 478 // Settling with preimage should succeed. 479 err = registry.SettleHodlInvoice(testInvoicePreimage) 480 if err != nil { 481 t.Fatal("expected set preimage to succeed") 482 } 483 484 htlcResolution := (<-hodlChan).(HtlcResolution) 485 require.NotNil(t, htlcResolution) 486 settleResolution := checkSettleResolution( 487 t, htlcResolution, testInvoicePreimage, 488 ) 489 require.Equal(t, testCurrentHeight, settleResolution.AcceptHeight) 490 require.Equal(t, ResultSettled, settleResolution.Outcome) 491 492 // We expect a settled notification to be sent out for both all and 493 // single invoice subscribers. 494 settledInvoice := <-allSubscriptions.SettledInvoices 495 if settledInvoice.State != channeldb.ContractSettled { 496 t.Fatalf("expected state ContractSettled, but got %v", 497 settledInvoice.State) 498 } 499 if settledInvoice.AmtPaid != amtPaid { 500 t.Fatalf("expected amount to be %v, but got %v", 501 amtPaid, settledInvoice.AmtPaid) 502 } 503 504 update = <-subscription.Updates 505 if update.State != channeldb.ContractSettled { 506 t.Fatalf("expected state ContractSettled, but got %v", 507 update.State) 508 } 509 510 // Idempotency. 511 err = registry.SettleHodlInvoice(testInvoicePreimage) 512 if err != channeldb.ErrInvoiceAlreadySettled { 513 t.Fatalf("expected ErrInvoiceAlreadySettled but got %v", err) 514 } 515 516 // Try to cancel. 517 err = registry.CancelInvoice(testInvoicePaymentHash) 518 if err == nil { 519 t.Fatal("expected cancelation of a settled invoice to fail") 520 } 521 } 522 523 // TestCancelHoldInvoice tests canceling of a hold invoice and related 524 // notifications. 525 func TestCancelHoldInvoice(t *testing.T) { 526 defer timeout()() 527 528 cdb, cleanup, err := newTestChannelDB(clock.NewTestClock(time.Time{})) 529 if err != nil { 530 t.Fatal(err) 531 } 532 defer cleanup() 533 534 // Instantiate and start the invoice ctx.registry. 535 cfg := RegistryConfig{ 536 FinalCltvRejectDelta: testFinalCltvRejectDelta, 537 Clock: clock.NewTestClock(testTime), 538 } 539 expiryWatcher := NewInvoiceExpiryWatcher( 540 cfg.Clock, 0, uint32(testCurrentHeight), nil, newMockNotifier(), 541 ) 542 registry := NewRegistry(cdb, expiryWatcher, &cfg) 543 544 err = registry.Start() 545 if err != nil { 546 t.Fatal(err) 547 } 548 defer func() { 549 if err := registry.Stop(); err != nil { 550 t.Fatalf("failed to stop invoice registry: %v", err) 551 } 552 }() 553 554 // Add the invoice. 555 _, err = registry.AddInvoice(testHodlInvoice, testInvoicePaymentHash) 556 if err != nil { 557 t.Fatal(err) 558 } 559 560 amtPaid := lnwire.MilliAtom(100000) 561 hodlChan := make(chan interface{}, 1) 562 563 // NotifyExitHopHtlc without a preimage present in the invoice registry 564 // should be possible. 565 resolution, err := registry.NotifyExitHopHtlc( 566 testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight, 567 getCircuitKey(0), hodlChan, testPayload, 568 ) 569 if err != nil { 570 t.Fatalf("expected settle to succeed but got %v", err) 571 } 572 if resolution != nil { 573 t.Fatalf("expected htlc to be held") 574 } 575 576 // Cancel invoice. 577 err = registry.CancelInvoice(testInvoicePaymentHash) 578 if err != nil { 579 t.Fatal("cancel invoice failed") 580 } 581 582 htlcResolution := (<-hodlChan).(HtlcResolution) 583 require.NotNil(t, htlcResolution) 584 checkFailResolution(t, htlcResolution, ResultCanceled) 585 586 // Offering the same htlc again at a higher height should still result 587 // in a rejection. The accept height is expected to be the original 588 // accept height. 589 resolution, err = registry.NotifyExitHopHtlc( 590 testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight+1, 591 getCircuitKey(0), hodlChan, testPayload, 592 ) 593 if err != nil { 594 t.Fatalf("expected settle to succeed but got %v", err) 595 } 596 require.NotNil(t, resolution) 597 failResolution := checkFailResolution( 598 t, resolution, ResultReplayToCanceled, 599 ) 600 require.Equal(t, testCurrentHeight, failResolution.AcceptHeight) 601 } 602 603 // TestUnknownInvoice tests that invoice registry returns an error when the 604 // invoice is unknown. This is to guard against returning a cancel htlc 605 // resolution for forwarded htlcs. In the link, NotifyExitHopHtlc is only called 606 // if we are the exit hop, but in htlcIncomingContestResolver it is called with 607 // forwarded htlc hashes as well. 608 func TestUnknownInvoice(t *testing.T) { 609 ctx := newTestContext(t) 610 defer ctx.cleanup() 611 612 // Notify arrival of a new htlc paying to this invoice. This should 613 // succeed. 614 hodlChan := make(chan interface{}) 615 amt := lnwire.MilliAtom(100000) 616 resolution, err := ctx.registry.NotifyExitHopHtlc( 617 testInvoicePaymentHash, amt, testHtlcExpiry, testCurrentHeight, 618 getCircuitKey(0), hodlChan, testPayload, 619 ) 620 if err != nil { 621 t.Fatal("unexpected error") 622 } 623 require.NotNil(t, resolution) 624 checkFailResolution(t, resolution, ResultInvoiceNotFound) 625 } 626 627 // TestKeySend tests receiving a spontaneous payment with and without keysend 628 // enabled. 629 func TestKeySend(t *testing.T) { 630 t.Run("enabled", func(t *testing.T) { 631 testKeySend(t, true) 632 }) 633 t.Run("disabled", func(t *testing.T) { 634 testKeySend(t, false) 635 }) 636 } 637 638 // testKeySend is the inner test function that tests keysend for a particular 639 // enabled state on the receiver end. 640 func testKeySend(t *testing.T, keySendEnabled bool) { 641 defer timeout()() 642 643 ctx := newTestContext(t) 644 defer ctx.cleanup() 645 646 ctx.registry.cfg.AcceptKeySend = keySendEnabled 647 648 allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) 649 require.Nil(t, err) 650 defer allSubscriptions.Cancel() 651 652 hodlChan := make(chan interface{}, 1) 653 654 amt := lnwire.MilliAtom(1000) 655 expiry := uint32(testCurrentHeight + 20) 656 657 // Create key for keysend. 658 preimage := lntypes.Preimage{1, 2, 3} 659 hash := preimage.Hash() 660 661 // Try to settle invoice with an invalid keysend htlc. 662 invalidKeySendPayload := &mockPayload{ 663 customRecords: map[uint64][]byte{ 664 record.KeySendType: {1, 2, 3}, 665 }, 666 } 667 668 resolution, err := ctx.registry.NotifyExitHopHtlc( 669 hash, amt, expiry, 670 testCurrentHeight, getCircuitKey(10), hodlChan, 671 invalidKeySendPayload, 672 ) 673 if err != nil { 674 t.Fatal(err) 675 } 676 require.NotNil(t, resolution) 677 678 if !keySendEnabled { 679 checkFailResolution(t, resolution, ResultInvoiceNotFound) 680 } else { 681 checkFailResolution(t, resolution, ResultKeySendError) 682 } 683 684 // Try to settle invoice with a valid keysend htlc. 685 keySendPayload := &mockPayload{ 686 customRecords: map[uint64][]byte{ 687 record.KeySendType: preimage[:], 688 }, 689 } 690 691 resolution, err = ctx.registry.NotifyExitHopHtlc( 692 hash, amt, expiry, 693 testCurrentHeight, getCircuitKey(10), hodlChan, keySendPayload, 694 ) 695 if err != nil { 696 t.Fatal(err) 697 } 698 699 // Expect a cancel resolution if keysend is disabled. 700 if !keySendEnabled { 701 checkFailResolution(t, resolution, ResultInvoiceNotFound) 702 return 703 } 704 705 checkSubscription := func() { 706 // We expect a new invoice notification to be sent out. 707 newInvoice := <-allSubscriptions.NewInvoices 708 require.Equal(t, newInvoice.State, channeldb.ContractOpen) 709 710 // We expect a settled notification to be sent out. 711 settledInvoice := <-allSubscriptions.SettledInvoices 712 require.Equal(t, settledInvoice.State, channeldb.ContractSettled) 713 } 714 715 checkSettleResolution(t, resolution, preimage) 716 checkSubscription() 717 718 // Replay the same keysend payment. We expect an identical resolution, 719 // but no event should be generated. 720 resolution, err = ctx.registry.NotifyExitHopHtlc( 721 hash, amt, expiry, 722 testCurrentHeight, getCircuitKey(10), hodlChan, keySendPayload, 723 ) 724 require.Nil(t, err) 725 checkSettleResolution(t, resolution, preimage) 726 727 select { 728 case <-allSubscriptions.NewInvoices: 729 t.Fatalf("replayed keysend should not generate event") 730 case <-time.After(time.Second): 731 } 732 733 // Finally, test that we can properly fulfill a second keysend payment 734 // with a unique preiamge. 735 preimage2 := lntypes.Preimage{1, 2, 3, 4} 736 hash2 := preimage2.Hash() 737 738 keySendPayload2 := &mockPayload{ 739 customRecords: map[uint64][]byte{ 740 record.KeySendType: preimage2[:], 741 }, 742 } 743 744 resolution, err = ctx.registry.NotifyExitHopHtlc( 745 hash2, amt, expiry, 746 testCurrentHeight, getCircuitKey(20), hodlChan, keySendPayload2, 747 ) 748 require.Nil(t, err) 749 750 checkSettleResolution(t, resolution, preimage2) 751 checkSubscription() 752 } 753 754 // TestHoldKeysend tests receiving a spontaneous payment that is held. 755 func TestHoldKeysend(t *testing.T) { 756 t.Run("settle", func(t *testing.T) { 757 testHoldKeysend(t, false) 758 }) 759 t.Run("timeout", func(t *testing.T) { 760 testHoldKeysend(t, true) 761 }) 762 } 763 764 // testHoldKeysend is the inner test function that tests hold-keysend. 765 func testHoldKeysend(t *testing.T, timeoutKeysend bool) { 766 defer timeout()() 767 768 const holdDuration = time.Minute 769 770 ctx := newTestContext(t) 771 defer ctx.cleanup() 772 773 ctx.registry.cfg.AcceptKeySend = true 774 ctx.registry.cfg.KeysendHoldTime = holdDuration 775 776 allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) 777 require.Nil(t, err) 778 defer allSubscriptions.Cancel() 779 780 hodlChan := make(chan interface{}, 1) 781 782 amt := lnwire.MilliAtom(1000) 783 expiry := uint32(testCurrentHeight + 20) 784 785 // Create key for keysend. 786 preimage := lntypes.Preimage{1, 2, 3} 787 hash := preimage.Hash() 788 789 // Try to settle invoice with a valid keysend htlc. 790 keysendPayload := &mockPayload{ 791 customRecords: map[uint64][]byte{ 792 record.KeySendType: preimage[:], 793 }, 794 } 795 796 resolution, err := ctx.registry.NotifyExitHopHtlc( 797 hash, amt, expiry, 798 testCurrentHeight, getCircuitKey(10), hodlChan, keysendPayload, 799 ) 800 if err != nil { 801 t.Fatal(err) 802 } 803 804 // No immediate resolution is expected. 805 require.Nil(t, resolution, "expected hold resolution") 806 807 // We expect a new invoice notification to be sent out. 808 newInvoice := <-allSubscriptions.NewInvoices 809 if newInvoice.State != channeldb.ContractOpen { 810 t.Fatalf("expected state ContractOpen, but got %v", 811 newInvoice.State) 812 } 813 814 // We expect no further invoice notifications yet (on the all invoices 815 // subscription). 816 select { 817 case <-allSubscriptions.NewInvoices: 818 t.Fatalf("no invoice update expected") 819 case <-time.After(100 * time.Millisecond): 820 } 821 822 if timeoutKeysend { 823 // Advance the clock to just past the hold duration. 824 ctx.clock.SetTime(ctx.clock.Now().Add( 825 holdDuration + time.Millisecond), 826 ) 827 828 // Expect the keysend payment to be failed. 829 res := <-hodlChan 830 failResolution, ok := res.(*HtlcFailResolution) 831 require.Truef( 832 t, ok, "expected fail resolution, got: %T", 833 resolution, 834 ) 835 require.Equal( 836 t, ResultCanceled, failResolution.Outcome, 837 "expected keysend payment to be failed", 838 ) 839 840 return 841 } 842 843 // Settle keysend payment manually. 844 require.Nil(t, ctx.registry.SettleHodlInvoice( 845 *newInvoice.Terms.PaymentPreimage, 846 )) 847 848 // We expect a settled notification to be sent out. 849 settledInvoice := <-allSubscriptions.SettledInvoices 850 require.Equal(t, settledInvoice.State, channeldb.ContractSettled) 851 } 852 853 // TestMppPayment tests settling of an invoice with multiple partial payments. 854 // It covers the case where there is a mpp timeout before the whole invoice is 855 // paid and the case where the invoice is settled in time. 856 func TestMppPayment(t *testing.T) { 857 defer timeout()() 858 859 ctx := newTestContext(t) 860 defer ctx.cleanup() 861 862 // Add the invoice. 863 _, err := ctx.registry.AddInvoice(testInvoice, testInvoicePaymentHash) 864 if err != nil { 865 t.Fatal(err) 866 } 867 868 mppPayload := &mockPayload{ 869 mpp: record.NewMPP(testInvoiceAmt, [32]byte{}), 870 } 871 872 // Send htlc 1. 873 hodlChan1 := make(chan interface{}, 1) 874 resolution, err := ctx.registry.NotifyExitHopHtlc( 875 testInvoicePaymentHash, testInvoice.Terms.Value/2, 876 testHtlcExpiry, 877 testCurrentHeight, getCircuitKey(10), hodlChan1, mppPayload, 878 ) 879 if err != nil { 880 t.Fatal(err) 881 } 882 if resolution != nil { 883 t.Fatal("expected no direct resolution") 884 } 885 886 // Simulate mpp timeout releasing htlc 1. 887 ctx.clock.SetTime(testTime.Add(30 * time.Second)) 888 889 htlcResolution := (<-hodlChan1).(HtlcResolution) 890 failResolution, ok := htlcResolution.(*HtlcFailResolution) 891 if !ok { 892 t.Fatalf("expected fail resolution, got: %T", 893 resolution) 894 } 895 if failResolution.Outcome != ResultMppTimeout { 896 t.Fatalf("expected mpp timeout, got: %v", 897 failResolution.Outcome) 898 } 899 900 // Send htlc 2. 901 hodlChan2 := make(chan interface{}, 1) 902 resolution, err = ctx.registry.NotifyExitHopHtlc( 903 testInvoicePaymentHash, testInvoice.Terms.Value/2, 904 testHtlcExpiry, 905 testCurrentHeight, getCircuitKey(11), hodlChan2, mppPayload, 906 ) 907 if err != nil { 908 t.Fatal(err) 909 } 910 if resolution != nil { 911 t.Fatal("expected no direct resolution") 912 } 913 914 // Send htlc 3. 915 hodlChan3 := make(chan interface{}, 1) 916 resolution, err = ctx.registry.NotifyExitHopHtlc( 917 testInvoicePaymentHash, testInvoice.Terms.Value/2, 918 testHtlcExpiry, 919 testCurrentHeight, getCircuitKey(12), hodlChan3, mppPayload, 920 ) 921 if err != nil { 922 t.Fatal(err) 923 } 924 settleResolution, ok := resolution.(*HtlcSettleResolution) 925 if !ok { 926 t.Fatalf("expected settle resolution, got: %T", 927 htlcResolution) 928 } 929 if settleResolution.Outcome != ResultSettled { 930 t.Fatalf("expected result settled, got: %v", 931 settleResolution.Outcome) 932 } 933 934 // Check that settled amount is equal to the sum of values of the htlcs 935 // 2 and 3. 936 inv, err := ctx.registry.LookupInvoice(testInvoicePaymentHash) 937 if err != nil { 938 t.Fatal(err) 939 } 940 if inv.State != channeldb.ContractSettled { 941 t.Fatal("expected invoice to be settled") 942 } 943 if inv.AmtPaid != testInvoice.Terms.Value { 944 t.Fatalf("amount incorrect, expected %v but got %v", 945 testInvoice.Terms.Value, inv.AmtPaid) 946 } 947 } 948 949 // Tests that invoices are canceled after expiration. 950 func TestInvoiceExpiryWithRegistry(t *testing.T) { 951 t.Parallel() 952 953 cdb, cleanup, err := newTestChannelDB(clock.NewTestClock(time.Time{})) 954 defer cleanup() 955 956 if err != nil { 957 t.Fatal(err) 958 } 959 960 testClock := clock.NewTestClock(testTime) 961 962 cfg := RegistryConfig{ 963 FinalCltvRejectDelta: testFinalCltvRejectDelta, 964 Clock: testClock, 965 } 966 967 expiryWatcher := NewInvoiceExpiryWatcher( 968 cfg.Clock, 0, uint32(testCurrentHeight), nil, newMockNotifier(), 969 ) 970 registry := NewRegistry(cdb, expiryWatcher, &cfg) 971 972 // First prefill the Channel DB with some pre-existing invoices, 973 // half of them still pending, half of them expired. 974 const numExpired = 5 975 const numPending = 5 976 existingInvoices := generateInvoiceExpiryTestData( 977 t, testTime, 0, numExpired, numPending, 978 ) 979 980 var expectedCancellations []lntypes.Hash 981 982 for paymentHash, expiredInvoice := range existingInvoices.expiredInvoices { 983 if _, err := cdb.AddInvoice(expiredInvoice, paymentHash); err != nil { 984 t.Fatalf("cannot add invoice to channel db: %v", err) 985 } 986 expectedCancellations = append(expectedCancellations, paymentHash) 987 } 988 989 for paymentHash, pendingInvoice := range existingInvoices.pendingInvoices { 990 if _, err := cdb.AddInvoice(pendingInvoice, paymentHash); err != nil { 991 t.Fatalf("cannot add invoice to channel db: %v", err) 992 } 993 } 994 995 if err = registry.Start(); err != nil { 996 t.Fatalf("cannot start registry: %v", err) 997 } 998 999 // Now generate pending and invoices and add them to the registry while 1000 // it is up and running. We'll manipulate the clock to let them expire. 1001 newInvoices := generateInvoiceExpiryTestData( 1002 t, testTime, numExpired+numPending, 0, numPending, 1003 ) 1004 1005 var invoicesThatWillCancel []lntypes.Hash 1006 for paymentHash, pendingInvoice := range newInvoices.pendingInvoices { 1007 _, err := registry.AddInvoice(pendingInvoice, paymentHash) 1008 invoicesThatWillCancel = append(invoicesThatWillCancel, paymentHash) 1009 if err != nil { 1010 t.Fatal(err) 1011 } 1012 } 1013 1014 // Check that they are really not canceled until before the clock is 1015 // advanced. 1016 for i := range invoicesThatWillCancel { 1017 invoice, err := registry.LookupInvoice(invoicesThatWillCancel[i]) 1018 if err != nil { 1019 t.Fatalf("cannot find invoice: %v", err) 1020 } 1021 1022 if invoice.State == channeldb.ContractCanceled { 1023 t.Fatalf("expected pending invoice, got canceled") 1024 } 1025 } 1026 1027 // Fwd time 1 day. 1028 testClock.SetTime(testTime.Add(24 * time.Hour)) 1029 1030 // Give some time to the watcher to cancel everything. 1031 time.Sleep(500 * time.Millisecond) 1032 if err = registry.Stop(); err != nil { 1033 t.Fatalf("failed to stop invoice registry: %v", err) 1034 } 1035 1036 // Create the expected cancellation set before the final check. 1037 expectedCancellations = append( 1038 expectedCancellations, invoicesThatWillCancel..., 1039 ) 1040 1041 // Retrospectively check that all invoices that were expected to be canceled 1042 // are indeed canceled. 1043 for i := range expectedCancellations { 1044 invoice, err := registry.LookupInvoice(expectedCancellations[i]) 1045 if err != nil { 1046 t.Fatalf("cannot find invoice: %v", err) 1047 } 1048 1049 if invoice.State != channeldb.ContractCanceled { 1050 t.Fatalf("expected canceled invoice, got: %v", invoice.State) 1051 } 1052 } 1053 } 1054 1055 // TestOldInvoiceRemovalOnStart tests that we'll attempt to remove old canceled 1056 // invoices upon start while keeping all settled ones. 1057 func TestOldInvoiceRemovalOnStart(t *testing.T) { 1058 t.Parallel() 1059 1060 testClock := clock.NewTestClock(testTime) 1061 cdb, cleanup, err := newTestChannelDB(testClock) 1062 defer cleanup() 1063 1064 require.NoError(t, err) 1065 1066 cfg := RegistryConfig{ 1067 FinalCltvRejectDelta: testFinalCltvRejectDelta, 1068 Clock: testClock, 1069 GcCanceledInvoicesOnStartup: true, 1070 } 1071 1072 expiryWatcher := NewInvoiceExpiryWatcher( 1073 cfg.Clock, 0, uint32(testCurrentHeight), nil, newMockNotifier(), 1074 ) 1075 registry := NewRegistry(cdb, expiryWatcher, &cfg) 1076 1077 // First prefill the Channel DB with some pre-existing expired invoices. 1078 const numExpired = 5 1079 const numPending = 0 1080 existingInvoices := generateInvoiceExpiryTestData( 1081 t, testTime, 0, numExpired, numPending, 1082 ) 1083 1084 i := 0 1085 for paymentHash, invoice := range existingInvoices.expiredInvoices { 1086 // Mark half of the invoices as settled, the other hald as 1087 // canceled. 1088 if i%2 == 0 { 1089 invoice.State = channeldb.ContractSettled 1090 } else { 1091 invoice.State = channeldb.ContractCanceled 1092 } 1093 1094 _, err := cdb.AddInvoice(invoice, paymentHash) 1095 require.NoError(t, err) 1096 i++ 1097 } 1098 1099 // Collect all settled invoices for our expectation set. 1100 var expected []channeldb.Invoice 1101 1102 // Perform a scan query to collect all invoices. 1103 query := channeldb.InvoiceQuery{ 1104 IndexOffset: 0, 1105 NumMaxInvoices: math.MaxUint64, 1106 } 1107 1108 response, err := cdb.QueryInvoices(query) 1109 require.NoError(t, err) 1110 1111 // Save all settled invoices for our expectation set. 1112 for _, invoice := range response.Invoices { 1113 if invoice.State == channeldb.ContractSettled { 1114 expected = append(expected, invoice) 1115 } 1116 } 1117 1118 // Start the registry which should collect and delete all canceled 1119 // invoices upon start. 1120 err = registry.Start() 1121 require.NoError(t, err, "cannot start the registry") 1122 1123 // Perform a scan query to collect all invoices. 1124 response, err = cdb.QueryInvoices(query) 1125 require.NoError(t, err) 1126 1127 // Check that we really only kept the settled invoices after the 1128 // registry start. 1129 require.Equal(t, expected, response.Invoices) 1130 } 1131 1132 // TestHeightExpiryWithRegistry tests our height-based invoice expiry for 1133 // invoices paid with single and multiple htlcs, testing the case where the 1134 // invoice is settled before expiry (and thus not canceled), and the case 1135 // where the invoice is expired. 1136 func TestHeightExpiryWithRegistry(t *testing.T) { 1137 t.Run("single shot settled before expiry", func(t *testing.T) { 1138 testHeightExpiryWithRegistry(t, 1, true) 1139 }) 1140 1141 t.Run("single shot expires", func(t *testing.T) { 1142 testHeightExpiryWithRegistry(t, 1, false) 1143 }) 1144 1145 t.Run("mpp settled before expiry", func(t *testing.T) { 1146 testHeightExpiryWithRegistry(t, 2, true) 1147 }) 1148 1149 t.Run("mpp expires", func(t *testing.T) { 1150 testHeightExpiryWithRegistry(t, 2, false) 1151 }) 1152 } 1153 1154 func testHeightExpiryWithRegistry(t *testing.T, numParts int, settle bool) { 1155 t.Parallel() 1156 defer timeout()() 1157 1158 ctx := newTestContext(t) 1159 defer ctx.cleanup() 1160 1161 require.Greater(t, numParts, 0, "test requires at least one part") 1162 1163 // Add a hold invoice, we set a non-nil payment request so that this 1164 // invoice is not considered a keysend by the expiry watcher. 1165 invoice := *testInvoice 1166 invoice.HodlInvoice = true 1167 invoice.PaymentRequest = []byte{1, 2, 3} 1168 1169 _, err := ctx.registry.AddInvoice(&invoice, testInvoicePaymentHash) 1170 require.NoError(t, err) 1171 1172 payLoad := testPayload 1173 if numParts > 1 { 1174 payLoad = &mockPayload{ 1175 mpp: record.NewMPP(testInvoiceAmt, [32]byte{}), 1176 } 1177 } 1178 1179 htlcAmt := invoice.Terms.Value / lnwire.MilliAtom(numParts) 1180 hodlChan := make(chan interface{}, numParts) 1181 for i := 0; i < numParts; i++ { 1182 // We bump our expiry height for each htlc so that we can test 1183 // that the lowest expiry height is used. 1184 expiry := testHtlcExpiry + uint32(i) 1185 1186 resolution, err := ctx.registry.NotifyExitHopHtlc( 1187 testInvoicePaymentHash, htlcAmt, expiry, 1188 testCurrentHeight, getCircuitKey(uint64(i)), hodlChan, 1189 payLoad, 1190 ) 1191 require.NoError(t, err) 1192 require.Nil(t, resolution, "did not expect direct resolution") 1193 } 1194 1195 require.Eventually(t, func() bool { 1196 inv, err := ctx.registry.LookupInvoice(testInvoicePaymentHash) 1197 require.NoError(t, err) 1198 1199 return inv.State == channeldb.ContractAccepted 1200 }, time.Second, time.Millisecond*100) 1201 1202 // Now that we've added our htlc(s), we tick our test clock to our 1203 // invoice expiry time. We don't expect the invoice to be canceled 1204 // based on its expiry time now that we have active htlcs. 1205 ctx.clock.SetTime(invoice.CreationDate.Add(invoice.Terms.Expiry + 1)) 1206 1207 // The expiry watcher loop takes some time to process the new clock 1208 // time. We mine the block before our expiry height, our mock will block 1209 // until the expiry watcher consumes this height, so we can be sure 1210 // that the expiry loop has run at least once after this block is 1211 // consumed. 1212 ctx.notifier.blockChan <- &chainntnfs.BlockEpoch{ 1213 Height: int32(testHtlcExpiry - 1), 1214 } 1215 1216 // If we want to settle our invoice in this test, we do so now. 1217 if settle { 1218 err = ctx.registry.SettleHodlInvoice(testInvoicePreimage) 1219 require.NoError(t, err) 1220 1221 for i := 0; i < numParts; i++ { 1222 htlcResolution := (<-hodlChan).(HtlcResolution) 1223 require.NotNil(t, htlcResolution) 1224 settleResolution := checkSettleResolution( 1225 t, htlcResolution, testInvoicePreimage, 1226 ) 1227 require.Equal(t, ResultSettled, settleResolution.Outcome) 1228 } 1229 } 1230 1231 // Now we mine our htlc's expiry height. 1232 ctx.notifier.blockChan <- &chainntnfs.BlockEpoch{ 1233 Height: int32(testHtlcExpiry), 1234 } 1235 1236 // If we did not settle the invoice before its expiry, we now expect 1237 // a cancelation. 1238 expectedState := channeldb.ContractSettled 1239 if !settle { 1240 expectedState = channeldb.ContractCanceled 1241 1242 htlcResolution := (<-hodlChan).(HtlcResolution) 1243 require.NotNil(t, htlcResolution) 1244 checkFailResolution( 1245 t, htlcResolution, ResultCanceled, 1246 ) 1247 } 1248 1249 // Finally, lookup the invoice and assert that we have the state we 1250 // expect. 1251 inv, err := ctx.registry.LookupInvoice(testInvoicePaymentHash) 1252 require.NoError(t, err) 1253 require.Equal(t, expectedState, inv.State, "expected "+ 1254 "hold invoice: %v, got: %v", expectedState, inv.State) 1255 } 1256 1257 // TestMultipleSetHeightExpiry pays a hold invoice with two mpp sets, testing 1258 // that the invoice expiry watcher only uses the expiry height of the second, 1259 // successful set to cancel the invoice, and does not cancel early using the 1260 // expiry height of the first set that was canceled back due to mpp timeout. 1261 func TestMultipleSetHeightExpiry(t *testing.T) { 1262 t.Parallel() 1263 defer timeout()() 1264 1265 ctx := newTestContext(t) 1266 defer ctx.cleanup() 1267 1268 // Add a hold invoice. 1269 invoice := *testInvoice 1270 invoice.HodlInvoice = true 1271 1272 _, err := ctx.registry.AddInvoice(&invoice, testInvoicePaymentHash) 1273 require.NoError(t, err) 1274 1275 mppPayload := &mockPayload{ 1276 mpp: record.NewMPP(testInvoiceAmt, [32]byte{}), 1277 } 1278 1279 // Send htlc 1. 1280 hodlChan1 := make(chan interface{}, 1) 1281 resolution, err := ctx.registry.NotifyExitHopHtlc( 1282 testInvoicePaymentHash, invoice.Terms.Value/2, 1283 testHtlcExpiry, 1284 testCurrentHeight, getCircuitKey(10), hodlChan1, mppPayload, 1285 ) 1286 require.NoError(t, err) 1287 require.Nil(t, resolution, "did not expect direct resolution") 1288 1289 // Simulate mpp timeout releasing htlc 1. 1290 ctx.clock.SetTime(testTime.Add(30 * time.Second)) 1291 1292 htlcResolution := (<-hodlChan1).(HtlcResolution) 1293 failResolution, ok := htlcResolution.(*HtlcFailResolution) 1294 require.True(t, ok, "expected fail resolution, got: %T", resolution) 1295 require.Equal(t, ResultMppTimeout, failResolution.Outcome, 1296 "expected MPP Timeout, got: %v", failResolution.Outcome) 1297 1298 // Notify the expiry height for our first htlc. We don't expect the 1299 // invoice to be expired based on block height because the htlc set 1300 // was never completed. 1301 ctx.notifier.blockChan <- &chainntnfs.BlockEpoch{ 1302 Height: int32(testHtlcExpiry), 1303 } 1304 1305 // Now we will send a full set of htlcs for the invoice with a higher 1306 // expiry height. We expect the invoice to move into the accepted state. 1307 expiry := testHtlcExpiry + 5 1308 1309 // Send htlc 2. 1310 hodlChan2 := make(chan interface{}, 1) 1311 resolution, err = ctx.registry.NotifyExitHopHtlc( 1312 testInvoicePaymentHash, invoice.Terms.Value/2, expiry, 1313 testCurrentHeight, getCircuitKey(11), hodlChan2, mppPayload, 1314 ) 1315 require.NoError(t, err) 1316 require.Nil(t, resolution, "did not expect direct resolution") 1317 1318 // Send htlc 3. 1319 hodlChan3 := make(chan interface{}, 1) 1320 resolution, err = ctx.registry.NotifyExitHopHtlc( 1321 testInvoicePaymentHash, invoice.Terms.Value/2, expiry, 1322 testCurrentHeight, getCircuitKey(12), hodlChan3, mppPayload, 1323 ) 1324 require.NoError(t, err) 1325 require.Nil(t, resolution, "did not expect direct resolution") 1326 1327 // Assert that we've reached an accepted state because the invoice has 1328 // been paid with a complete set. 1329 inv, err := ctx.registry.LookupInvoice(testInvoicePaymentHash) 1330 require.NoError(t, err) 1331 require.Equal(t, channeldb.ContractAccepted, inv.State, "expected "+ 1332 "hold invoice accepted") 1333 1334 // Now we will notify the expiry height for the new set of htlcs. We 1335 // expect the invoice to be canceled by the expiry watcher. 1336 ctx.notifier.blockChan <- &chainntnfs.BlockEpoch{ 1337 Height: int32(expiry), 1338 } 1339 1340 require.Eventuallyf(t, func() bool { 1341 inv, err := ctx.registry.LookupInvoice(testInvoicePaymentHash) 1342 require.NoError(t, err) 1343 1344 return inv.State == channeldb.ContractCanceled 1345 }, testTimeout, time.Millisecond*100, "invoice not canceled") 1346 } 1347 1348 // TestSettleInvoicePaymentAddrRequired tests that if an incoming payment has 1349 // an invoice that requires the payment addr bit to be set, and the incoming 1350 // payment doesn't include an mpp payload, then the payment is rejected. 1351 func TestSettleInvoicePaymentAddrRequired(t *testing.T) { 1352 t.Parallel() 1353 1354 ctx := newTestContext(t) 1355 defer ctx.cleanup() 1356 1357 allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) 1358 require.Nil(t, err) 1359 defer allSubscriptions.Cancel() 1360 1361 // Subscribe to the not yet existing invoice. 1362 subscription, err := ctx.registry.SubscribeSingleInvoice( 1363 testInvoicePaymentHash, 1364 ) 1365 require.NoError(t, err) 1366 defer subscription.Cancel() 1367 1368 require.Equal( 1369 t, subscription.invoiceRef.PayHash(), &testInvoicePaymentHash, 1370 ) 1371 1372 // Add the invoice, which requires the MPP payload to always be 1373 // included due to its set of feature bits. 1374 addIdx, err := ctx.registry.AddInvoice( 1375 testPayAddrReqInvoice, testInvoicePaymentHash, 1376 ) 1377 require.NoError(t, err) 1378 require.Equal(t, int(addIdx), 1) 1379 1380 // We expect the open state to be sent to the single invoice subscriber. 1381 select { 1382 case update := <-subscription.Updates: 1383 if update.State != channeldb.ContractOpen { 1384 t.Fatalf("expected state ContractOpen, but got %v", 1385 update.State) 1386 } 1387 case <-time.After(testTimeout): 1388 t.Fatal("no update received") 1389 } 1390 1391 // We expect a new invoice notification to be sent out. 1392 select { 1393 case newInvoice := <-allSubscriptions.NewInvoices: 1394 if newInvoice.State != channeldb.ContractOpen { 1395 t.Fatalf("expected state ContractOpen, but got %v", 1396 newInvoice.State) 1397 } 1398 case <-time.After(testTimeout): 1399 t.Fatal("no update received") 1400 } 1401 1402 hodlChan := make(chan interface{}, 1) 1403 1404 // Now try to settle the invoice, the testPayload doesn't have any mpp 1405 // information, so it should be forced to the updateLegacy path then 1406 // fail as a required feature bit exists. 1407 resolution, err := ctx.registry.NotifyExitHopHtlc( 1408 testInvoicePaymentHash, testInvoice.Terms.Value, 1409 uint32(testCurrentHeight)+testInvoiceCltvDelta-1, 1410 testCurrentHeight, getCircuitKey(10), hodlChan, testPayload, 1411 ) 1412 require.NoError(t, err) 1413 1414 failResolution, ok := resolution.(*HtlcFailResolution) 1415 if !ok { 1416 t.Fatalf("expected fail resolution, got: %T", 1417 resolution) 1418 } 1419 require.Equal(t, failResolution.AcceptHeight, testCurrentHeight) 1420 require.Equal(t, failResolution.Outcome, ResultAddressMismatch) 1421 } 1422 1423 // TestSettleInvoicePaymentAddrRequiredOptionalGrace tests that if an invoice 1424 // in the database has an optional payment addr required bit set, then we'll 1425 // still allow it to be paid by an incoming HTLC that doesn't include the MPP 1426 // payload. This ensures we don't break payment for any invoices in the wild. 1427 func TestSettleInvoicePaymentAddrRequiredOptionalGrace(t *testing.T) { 1428 t.Parallel() 1429 1430 ctx := newTestContext(t) 1431 defer ctx.cleanup() 1432 1433 allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) 1434 require.Nil(t, err) 1435 defer allSubscriptions.Cancel() 1436 1437 // Subscribe to the not yet existing invoice. 1438 subscription, err := ctx.registry.SubscribeSingleInvoice( 1439 testInvoicePaymentHash, 1440 ) 1441 require.NoError(t, err) 1442 defer subscription.Cancel() 1443 1444 require.Equal( 1445 t, subscription.invoiceRef.PayHash(), &testInvoicePaymentHash, 1446 ) 1447 1448 // Add the invoice, which requires the MPP payload to always be 1449 // included due to its set of feature bits. 1450 addIdx, err := ctx.registry.AddInvoice( 1451 testPayAddrOptionalInvoice, testInvoicePaymentHash, 1452 ) 1453 require.NoError(t, err) 1454 require.Equal(t, int(addIdx), 1) 1455 1456 // We expect the open state to be sent to the single invoice 1457 // subscriber. 1458 select { 1459 case update := <-subscription.Updates: 1460 if update.State != channeldb.ContractOpen { 1461 t.Fatalf("expected state ContractOpen, but got %v", 1462 update.State) 1463 } 1464 case <-time.After(testTimeout): 1465 t.Fatal("no update received") 1466 } 1467 1468 // We expect a new invoice notification to be sent out. 1469 select { 1470 case newInvoice := <-allSubscriptions.NewInvoices: 1471 if newInvoice.State != channeldb.ContractOpen { 1472 t.Fatalf("expected state ContractOpen, but got %v", 1473 newInvoice.State) 1474 } 1475 case <-time.After(testTimeout): 1476 t.Fatal("no update received") 1477 } 1478 1479 // We'll now attempt to settle the invoice as normal, this should work 1480 // no problem as we should allow these existing invoices to be settled. 1481 hodlChan := make(chan interface{}, 1) 1482 resolution, err := ctx.registry.NotifyExitHopHtlc( 1483 testInvoicePaymentHash, testInvoiceAmt, 1484 testHtlcExpiry, testCurrentHeight, 1485 getCircuitKey(10), hodlChan, testPayload, 1486 ) 1487 require.NoError(t, err) 1488 1489 settleResolution, ok := resolution.(*HtlcSettleResolution) 1490 if !ok { 1491 t.Fatalf("expected settle resolution, got: %T", 1492 resolution) 1493 } 1494 require.Equal(t, settleResolution.Outcome, ResultSettled) 1495 1496 // We expect the settled state to be sent to the single invoice 1497 // subscriber. 1498 select { 1499 case update := <-subscription.Updates: 1500 if update.State != channeldb.ContractSettled { 1501 t.Fatalf("expected state ContractOpen, but got %v", 1502 update.State) 1503 } 1504 if update.AmtPaid != testInvoice.Terms.Value { 1505 t.Fatal("invoice AmtPaid incorrect") 1506 } 1507 case <-time.After(testTimeout): 1508 t.Fatal("no update received") 1509 } 1510 1511 // We expect a settled notification to be sent out. 1512 select { 1513 case settledInvoice := <-allSubscriptions.SettledInvoices: 1514 if settledInvoice.State != channeldb.ContractSettled { 1515 t.Fatalf("expected state ContractOpen, but got %v", 1516 settledInvoice.State) 1517 } 1518 case <-time.After(testTimeout): 1519 t.Fatal("no update received") 1520 } 1521 } 1522 1523 // TestAMPWithoutMPPPayload asserts that we correctly reject an AMP HTLC that 1524 // does not include an MPP record. 1525 func TestAMPWithoutMPPPayload(t *testing.T) { 1526 defer timeout()() 1527 1528 ctx := newTestContext(t) 1529 defer ctx.cleanup() 1530 1531 ctx.registry.cfg.AcceptAMP = true 1532 1533 const ( 1534 shardAmt = lnwire.MilliAtom(10) 1535 expiry = uint32(testCurrentHeight + 20) 1536 ) 1537 1538 // Create payload with missing MPP field. 1539 payload := &mockPayload{ 1540 amp: record.NewAMP([32]byte{}, [32]byte{}, 0), 1541 } 1542 1543 hodlChan := make(chan interface{}, 1) 1544 resolution, err := ctx.registry.NotifyExitHopHtlc( 1545 lntypes.Hash{}, shardAmt, expiry, 1546 testCurrentHeight, getCircuitKey(uint64(10)), hodlChan, 1547 payload, 1548 ) 1549 require.NoError(t, err) 1550 1551 // We should receive the ResultAmpError failure. 1552 require.NotNil(t, resolution) 1553 checkFailResolution(t, resolution, ResultAmpError) 1554 } 1555 1556 // TestSpontaneousAmpPayment tests receiving a spontaneous AMP payment with both 1557 // valid and invalid reconstructions. 1558 func TestSpontaneousAmpPayment(t *testing.T) { 1559 tests := []struct { 1560 name string 1561 ampEnabled bool 1562 failReconstruction bool 1563 numShards int 1564 }{ 1565 { 1566 name: "enabled valid one shard", 1567 ampEnabled: true, 1568 failReconstruction: false, 1569 numShards: 1, 1570 }, 1571 { 1572 name: "enabled valid multiple shards", 1573 ampEnabled: true, 1574 failReconstruction: false, 1575 numShards: 3, 1576 }, 1577 { 1578 name: "enabled invalid one shard", 1579 ampEnabled: true, 1580 failReconstruction: true, 1581 numShards: 1, 1582 }, 1583 { 1584 name: "enabled invalid multiple shards", 1585 ampEnabled: true, 1586 failReconstruction: true, 1587 numShards: 3, 1588 }, 1589 { 1590 name: "disabled valid multiple shards", 1591 ampEnabled: false, 1592 failReconstruction: false, 1593 numShards: 3, 1594 }, 1595 } 1596 1597 for _, test := range tests { 1598 test := test 1599 t.Run(test.name, func(t *testing.T) { 1600 testSpontaneousAmpPayment( 1601 t, test.ampEnabled, test.failReconstruction, 1602 test.numShards, 1603 ) 1604 }) 1605 } 1606 } 1607 1608 // testSpontaneousAmpPayment runs a specific spontaneous AMP test case. 1609 func testSpontaneousAmpPayment( 1610 t *testing.T, ampEnabled, failReconstruction bool, numShards int) { 1611 1612 defer timeout()() 1613 1614 ctx := newTestContext(t) 1615 defer ctx.cleanup() 1616 1617 ctx.registry.cfg.AcceptAMP = ampEnabled 1618 1619 allSubscriptions, err := ctx.registry.SubscribeNotifications(0, 0) 1620 require.Nil(t, err) 1621 defer allSubscriptions.Cancel() 1622 1623 const ( 1624 totalAmt = lnwire.MilliAtom(360) 1625 expiry = uint32(testCurrentHeight + 20) 1626 ) 1627 1628 var ( 1629 shardAmt = totalAmt / lnwire.MilliAtom(numShards) 1630 payAddr [32]byte 1631 setID [32]byte 1632 ) 1633 _, err = rand.Read(payAddr[:]) 1634 require.NoError(t, err) 1635 _, err = rand.Read(setID[:]) 1636 require.NoError(t, err) 1637 1638 var sharer amp.Sharer 1639 sharer, err = amp.NewSeedSharer() 1640 require.NoError(t, err) 1641 1642 // Asserts that a new invoice is published on the NewInvoices channel. 1643 checkOpenSubscription := func() { 1644 t.Helper() 1645 newInvoice := <-allSubscriptions.NewInvoices 1646 require.Equal(t, newInvoice.State, channeldb.ContractOpen) 1647 } 1648 1649 // Asserts that a settled invoice is published on the SettledInvoices 1650 // channel. 1651 checkSettleSubscription := func() { 1652 t.Helper() 1653 settledInvoice := <-allSubscriptions.SettledInvoices 1654 1655 // Since this is an AMP invoice, the invoice state never 1656 // changes, but the AMP state should show that the setID has 1657 // been settled. 1658 htlcState := settledInvoice.AMPState[setID].State 1659 require.Equal(t, htlcState, channeldb.HtlcStateSettled) 1660 } 1661 1662 // Asserts that no invoice is published on the SettledInvoices channel 1663 // w/in two seconds. 1664 checkNoSettleSubscription := func() { 1665 t.Helper() 1666 select { 1667 case <-allSubscriptions.SettledInvoices: 1668 t.Fatal("no settle ntfn expected") 1669 case <-time.After(2 * time.Second): 1670 } 1671 } 1672 1673 // Record the hodl channels of all HTLCs but the last one, which 1674 // received its resolution directly from NotifyExistHopHtlc. 1675 hodlChans := make(map[lntypes.Preimage]chan interface{}) 1676 for i := 0; i < numShards; i++ { 1677 isFinalShard := i == numShards-1 1678 1679 hodlChan := make(chan interface{}, 1) 1680 1681 var child *amp.Child 1682 if !isFinalShard { 1683 var left amp.Sharer 1684 left, sharer, err = sharer.Split() 1685 require.NoError(t, err) 1686 1687 child = left.Child(uint32(i)) 1688 1689 // Only store the first numShards-1 hodlChans. 1690 hodlChans[child.Preimage] = hodlChan 1691 } else { 1692 child = sharer.Child(uint32(i)) 1693 } 1694 1695 // Send a blank share when the set should fail reconstruction, 1696 // otherwise send the derived share. 1697 var share [32]byte 1698 if !failReconstruction { 1699 share = child.Share 1700 } 1701 1702 payload := &mockPayload{ 1703 mpp: record.NewMPP(totalAmt, payAddr), 1704 amp: record.NewAMP(share, setID, uint32(i)), 1705 } 1706 1707 resolution, err := ctx.registry.NotifyExitHopHtlc( 1708 child.Hash, shardAmt, expiry, 1709 testCurrentHeight, getCircuitKey(uint64(i)), hodlChan, 1710 payload, 1711 ) 1712 require.NoError(t, err) 1713 1714 // When keysend is disabled all HTLC should fail with invoice 1715 // not found, since one is not inserted before executing 1716 // UpdateInvoice. 1717 if !ampEnabled { 1718 require.NotNil(t, resolution) 1719 checkFailResolution(t, resolution, ResultInvoiceNotFound) 1720 continue 1721 } 1722 1723 // Check that resolutions are properly formed. 1724 if !isFinalShard { 1725 // Non-final shares should always return a nil 1726 // resolution, theirs will be delivered via the 1727 // hodlChan. 1728 require.Nil(t, resolution) 1729 } else { 1730 // The final share should receive a non-nil resolution. 1731 // Also assert that it is the proper type based on the 1732 // test case. 1733 require.NotNil(t, resolution) 1734 if failReconstruction { 1735 checkFailResolution(t, resolution, ResultAmpReconstruction) 1736 } else { 1737 checkSettleResolution(t, resolution, child.Preimage) 1738 } 1739 } 1740 1741 // Assert the behavior of the Open and Settle notifications. 1742 // There should always be an open (keysend is enabled) followed 1743 // by settle for valid AMP payments. 1744 // 1745 // NOTE: The cases are split in separate if conditions, rather 1746 // than else-if, to properly handle the case when there is only 1747 // one shard. 1748 if i == 0 { 1749 checkOpenSubscription() 1750 } 1751 if isFinalShard { 1752 if failReconstruction { 1753 checkNoSettleSubscription() 1754 } else { 1755 checkSettleSubscription() 1756 } 1757 } 1758 } 1759 1760 // No need to check the hodl chans when keysend is not enabled. 1761 if !ampEnabled { 1762 return 1763 } 1764 1765 // For the non-final hodl chans, assert that they receive the expected 1766 // failure or preimage. 1767 for preimage, hodlChan := range hodlChans { 1768 resolution, ok := (<-hodlChan).(HtlcResolution) 1769 require.True(t, ok) 1770 require.NotNil(t, resolution) 1771 if failReconstruction { 1772 checkFailResolution(t, resolution, ResultAmpReconstruction) 1773 } else { 1774 checkSettleResolution(t, resolution, preimage) 1775 } 1776 } 1777 }