mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-10-09 22:33:57 +02:00
routing: use mission control to select blinded paths
Add a `FindBlindedPaths` method to the `ChannelRouter` which will use the new `findBlindedPaths` function to get a set of candidate blinded path routes. It then uses mission control to select the best of these paths. Note that as of this commit, the MC data we get from these queries won't mean much since we wont have data about a channel in the direction towards us. But we do this now in preparation for a future PR which will start writing mission control success pairs for successful receives from blinded route paths.
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -675,6 +676,131 @@ func (r *ChannelRouter) FindRoute(req *RouteRequest) (*route.Route, float64,
|
||||
return route, probability, nil
|
||||
}
|
||||
|
||||
// probabilitySource defines the signature of a function that can be used to
|
||||
// query the success probability of sending a given amount between the two
|
||||
// given vertices.
|
||||
type probabilitySource func(route.Vertex, route.Vertex, lnwire.MilliSatoshi,
|
||||
btcutil.Amount) float64
|
||||
|
||||
// BlindedPathRestrictions are a set of constraints to adhere to when
|
||||
// choosing a set of blinded paths to this node.
|
||||
type BlindedPathRestrictions struct {
|
||||
// MinDistanceFromIntroNode is the minimum number of _real_ (non-dummy)
|
||||
// hops to include in a blinded path. Since we post-fix dummy hops, this
|
||||
// is the minimum distance between our node and the introduction node
|
||||
// of the path. This doesn't include our node, so if the minimum is 1,
|
||||
// then the path will contain at minimum our node along with an
|
||||
// introduction node hop.
|
||||
MinDistanceFromIntroNode uint8
|
||||
|
||||
// NumHops is the number of hops that each blinded path should consist
|
||||
// of. If paths are found with a number of hops less that NumHops, then
|
||||
// dummy hops will be padded on to the route. This value doesn't
|
||||
// include our node, so if the maximum is 1, then the path will contain
|
||||
// our node along with an introduction node hop.
|
||||
NumHops uint8
|
||||
|
||||
// MaxNumPaths is the maximum number of blinded paths to select.
|
||||
MaxNumPaths uint8
|
||||
}
|
||||
|
||||
// FindBlindedPaths finds a selection of paths to the destination node that can
|
||||
// be used in blinded payment paths.
|
||||
func (r *ChannelRouter) FindBlindedPaths(destination route.Vertex,
|
||||
amt lnwire.MilliSatoshi, probabilitySrc probabilitySource,
|
||||
restrictions *BlindedPathRestrictions) ([]*route.Route, error) {
|
||||
|
||||
// First, find a set of candidate paths given the destination node and
|
||||
// path length restrictions.
|
||||
paths, err := findBlindedPaths(
|
||||
r.cfg.RoutingGraph, destination, &blindedPathRestrictions{
|
||||
minNumHops: restrictions.MinDistanceFromIntroNode,
|
||||
maxNumHops: restrictions.NumHops,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// routeWithProbability groups a route with the probability of a
|
||||
// payment of the given amount succeeding on that path.
|
||||
type routeWithProbability struct {
|
||||
route *route.Route
|
||||
probability float64
|
||||
}
|
||||
|
||||
// Iterate over all the candidate paths and determine the success
|
||||
// probability of each path given the data we have about forwards
|
||||
// between any two nodes on a path.
|
||||
routes := make([]*routeWithProbability, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
if len(path) < 1 {
|
||||
return nil, fmt.Errorf("a blinded path must have at " +
|
||||
"least one hop")
|
||||
}
|
||||
|
||||
var (
|
||||
introNode = path[0].vertex
|
||||
prevNode = introNode
|
||||
hops = make(
|
||||
[]*route.Hop, 0, len(path)-1,
|
||||
)
|
||||
totalRouteProbability = float64(1)
|
||||
)
|
||||
|
||||
// For each set of hops on the path, get the success probability
|
||||
// of a forward between those two vertices and use that to
|
||||
// update the overall route probability.
|
||||
for j := 1; j < len(path); j++ {
|
||||
probability := probabilitySrc(
|
||||
prevNode, path[j].vertex, amt,
|
||||
path[j-1].edgeCapacity,
|
||||
)
|
||||
|
||||
totalRouteProbability *= probability
|
||||
|
||||
hops = append(hops, &route.Hop{
|
||||
PubKeyBytes: path[j].vertex,
|
||||
ChannelID: path[j-1].edgePolicy.ChannelID,
|
||||
})
|
||||
|
||||
prevNode = path[j].vertex
|
||||
}
|
||||
|
||||
// Don't bother adding a route if its success probability less
|
||||
// minimum that can be assigned to any single pair.
|
||||
if totalRouteProbability <= DefaultMinRouteProbability {
|
||||
continue
|
||||
}
|
||||
|
||||
routes = append(routes, &routeWithProbability{
|
||||
route: &route.Route{
|
||||
SourcePubKey: introNode,
|
||||
Hops: hops,
|
||||
},
|
||||
probability: totalRouteProbability,
|
||||
})
|
||||
}
|
||||
|
||||
// Sort the routes based on probability.
|
||||
sort.Slice(routes, func(i, j int) bool {
|
||||
return routes[i].probability < routes[j].probability
|
||||
})
|
||||
|
||||
// Now just choose the best paths up until the maximum number of allowed
|
||||
// paths.
|
||||
bestRoutes := make([]*route.Route, 0, restrictions.MaxNumPaths)
|
||||
for _, route := range routes {
|
||||
if len(bestRoutes) >= int(restrictions.MaxNumPaths) {
|
||||
break
|
||||
}
|
||||
|
||||
bestRoutes = append(bestRoutes, route.route)
|
||||
}
|
||||
|
||||
return bestRoutes, nil
|
||||
}
|
||||
|
||||
// generateNewSessionKey generates a new ephemeral private key to be used for a
|
||||
// payment attempt.
|
||||
func generateNewSessionKey() (*btcec.PrivateKey, error) {
|
||||
|
Reference in New Issue
Block a user