Repository: lightningnetwork/lnd (go)

Issue:
Payment is stuck in flight after no routes are found
### Background

Two days ago, two payment attemps to Pheonix got stuck `IN_FLIGHT` and they still are now. Both attempts have the same output in ours logs:

```
May 22 21:01:48 ip-10-2-1-20 lnd[2438]: 2020-05-22 21:01:48.077 [WRN] CRTR: Failed to find route for payment 5eb9b3641ddb5395df448a19e6c647086a3d6c97715fdea48c363c41b26afcee: missing feature dependency: 9
May 22 21:01:48 ip-10-2-1-20 lnd[2438]: 2020-05-22 21:01:48.077 [ERR] CRTR: Payment with hash 5eb9b3641ddb5395df448a19e6c647086a3d6c97715fdea48c363c41b26afcee failed: missing feature dependency: 9
```

One of the invoices is `lnbc10n1p0vs0d2pp5t6umxeqamdfeth6y3gv7d3j8pp4r6myhw90aafyvxc7yrvn2lnhqdqc2p5x7etwd9uzqurp09kk2mn5xqrrss9qtzqqqqqq9qqqsp5fkhdnf2gl92xrfw80as34refnsm9a57rzmq38tm7ysvwpchkhussrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glcll7vvhf9q6ppwqqqqqlgqqqqqeqqjq08rlj7aup45w2pwz7pay2ct9fs933vt8ck5ex3zerfgatar2ql0kzrw0nmlyn95hvtz0g20ah8649xzvsene6we02x8arrg862mwv5qpuaar3w`

The output of track payment is:
```
$ lncli trackpayment 5eb9b3641ddb5395df448a19e6c647086a3d6c97715fdea48c363c41b26afcee
+------------+--------------+--------------+--------------+-----+----------+----------+-------+
| HTLC_STATE | ATTEMPT_TIME | RESOLVE_TIME | RECEIVER_AMT | FEE | TIMELOCK | CHAN_OUT | ROUTE |
+------------+--------------+--------------+--------------+-----+----------+----------+-------+
+------------+--------------+--------------+--------------+-----+----------+----------+-------+
Amount + fee:   0 + 0 sat
Payment hash:   5eb9b3641ddb5395df448a19e6c647086a3d6c97715fdea48c363c41b26afcee
Payment status: IN_FLIGHT
```

### Your environment

* `lncli version 0.10.0-beta commit=v0.10.0-beta`
* `Linux ip-10-2-1-20 5.3.0-1017-aws #18~18.04.1-Ubuntu SMP Wed Apr 8 15:12:16 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux`
* `Bitcoin Core Daemon version v0.18.1`

### Steps to reproduce

Generate an invoice with Pheonix app and try to pay it. This seems to be happening consistentely.

### Expected behaviour

Either the payment should be succesfull or it should correctly fail the attempt.

### Actual behaviour

The payment is stuck `IN_FLIGHT` for days.


Pull request:
routing: don't treat bad features as an unexpected error
Previous behavior led to the payment loop being abandoned immediately, resulting in a payment stuck in state in_flight.

Fixes #4319 

Patch:
diff --git a/routing/pathfind.go b/routing/pathfind.go
index 70efb1c2d..6d5e9abd5 100644
--- a/routing/pathfind.go
+++ b/routing/pathfind.go
@@ -420,13 +420,15 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
 	// required features.
 	err := feature.ValidateRequired(features)
 	if err != nil {
-		return nil, err
+		log.Warnf("Pathfinding destination node features: %v", err)
+		return nil, errUnknownRequiredFeature
 	}
 
 	// Ensure that all transitive dependencies are set.
 	err = feature.ValidateDeps(features)
 	if err != nil {
-		return nil, err
+		log.Warnf("Pathfinding destination node features: %v", err)
+		return nil, errMissingDependentFeature
 	}
 
 	// Now that we know the feature vector is well formed, we'll proceed in
diff --git a/routing/payment_session.go b/routing/payment_session.go
index b4aac5aad..2d7ef8bf8 100644
--- a/routing/payment_session.go
+++ b/routing/payment_session.go
@@ -37,6 +37,14 @@ const (
 	// errEmptyPaySession is returned when the empty payment session is
 	// queried for a route.
 	errEmptyPaySession
+
+	// errUnknownRequiredFeature is returned when the destination node
+	// requires an unknown feature.
+	errUnknownRequiredFeature
+
+	// errMissingDependentFeature is returned when the destination node
+	// misses a feature that a feature that we require depends on.
+	errMissingDependentFeature
 )
 
 var (
@@ -64,6 +72,12 @@ func (e noRouteError) Error() string {
 	case errInsufficientBalance:
 		return "insufficient local balance"
 
+	case errUnknownRequiredFeature:
+		return "unknown required feature"
+
+	case errMissingDependentFeature:
+		return "missing dependent feature"
+
 	default:
 		return "unknown no-route error"
 	}
@@ -76,7 +90,9 @@ func (e noRouteError) FailureReason() channeldb.FailureReason {
 		errNoTlvPayload,
 		errNoPaymentAddr,
 		errNoPathFound,
-		errEmptyPaySession:
+		errEmptyPaySession,
+		errUnknownRequiredFeature,
+		errMissingDependentFeature:
 
 		return channeldb.FailureReasonNoRoute
 


Test patch:
diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go
index 28bd29330..ba1f39ebd 100644
--- a/routing/pathfind_test.go
+++ b/routing/pathfind_test.go
@@ -23,7 +23,6 @@ import (
 	"github.com/btcsuite/btcd/wire"
 	"github.com/btcsuite/btcutil"
 	"github.com/lightningnetwork/lnd/channeldb"
-	"github.com/lightningnetwork/lnd/feature"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/record"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -1590,9 +1589,7 @@ func TestMissingFeatureDep(t *testing.T) {
 	joost := ctx.keyFromAlias("joost")
 
 	_, err := ctx.findPath(conner, 100)
-	if err != feature.NewErrMissingFeatureDep(
-		lnwire.TLVOnionPayloadOptional,
-	) {
+	if err != errMissingDependentFeature {
 		t.Fatalf("path shouldn't have been found: %v", err)
 	}
 
@@ -1667,9 +1664,8 @@ func TestUnknownRequiredFeatures(t *testing.T) {
 	// Conner's node in the graph has an unknown required feature (100).
 	// Pathfinding should fail since we check the destination's features for
 	// unknown required features before beginning pathfinding.
-	expErr := feature.NewErrUnknownRequired([]lnwire.FeatureBit{100})
 	_, err := ctx.findPath(conner, 100)
-	if !reflect.DeepEqual(err, expErr) {
+	if !reflect.DeepEqual(err, errUnknownRequiredFeature) {
 		t.Fatalf("path shouldn't have been found: %v", err)
 	}