@@ -62,8 +62,8 @@ type loopOutSwap struct {
6262 // htlcTxHash is the confirmed htlc tx id.
6363 htlcTxHash * chainhash.Hash
6464
65- swapPaymentChan chan lndclient. PaymentResult
66- prePaymentChan chan lndclient. PaymentResult
65+ swapPaymentChan chan paymentResult
66+ prePaymentChan chan paymentResult
6767
6868 wg sync.WaitGroup
6969}
@@ -340,27 +340,35 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
340340 select {
341341 case result := <- s .swapPaymentChan :
342342 s .swapPaymentChan = nil
343- if result .Err != nil {
343+
344+ err := s .handlePaymentResult (result )
345+ if err != nil {
346+ return err
347+ }
348+
349+ if result .failure () != nil {
344350 // Server didn't pull the swap payment.
345351 s .log .Infof ("Swap payment failed: %v" ,
346- result .Err )
352+ result .failure () )
347353
348354 continue
349355 }
350- s .cost .Server += result .PaidAmt
351- s .cost .Offchain += result .PaidFee
352356
353357 case result := <- s .prePaymentChan :
354358 s .prePaymentChan = nil
355- if result .Err != nil {
359+
360+ err := s .handlePaymentResult (result )
361+ if err != nil {
362+ return err
363+ }
364+
365+ if result .failure () != nil {
356366 // Server didn't pull the prepayment.
357367 s .log .Infof ("Prepayment failed: %v" ,
358- result .Err )
368+ result .failure () )
359369
360370 continue
361371 }
362- s .cost .Server += result .PaidAmt
363- s .cost .Offchain += result .PaidFee
364372
365373 case <- globalCtx .Done ():
366374 return globalCtx .Err ()
@@ -379,6 +387,27 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
379387 return s .persistState (globalCtx )
380388}
381389
390+ func (s * loopOutSwap ) handlePaymentResult (result paymentResult ) error {
391+ switch {
392+ // If our result has a non-nil error, our status will be nil. In this
393+ // case the payment failed so we do not need to take any action.
394+ case result .err != nil :
395+ return nil
396+
397+ case result .status .State == lnrpc .Payment_SUCCEEDED :
398+ s .cost .Server += result .status .Value .ToSatoshis ()
399+ s .cost .Offchain += result .status .Fee .ToSatoshis ()
400+
401+ return nil
402+
403+ case result .status .State == lnrpc .Payment_FAILED :
404+ return nil
405+
406+ default :
407+ return fmt .Errorf ("unexpected state: %v" , result .status .State )
408+ }
409+ }
410+
382411// executeSwap executes the swap, but returns as soon as the swap outcome is
383412// final. At that point, there may still be pending off-chain payment(s).
384413func (s * loopOutSwap ) executeSwap (globalCtx context.Context ) error {
@@ -510,31 +539,64 @@ func (s *loopOutSwap) payInvoices(ctx context.Context) {
510539 )
511540}
512541
542+ // paymentResult contains the response for a failed or settled payment, and
543+ // any errors that occurred if the payment unexpectedly failed.
544+ type paymentResult struct {
545+ status lndclient.PaymentStatus
546+ err error
547+ }
548+
549+ // failure returns the error we encountered trying to dispatch a payment result,
550+ // if any.
551+ func (p paymentResult ) failure () error {
552+ if p .err != nil {
553+ return p .err
554+ }
555+
556+ if p .status .State == lnrpc .Payment_SUCCEEDED {
557+ return nil
558+ }
559+
560+ return fmt .Errorf ("payment failed: %v" , p .status .FailureReason )
561+ }
562+
513563// payInvoice pays a single invoice.
514564func (s * loopOutSwap ) payInvoice (ctx context.Context , invoice string ,
515565 maxFee btcutil.Amount ,
516- outgoingChanIds loopdb.ChannelSet ) chan lndclient. PaymentResult {
566+ outgoingChanIds loopdb.ChannelSet ) chan paymentResult {
517567
518- resultChan := make (chan lndclient.PaymentResult )
568+ resultChan := make (chan paymentResult )
569+ sendResult := func (result paymentResult ) {
570+ select {
571+ case resultChan <- result :
572+ case <- ctx .Done ():
573+ }
574+ }
519575
520576 go func () {
521- var result lndclient. PaymentResult
577+ var result paymentResult
522578
523579 status , err := s .payInvoiceAsync (
524580 ctx , invoice , maxFee , outgoingChanIds ,
525581 )
526582 if err != nil {
527- result .Err = err
528- } else {
529- result .Preimage = status .Preimage
530- result .PaidFee = status .Fee .ToSatoshis ()
531- result .PaidAmt = status .Value .ToSatoshis ()
583+ result .err = err
584+ sendResult (result )
585+ return
532586 }
533587
534- select {
535- case resultChan <- result :
536- case <- ctx .Done ():
588+ // If our payment failed or succeeded, our status should be
589+ // non-nil.
590+ switch status .State {
591+ case lnrpc .Payment_FAILED , lnrpc .Payment_SUCCEEDED :
592+ result .status = * status
593+
594+ default :
595+ result .err = fmt .Errorf ("unexpected payment state: %v" ,
596+ status .State )
537597 }
598+
599+ sendResult (result )
538600 }()
539601
540602 return resultChan
@@ -583,7 +645,7 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
583645 return & payState , nil
584646
585647 case lnrpc .Payment_FAILED :
586- return nil , errors . New ( "payment failed" )
648+ return & payState , nil
587649
588650 case lnrpc .Payment_IN_FLIGHT :
589651 // Continue waiting for final state.
@@ -686,30 +748,38 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
686748 // have lost the prepayment.
687749 case result := <- s .swapPaymentChan :
688750 s .swapPaymentChan = nil
689- if result .Err != nil {
690- s .state = loopdb .StateFailOffchainPayments
751+
752+ err := s .handlePaymentResult (result )
753+ if err != nil {
754+ return nil , err
755+ }
756+
757+ if result .failure () != nil {
691758 s .log .Infof ("Failed swap payment: %v" ,
692- result .Err )
759+ result .failure () )
693760
761+ s .state = loopdb .StateFailOffchainPayments
694762 return nil , nil
695763 }
696- s .cost .Server += result .PaidAmt
697- s .cost .Offchain += result .PaidFee
698764
699765 // If the prepay fails, abandon the swap. Because we
700766 // didn't reveal the preimage, the swap payment will be
701767 // canceled or time out.
702768 case result := <- s .prePaymentChan :
703769 s .prePaymentChan = nil
704- if result .Err != nil {
705- s .state = loopdb .StateFailOffchainPayments
770+
771+ err := s .handlePaymentResult (result )
772+ if err != nil {
773+ return nil , err
774+ }
775+
776+ if result .failure () != nil {
706777 s .log .Infof ("Failed prepayment: %v" ,
707- result .Err )
778+ result .failure () )
708779
780+ s .state = loopdb .StateFailOffchainPayments
709781 return nil , nil
710782 }
711- s .cost .Server += result .PaidAmt
712- s .cost .Offchain += result .PaidFee
713783
714784 // Unexpected error on the confirm channel happened,
715785 // abandon the swap.
0 commit comments