routing/pathfind: fix TLV fallback for unadvertised hops

In this commit, we fix a bug that prevents us from sending custom
records to nodes that aren't in the graph. Previously we would simply
fail if we were unable to retrieve the node's features.

To remedy, we add the option of supplying the destination's feature bits
into path finding. If present, we will use them directly without
consulting the graph, resolving the original issue. Instead, we will
only consult the graph as a fallback, which will still fail if the node
doesn't exist since the TLV features won't be populated in the empty
feature vector.

Furthermore, this also permits us to provide "virtual features" into the
pathfinding logic, where we make assumptions about what the receiver
supports even if the feature vector isn't actually taken from an
invoice. This can useful in cases like keysend, where we don't have an
invoice, but we can still attempt the payment if we assume the receiver
supports TLV.
This commit is contained in:
Conner Fromknecht committed 2019-12-18 23:53:51 -08:00
1 parent bd66c0d34e
commit cfa3fe2921
2 files changed
+188 -21

No files matched your search

+30 -13
View File
@@ -292,6 +292,11 @@ type RestrictParams struct {
// DestCustomRecords contains the custom records to drop off at the
// final hop, if any.
DestCustomRecords record.CustomSet
// DestFeatures is a feature vector describing what the final hop
// supports. If none are provided, pathfinding will try to inspect any
// features on the node announcement instead.
DestFeatures *lnwire.FeatureVector
}
// PathFindingConfig defines global parameters that control the trade-off in
@@ -395,29 +400,41 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
defer tx.Rollback()
}
if len(r.DestCustomRecords) > 0 {
// Check if the target has TLV enabled
// If no destination features are provided, we will load what features
// we have for the target node from our graph.
features := r.DestFeatures
if features == nil {
targetKey, err := btcec.ParsePubKey(target[:], btcec.S256())
if err != nil {
return nil, err
}
targetNode, err := g.graph.FetchLightningNode(targetKey)
if err != nil {
return nil, err
}
switch {
if targetNode.Features != nil {
supportsTLV := targetNode.Features.HasFeature(
lnwire.TLVOnionPayloadOptional,
)
if !supportsTLV {
return nil, errNoTlvPayload
}
// If the node exists and has features, use them directly.
case err == nil:
features = targetNode.Features
// If an error other than the node not existing is hit, abort.
case err != channeldb.ErrGraphNodeNotFound:
return nil, err
// Otherwise, we couldn't find a node announcement, populate a
// blank feature vector.
default:
features = lnwire.EmptyFeatureVector()
}
}
// If the caller needs to send custom records, check that our
// destination feature vector supports TLV.
if len(r.DestCustomRecords) > 0 &&
!features.HasFeature(lnwire.TLVOnionPayloadOptional) {
return nil, errNoTlvPayload
}
// If we are routing from ourselves, check that we have enough local
// balance available.
if source == self {