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:
2 files changed
+188
-21
No files matched your search
+30
-13
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user