diff --git a/channeldb/payment_control.go b/channeldb/payment_control.go index 6bd09477..440742ca 100644 --- a/channeldb/payment_control.go +++ b/channeldb/payment_control.go @@ -246,9 +246,12 @@ func (p *PaymentControl) Success(paymentHash lntypes.Hash, // its next call for this payment hash, allowing the switch to make a // subsequent payment. func (p *PaymentControl) Fail(paymentHash lntypes.Hash, - reason FailureReason) error { + reason FailureReason) (*route.Route, error) { - var updateErr error + var ( + updateErr error + route *route.Route + ) err := p.db.Batch(func(tx *bbolt.Tx) error { // Reset the update error, to avoid carrying over an error // from a previous execution of the batched db transaction. @@ -270,13 +273,27 @@ func (p *PaymentControl) Fail(paymentHash lntypes.Hash, // Put the failure reason in the bucket for record keeping. v := []byte{byte(reason)} - return bucket.Put(paymentFailInfoKey, v) + err = bucket.Put(paymentFailInfoKey, v) + if err != nil { + return err + } + + // Retrieve attempt info for the notification, if available. + attempt, err := fetchPaymentAttempt(bucket) + if err != nil && err != errNoAttemptInfo { + return err + } + if err != errNoAttemptInfo { + route = &attempt.Route + } + + return nil }) if err != nil { - return err + return nil, err } - return updateErr + return route, updateErr } // FetchPayment returns information about a payment from the database. diff --git a/channeldb/payment_control_test.go b/channeldb/payment_control_test.go index ea69645f..cc4ae492 100644 --- a/channeldb/payment_control_test.go +++ b/channeldb/payment_control_test.go @@ -94,7 +94,7 @@ func TestPaymentControlSwitchFail(t *testing.T) { // Fail the payment, which should moved it to Failed. failReason := FailureReasonNoRoute - err = pControl.Fail(info.PaymentHash, failReason) + _, err = pControl.Fail(info.PaymentHash, failReason) if err != nil { t.Fatalf("unable to fail payment hash: %v", err) } @@ -270,7 +270,7 @@ func TestPaymentControlFailsWithoutInFlight(t *testing.T) { } // Calling Fail should return an error. - err = pControl.Fail(info.PaymentHash, FailureReasonNoRoute) + _, err = pControl.Fail(info.PaymentHash, FailureReasonNoRoute) if err != ErrPaymentNotInitiated { t.Fatalf("expected ErrPaymentNotInitiated, got %v", err) } @@ -330,7 +330,7 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) { if p.failed { // Fail the payment, which should moved it to Failed. failReason := FailureReasonNoRoute - err = pControl.Fail(info.PaymentHash, failReason) + _, err = pControl.Fail(info.PaymentHash, failReason) if err != nil { t.Fatalf("unable to fail payment hash: %v", err) } diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 181edecf..927bde61 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -499,8 +499,10 @@ func (s *Server) TrackPayment(request *TrackPaymentRequest, func (s *Server) trackPayment(paymentHash lntypes.Hash, stream Router_TrackPaymentServer) error { + router := s.cfg.RouterBackend + // Subscribe to the outcome of this payment. - inFlight, resultChan, err := s.cfg.RouterBackend.Tower.SubscribePayment( + inFlight, resultChan, err := router.Tower.SubscribePayment( paymentHash, ) switch { @@ -535,7 +537,7 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash, status.State = PaymentState_SUCCEEDED status.Preimage = result.Preimage[:] - status.Route = s.cfg.RouterBackend.MarshallRoute( + status.Route = router.MarshallRoute( result.Route, ) } else { @@ -546,6 +548,11 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash, return err } status.State = state + if result.Route != nil { + status.Route = router.MarshallRoute( + result.Route, + ) + } } // Send event to the client. diff --git a/routing/control_tower.go b/routing/control_tower.go index 990c9f94..0bf08476 100644 --- a/routing/control_tower.go +++ b/routing/control_tower.go @@ -132,11 +132,21 @@ func createSuccessResult(rt *route.Route, } // createFailResult creates a failed result to send to subscribers. -func createFailedResult(reason channeldb.FailureReason) *PaymentResult { - return &PaymentResult{ +func createFailedResult(rt *route.Route, + reason channeldb.FailureReason) *PaymentResult { + + result := &PaymentResult{ Success: false, FailureReason: reason, } + + // In case of incorrect payment details, set the route. This can be used + // for probing and to extract a fee estimate from the route. + if reason == channeldb.FailureReasonIncorrectPaymentDetails { + result.Route = rt + } + + return result } // Fail transitions a payment into the Failed state, and records the reason the @@ -146,14 +156,14 @@ func createFailedResult(reason channeldb.FailureReason) *PaymentResult { func (p *controlTower) Fail(paymentHash lntypes.Hash, reason channeldb.FailureReason) error { - err := p.db.Fail(paymentHash, reason) + route, err := p.db.Fail(paymentHash, reason) if err != nil { return err } // Notify subscribers of fail event. p.notifyFinalEvent( - paymentHash, createFailedResult(reason), + paymentHash, createFailedResult(route, reason), ) return nil @@ -211,7 +221,13 @@ func (p *controlTower) SubscribePayment(paymentHash lntypes.Hash) ( // subscriber, because we can send the result on the channel // immediately. case channeldb.StatusFailed: - event = *createFailedResult(*payment.Failure) + var route *route.Route + if payment.Attempt != nil { + route = &payment.Attempt.Route + } + event = *createFailedResult( + route, *payment.Failure, + ) default: return false, nil, errors.New("unknown payment status")