lnrpc+routing: remove k shortest path finding
This commit is contained in:
13 files changed
+714
-1228
No files matched your search
@@ -616,180 +616,3 @@ func findPath(g *graphParams, r *RestrictParams, source, target route.Vertex,
|
||||
|
||||
return pathEdges, nil
|
||||
}
|
||||
|
||||
// findPaths implements a k-shortest paths algorithm to find all the reachable
|
||||
// paths between the passed source and target. The algorithm will continue to
|
||||
// traverse the graph until all possible candidate paths have been depleted.
|
||||
// This function implements a modified version of Yen's. To find each path
|
||||
// itself, we utilize our modified version of Dijkstra's found above. When
|
||||
// examining possible spur and root paths, rather than removing edges or
|
||||
// Vertexes from the graph, we instead utilize a Vertex+edge black-list that
|
||||
// will be ignored by our modified Dijkstra's algorithm. With this approach, we
|
||||
// make our inner path finding algorithm aware of our k-shortest paths
|
||||
// algorithm, rather than attempting to use an unmodified path finding
|
||||
// algorithm in a block box manner.
|
||||
func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
||||
source, target route.Vertex, amt lnwire.MilliSatoshi,
|
||||
restrictions *RestrictParams, numPaths uint32,
|
||||
bandwidthHints map[uint64]lnwire.MilliSatoshi) (
|
||||
[][]*channeldb.ChannelEdgePolicy, error) {
|
||||
|
||||
// TODO(roasbeef): modifying ordering within heap to eliminate final
|
||||
// sorting step?
|
||||
var (
|
||||
shortestPaths [][]*channeldb.ChannelEdgePolicy
|
||||
candidatePaths pathHeap
|
||||
)
|
||||
|
||||
// First we'll find a single shortest path from the source (our
|
||||
// selfNode) to the target destination that's capable of carrying amt
|
||||
// satoshis along the path before fees are calculated.
|
||||
startingPath, err := findPath(
|
||||
&graphParams{
|
||||
tx: tx,
|
||||
graph: graph,
|
||||
bandwidthHints: bandwidthHints,
|
||||
},
|
||||
restrictions, source, target, amt,
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to find path: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Manually insert a "self" edge emanating from ourselves. This
|
||||
// self-edge is required in order for the path finding algorithm to
|
||||
// function properly.
|
||||
firstPath := make([]*channeldb.ChannelEdgePolicy, 0, len(startingPath)+1)
|
||||
firstPath = append(firstPath, &channeldb.ChannelEdgePolicy{
|
||||
Node: &channeldb.LightningNode{PubKeyBytes: source},
|
||||
})
|
||||
firstPath = append(firstPath, startingPath...)
|
||||
|
||||
shortestPaths = append(shortestPaths, firstPath)
|
||||
|
||||
// While we still have candidate paths to explore we'll keep exploring
|
||||
// the sub-graphs created to find the next k-th shortest path.
|
||||
for k := uint32(1); k < numPaths; k++ {
|
||||
prevShortest := shortestPaths[k-1]
|
||||
|
||||
// We'll examine each edge in the previous iteration's shortest
|
||||
// path in order to find path deviations from each node in the
|
||||
// path.
|
||||
for i := 0; i < len(prevShortest)-1; i++ {
|
||||
// These two maps will mark the edges and Vertexes
|
||||
// we'll exclude from the next path finding attempt.
|
||||
// These are required to ensure the paths are unique
|
||||
// and loopless.
|
||||
ignoredEdges := make(map[EdgeLocator]struct{})
|
||||
ignoredVertexes := make(map[route.Vertex]struct{})
|
||||
|
||||
for e := range restrictions.IgnoredEdges {
|
||||
ignoredEdges[e] = struct{}{}
|
||||
}
|
||||
for n := range restrictions.IgnoredNodes {
|
||||
ignoredVertexes[n] = struct{}{}
|
||||
}
|
||||
|
||||
// Our spur node is the i-th node in the prior shortest
|
||||
// path, and our root path will be all nodes in the
|
||||
// path leading up to our spurNode.
|
||||
spurNode := prevShortest[i].Node
|
||||
rootPath := prevShortest[:i+1]
|
||||
|
||||
// Before we kickoff our next path finding iteration,
|
||||
// we'll find all the edges we need to ignore in this
|
||||
// next round. This ensures that we create a new unique
|
||||
// path.
|
||||
for _, path := range shortestPaths {
|
||||
// If our current rootPath is a prefix of this
|
||||
// shortest path, then we'll remove the edge
|
||||
// directly _after_ our spur node from the
|
||||
// graph so we don't repeat paths.
|
||||
if len(path) > i+1 &&
|
||||
isSamePath(rootPath, path[:i+1]) {
|
||||
|
||||
locator := newEdgeLocator(path[i+1])
|
||||
ignoredEdges[*locator] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Next we'll remove all entries in the root path that
|
||||
// aren't the current spur node from the graph. This
|
||||
// ensures we don't create a path with loops.
|
||||
for _, hop := range rootPath {
|
||||
node := hop.Node.PubKeyBytes
|
||||
if node == spurNode.PubKeyBytes {
|
||||
continue
|
||||
}
|
||||
|
||||
ignoredVertexes[route.Vertex(node)] = struct{}{}
|
||||
}
|
||||
|
||||
// With the edges that are part of our root path, and
|
||||
// the Vertexes (other than the spur path) within the
|
||||
// root path removed, we'll attempt to find another
|
||||
// shortest path from the spur node to the destination.
|
||||
//
|
||||
// TODO: Fee limit passed to spur path finding isn't
|
||||
// correct, because it doesn't take into account the
|
||||
// fees already paid on the root path.
|
||||
//
|
||||
// TODO: Outgoing channel restriction isn't obeyed for
|
||||
// spur paths.
|
||||
spurRestrictions := &RestrictParams{
|
||||
IgnoredEdges: ignoredEdges,
|
||||
IgnoredNodes: ignoredVertexes,
|
||||
FeeLimit: restrictions.FeeLimit,
|
||||
}
|
||||
|
||||
spurPath, err := findPath(
|
||||
&graphParams{
|
||||
tx: tx,
|
||||
graph: graph,
|
||||
bandwidthHints: bandwidthHints,
|
||||
},
|
||||
spurRestrictions, spurNode.PubKeyBytes,
|
||||
target, amt,
|
||||
)
|
||||
|
||||
// If we weren't able to find a path, we'll continue to
|
||||
// the next round.
|
||||
if IsError(err, ErrNoPathFound) {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the new combined path by concatenating the
|
||||
// rootPath to the spurPath.
|
||||
newPathLen := len(rootPath) + len(spurPath)
|
||||
newPath := path{
|
||||
hops: make([]*channeldb.ChannelEdgePolicy, 0, newPathLen),
|
||||
dist: newPathLen,
|
||||
}
|
||||
newPath.hops = append(newPath.hops, rootPath...)
|
||||
newPath.hops = append(newPath.hops, spurPath...)
|
||||
|
||||
// TODO(roasbeef): add and consult path finger print
|
||||
|
||||
// We'll now add this newPath to the heap of candidate
|
||||
// shortest paths.
|
||||
heap.Push(&candidatePaths, newPath)
|
||||
}
|
||||
|
||||
// If our min-heap of candidate paths is empty, then we can
|
||||
// exit early.
|
||||
if candidatePaths.Len() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// To conclude this latest iteration, we'll take the shortest
|
||||
// path in our set of candidate paths and add it to our
|
||||
// shortestPaths list as the *next* shortest path.
|
||||
nextShortestPath := heap.Pop(&candidatePaths).(path).hops
|
||||
shortestPaths = append(shortestPaths, nextShortestPath)
|
||||
}
|
||||
|
||||
return shortestPaths, nil
|
||||
}
|
||||
Reference in new issue
Block a user