routing/pathfind: set final hop features used in pathfinding

In this commit, we overwrite the final hop's features with either the
destination features or those loaded from the graph fallback. This
ensures that the same features used in pathfinding will be provided to
route construction.

In an earlier commit, we validated the final hop's transitive feature
dependencies, so we also add validation to non-final nodes.
This commit is contained in:
Conner Fromknecht 2019-12-18 23:54:49 -08:00
parent 990d55d08c
commit 71e05e05bf
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
2 changed files with 58 additions and 0 deletions

@ -644,6 +644,36 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
edge.ChannelID)
}
switch {
// If this edge takes us to the final hop, we'll set the node
// features to those determined above. These are either taken
// from the destination features, e.g. virtual or invoice
// features, or loaded as a fallback from the graph. The
// transitive dependencies were already validated above, so no
// need to do so now.
//
// NOTE: This may overwrite features loaded from the graph if
// destination features were provided. This is fine though,
// since our route construction does not care where the features
// are actually taken from. In the future we may wish to do
// route construction within findPath, and avoid using
// ChannelEdgePolicy altogether.
case edge.Node.PubKeyBytes == target:
edge.Node.Features = features
// Otherwise, this is some other intermediary node. Verify the
// transitive feature dependencies for this node, and skip the
// channel if they are invalid.
default:
err := feature.ValidateDeps(edge.Node.Features)
if err != nil {
log.Tracef("Node %x has invalid features",
edge.Node.PubKeyBytes)
return
}
}
// All conditions are met and this new tentative distance is
// better than the current best known distance to this node.
// The new better distance is recorded, and also our "next hop"

@ -1482,6 +1482,21 @@ func TestMissingFeatureDep(t *testing.T) {
Features: payAddrFeatures,
}, 0,
),
asymmetricTestChannel("conner", "joost", 100000,
&testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000000,
Features: payAddrFeatures,
},
&testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: 100000000,
}, 0,
),
}
ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
@ -1530,6 +1545,19 @@ func TestMissingFeatureDep(t *testing.T) {
t.Fatalf("path should have been found: %v", err)
}
assertExpectedPath(t, ctx.testGraphInstance.aliasMap, path, "conner")
// Finally, try to find a route to joost through conner. The
// destination features are set properly from the previous assertions,
// but conner's feature vector in the graph is still broken. We expect
// errNoPathFound and not the missing feature dep err above since
// intermediate hops are simply skipped if they have invalid feature
// vectors, leaving no possible route to joost.
joost := ctx.testGraphInstance.aliasMap["joost"]
_, err = find(&restrictions, joost)
if err != errNoPathFound {
t.Fatalf("path shouldn't have been found: %v", err)
}
}
// TestDestPaymentAddr asserts that we properly detect when we can send a