Skip to content

Commit 4040bb3

Browse files
committed
loopout: refactor payInvoice to return more payment information
We're going to want more information about our failures going forward, so we refactor payInvoice to return a full payment status. The primary change in this commit is that we surface both types of payment failures (result.err when we fail immediately, and lnrpc.Failure when our payment is failed back) and return them in the failure() method, rather than combining this information at a lower level.
1 parent 969e300 commit 4040bb3

File tree

1 file changed

+102
-32
lines changed

1 file changed

+102
-32
lines changed

loopout.go

Lines changed: 102 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -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).
384413
func (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.
514564
func (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

Comments
 (0)