Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions channeldb/payment_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions channeldb/payment_control_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
13 changes: 12 additions & 1 deletion channeldb/payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,17 @@ const (
// destination was found during path finding.
FailureReasonNoRoute FailureReason = 1

// FailureReasonError indicates that an unexpected error happened during
// payment.
FailureReasonError FailureReason = 2

// FailureReasonIncorrectPaymentDetails indicates that either the hash
// is unknown or the final cltv delta or amount is incorrect.
FailureReasonIncorrectPaymentDetails FailureReason = 3
Comment thread
cfromknecht marked this conversation as resolved.
Outdated

// TODO(halseth): cancel state.

// TODO(joostjager): Add failure reasons for:
// UnknownPaymentHash, FinalInvalidAmt, FinalInvalidCltv
// LocalLiquidityInsufficient, RemoteCapacityInsufficient.
)

Expand All @@ -108,6 +115,10 @@ func (r FailureReason) String() string {
return "timeout"
case FailureReasonNoRoute:
return "no_route"
case FailureReasonError:
return "error"
case FailureReasonIncorrectPaymentDetails:
return "incorrect_payment_details"
}

return "unknown"
Expand Down
240 changes: 132 additions & 108 deletions lnrpc/routerrpc/router.pb.go

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions lnrpc/routerrpc/router.proto
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ message SendPaymentRequest {
maximum enforced.
*/
int32 cltv_limit = 9;

/**
Optional route hints to reach the destination through private channels.
*/
repeated lnrpc.RouteHint route_hints = 10 [json_name = "route_hints"];
Comment thread
Roasbeef marked this conversation as resolved.
Outdated
}

message TrackPaymentRequest {
Expand Down Expand Up @@ -86,6 +91,17 @@ enum PaymentState {
routes to the destination at all.
*/
FAILED_NO_ROUTE = 3;

/**
A non-recoverable error has occured.
*/
FAILED_ERROR = 4;

/**
Payment details incorrect (unknown hash, invalid amt or
invalid final cltv delta)
*/
FAILED_INCORRECT_PAYMENT_DETAILS = 5;
Comment thread
Roasbeef marked this conversation as resolved.
Outdated
}


Expand Down
59 changes: 58 additions & 1 deletion lnrpc/routerrpc/router_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"time"

"github.com/btcsuite/btcd/btcec"

"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnrpc"
Expand Down Expand Up @@ -382,6 +384,15 @@ func (r *RouterBackend) extractIntentFromSendRequest(
payIntent.PayAttemptTimeout = time.Second *
time.Duration(rpcPayReq.TimeoutSeconds)

// Route hints.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't attempt to also parse the route hints from the regular payment request.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Payment request route hints are appended to the list of hints further down. This allows you to augment the encoded hints with a few more.

routeHints, err := unmarshallRouteHints(
rpcPayReq.RouteHints,
)
if err != nil {
return nil, err
}
payIntent.RouteHints = routeHints

// If the payment request field isn't blank, then the details of the
// invoice are encoded entirely within the encoded payReq. So we'll
// attempt to decode it, populating the payment accordingly.
Expand Down Expand Up @@ -443,7 +454,9 @@ func (r *RouterBackend) extractIntentFromSendRequest(
copy(payIntent.Target[:], destKey)

payIntent.FinalCLTVDelta = uint16(payReq.MinFinalCLTVExpiry())
payIntent.RouteHints = payReq.RouteHints
payIntent.RouteHints = append(
payIntent.RouteHints, payReq.RouteHints...,
)
} else {
// Otherwise, If the payment request field was not specified
// (and a custom route wasn't specified), construct the payment
Expand Down Expand Up @@ -493,6 +506,50 @@ func (r *RouterBackend) extractIntentFromSendRequest(
return payIntent, nil
}

// unmarshallRouteHints unmarshalls a list of route hints.
func unmarshallRouteHints(rpcRouteHints []*lnrpc.RouteHint) (
[][]zpay32.HopHint, error) {

routeHints := make([][]zpay32.HopHint, 0, len(rpcRouteHints))
for _, rpcRouteHint := range rpcRouteHints {
routeHint := make(
[]zpay32.HopHint, 0, len(rpcRouteHint.HopHints),
)
for _, rpcHint := range rpcRouteHint.HopHints {
hint, err := unmarshallHopHint(rpcHint)
if err != nil {
return nil, err
}

routeHint = append(routeHint, hint)
}
routeHints = append(routeHints, routeHint)
}

return routeHints, nil
}

// unmarshallHopHint unmarshalls a single hop hint.
func unmarshallHopHint(rpcHint *lnrpc.HopHint) (zpay32.HopHint, error) {
pubBytes, err := hex.DecodeString(rpcHint.NodeId)
if err != nil {
return zpay32.HopHint{}, err
}

pubkey, err := btcec.ParsePubKey(pubBytes, btcec.S256())
if err != nil {
return zpay32.HopHint{}, err
}

return zpay32.HopHint{
NodeID: pubkey,
ChannelID: rpcHint.ChanId,
FeeBaseMSat: rpcHint.FeeBaseMsat,
FeeProportionalMillionths: rpcHint.FeeProportionalMillionths,
CLTVExpiryDelta: uint16(rpcHint.CltvExpiryDelta),
}, nil
}

// ValidatePayReqExpiry checks if the passed payment request has expired. In
// the case it has expired, an error will be returned.
func ValidatePayReqExpiry(payReq *zpay32.Invoice) error {
Expand Down
50 changes: 38 additions & 12 deletions lnrpc/routerrpc/router_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -535,20 +537,21 @@ 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 {
switch result.FailureReason {

case channeldb.FailureReasonTimeout:
status.State = PaymentState_FAILED_TIMEOUT

case channeldb.FailureReasonNoRoute:
status.State = PaymentState_FAILED_NO_ROUTE

default:
return errors.New("unknown failure reason")
state, err := marshallFailureReason(
result.FailureReason,
)
if err != nil {
return err
}
status.State = state
if result.Route != nil {
status.Route = router.MarshallRoute(
result.Route,
)
}
}

Expand All @@ -565,3 +568,26 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash,

return nil
}

// marshallFailureReason marshalls the failure reason to the corresponding rpc
// type.
func marshallFailureReason(reason channeldb.FailureReason) (
PaymentState, error) {

switch reason {

case channeldb.FailureReasonTimeout:
return PaymentState_FAILED_TIMEOUT, nil

case channeldb.FailureReasonNoRoute:
return PaymentState_FAILED_NO_ROUTE, nil

case channeldb.FailureReasonError:
return PaymentState_FAILED_ERROR, nil

case channeldb.FailureReasonIncorrectPaymentDetails:
return PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS, nil
}

return 0, errors.New("unknown failure reason")
}
2 changes: 1 addition & 1 deletion lntest/itest/lnd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13803,7 +13803,7 @@ func testHoldInvoicePersistence(net *lntest.NetworkHarness, t *harnessTest) {
status.State)
}
} else {
if status.State != routerrpc.PaymentState_FAILED_NO_ROUTE {
if status.State != routerrpc.PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS {
t.Fatalf("state not failed: %v",
status.State)
}
Expand Down
Loading