44 "context"
55 "crypto/rand"
66 "crypto/sha256"
7+ "errors"
78 "fmt"
89 "time"
910
@@ -14,6 +15,8 @@ import (
1415 "github.com/lightninglabs/loop/swap"
1516 "github.com/lightninglabs/loop/sweep"
1617 "github.com/lightningnetwork/lnd/chainntnfs"
18+ "github.com/lightningnetwork/lnd/channeldb"
19+ "github.com/lightningnetwork/lnd/lnrpc"
1720 "github.com/lightningnetwork/lnd/lntypes"
1821)
1922
3235 //
3336 // TODO(wilmer): tune?
3437 DefaultSweepConfTargetDelta = DefaultSweepConfTarget * 2
38+
39+ // paymentTimeout is the timeout for the loop out payment loop as
40+ // communicated to lnd.
41+ paymentTimeout = time .Minute
3542)
3643
3744// loopOutSwap contains all the in-memory state related to a pending loop out
@@ -384,19 +391,120 @@ func (s *loopOutSwap) persistState(ctx context.Context) error {
384391func (s * loopOutSwap ) payInvoices (ctx context.Context ) {
385392 // Pay the swap invoice.
386393 s .log .Infof ("Sending swap payment %v" , s .SwapInvoice )
387- s .swapPaymentChan = s .lnd . Client . PayInvoice (
394+ s .swapPaymentChan = s .payInvoice (
388395 ctx , s .SwapInvoice , s .MaxSwapRoutingFee ,
389396 s .LoopOutContract .UnchargeChannel ,
390397 )
391398
392399 // Pay the prepay invoice.
393400 s .log .Infof ("Sending prepayment %v" , s .PrepayInvoice )
394- s .prePaymentChan = s .lnd . Client . PayInvoice (
401+ s .prePaymentChan = s .payInvoice (
395402 ctx , s .PrepayInvoice , s .MaxPrepayRoutingFee ,
396403 nil ,
397404 )
398405}
399406
407+ // payInvoice pays a single invoice.
408+ func (s * loopOutSwap ) payInvoice (ctx context.Context , invoice string ,
409+ maxFee btcutil.Amount ,
410+ outgoingChannel * uint64 ) chan lndclient.PaymentResult {
411+
412+ resultChan := make (chan lndclient.PaymentResult )
413+
414+ go func () {
415+ var result lndclient.PaymentResult
416+
417+ status , err := s .payInvoiceAsync (
418+ ctx , invoice , maxFee , outgoingChannel ,
419+ )
420+ if err != nil {
421+ result .Err = err
422+ } else {
423+ result .Preimage = status .Preimage
424+ result .PaidFee = status .Fee .ToSatoshis ()
425+ result .PaidAmt = status .Value .ToSatoshis ()
426+ }
427+
428+ select {
429+ case resultChan <- result :
430+ case <- ctx .Done ():
431+ }
432+ }()
433+
434+ return resultChan
435+ }
436+
437+ // payInvoiceAsync is the asynchronously executed part of paying an invoice.
438+ func (s * loopOutSwap ) payInvoiceAsync (ctx context.Context ,
439+ invoice string , maxFee btcutil.Amount , outgoingChannel * uint64 ) (
440+ * lndclient.PaymentStatus , error ) {
441+
442+ // Extract hash from payment request. Unfortunately the request
443+ // components aren't available directly.
444+ chainParams := s .lnd .ChainParams
445+ hash , _ , err := swap .DecodeInvoice (chainParams , invoice )
446+ if err != nil {
447+ return nil , err
448+ }
449+
450+ req := lndclient.SendPaymentRequest {
451+ MaxFee : maxFee ,
452+ Invoice : invoice ,
453+ OutgoingChannel : outgoingChannel ,
454+ Timeout : paymentTimeout ,
455+ }
456+
457+ // Lookup state of the swap payment.
458+ paymentStateCtx , cancel := context .WithCancel (ctx )
459+ defer cancel ()
460+
461+ payStatusChan , payErrChan , err := s .lnd .Router .SendPayment (
462+ paymentStateCtx , req ,
463+ )
464+ if err != nil {
465+ return nil , err
466+ }
467+
468+ for {
469+ select {
470+ // Payment advanced to the next state.
471+ case payState := <- payStatusChan :
472+ s .log .Infof ("Payment %v: state=%v" ,
473+ hash , payState .State )
474+
475+ switch payState .State {
476+ case lnrpc .Payment_SUCCEEDED :
477+ return & payState , nil
478+
479+ case lnrpc .Payment_FAILED :
480+ return nil , errors .New ("payment failed" )
481+
482+ case lnrpc .Payment_IN_FLIGHT :
483+ // Continue waiting for final state.
484+
485+ default :
486+ return nil , errors .New ("unknown payment state" )
487+ }
488+
489+ // Abort the swap in case of an error. An unknown payment error
490+ // from TrackPayment is no longer expected here.
491+ case err := <- payErrChan :
492+ if err != channeldb .ErrAlreadyPaid {
493+ return nil , err
494+ }
495+
496+ payStatusChan , payErrChan , err =
497+ s .lnd .Router .TrackPayment (paymentStateCtx , hash )
498+ if err != nil {
499+ return nil , err
500+ }
501+
502+ case <- ctx .Done ():
503+ return nil , ctx .Err ()
504+ }
505+ }
506+ }
507+
400508// waitForConfirmedHtlc waits for a confirmed htlc to appear on the chain. In
401509// case we haven't revealed the preimage yet, it also monitors block height and
402510// off-chain payment failure.
0 commit comments