diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index a0125b63..44948924 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -406,9 +406,17 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, // to our destination, respecting the recommendations from // missionControl. path, err := findPath( - nil, p.mc.graph, p.additionalEdges, p.mc.selfNode, - payment.Target, pruneView.vertexes, pruneView.edges, - payment.Amount, payment.FeeLimit, p.bandwidthHints, + &graphParams{ + graph: p.mc.graph, + additionalEdges: p.additionalEdges, + bandwidthHints: p.bandwidthHints, + }, + &restrictParams{ + ignoredNodes: pruneView.vertexes, + ignoredEdges: pruneView.edges, + feeLimit: payment.FeeLimit, + }, + p.mc.selfNode, payment.Target, payment.Amount, ) if err != nil { return nil, err diff --git a/routing/pathfind.go b/routing/pathfind.go index 7ab7ef8c..a1910292 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -446,6 +446,46 @@ func edgeWeight(lockedAmt lnwire.MilliSatoshi, fee lnwire.MilliSatoshi, return int64(fee) + timeLockPenalty } +// graphParams wraps the set of graph parameters passed to findPath. +type graphParams struct { + // tx can be set to an existing db transaction. If not set, a new + // transaction will be started. + tx *bbolt.Tx + + // graph is the ChannelGraph to be used during path finding. + graph *channeldb.ChannelGraph + + // additionalEdges is an optional set of edges that should be + // considered during path finding, that is not already found in the + // channel graph. + additionalEdges map[Vertex][]*channeldb.ChannelEdgePolicy + + // bandwidthHints is an optional map from channels to bandwidths that + // can be populated if the caller has a better estimate of the current + // channel bandwidth than what is found in the graph. If set, it will + // override the capacities and disabled flags found in the graph for + // local channels when doing path finding. In particular, it should be + // set to the current available sending bandwidth for active local + // channels, and 0 for inactive channels. + bandwidthHints map[uint64]lnwire.MilliSatoshi +} + +// restrictParams wraps the set of restrictions passed to findPath that the +// found path must adhere to. +type restrictParams struct { + // ignoredNodes is an optional set of nodes that should be ignored if + // encountered during path finding. + ignoredNodes map[Vertex]struct{} + + // ignoredEdges is an optional set of edges that should be ignored if + // encountered during path finding. + ignoredEdges map[uint64]struct{} + + // feeLimit is a maximum fee amount allowed to be used on the path from + // the source to the target. + feeLimit lnwire.MilliSatoshi +} + // findPath attempts to find a path from the source node within the // ChannelGraph to the target node that's capable of supporting a payment of // `amt` value. The current approach implemented is modified version of @@ -457,16 +497,14 @@ func edgeWeight(lockedAmt lnwire.MilliSatoshi, fee lnwire.MilliSatoshi, // destination node back to source. This is to properly accumulate fees // that need to be paid along the path and accurately check the amount // to forward at every node against the available bandwidth. -func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, - additionalEdges map[Vertex][]*channeldb.ChannelEdgePolicy, +func findPath(g *graphParams, r *restrictParams, sourceNode *channeldb.LightningNode, target *btcec.PublicKey, - ignoredNodes map[Vertex]struct{}, ignoredEdges map[uint64]struct{}, - amt lnwire.MilliSatoshi, feeLimit lnwire.MilliSatoshi, - bandwidthHints map[uint64]lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) { + amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) { var err error + tx := g.tx if tx == nil { - tx, err = graph.Database().Begin(false) + tx, err = g.graph.Database().Begin(false) if err != nil { return nil, err } @@ -483,7 +521,8 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, // also returns the source node, so there is no need to add the source // node explicitly. distance := make(map[Vertex]nodeWithDist) - if err := graph.ForEachNode(tx, func(_ *bbolt.Tx, node *channeldb.LightningNode) error { + if err := g.graph.ForEachNode(tx, func(_ *bbolt.Tx, + node *channeldb.LightningNode) error { // TODO(roasbeef): with larger graph can just use disk seeks // with a visited map distance[Vertex(node.PubKeyBytes)] = nodeWithDist{ @@ -496,7 +535,7 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, } additionalEdgesWithSrc := make(map[Vertex][]*edgePolicyWithSource) - for vertex, outgoingEdgePolicies := range additionalEdges { + for vertex, outgoingEdgePolicies := range g.additionalEdges { // We'll also include all the nodes found within the additional // edges that are not known to us yet in the distance map. node := &channeldb.LightningNode{PubKeyBytes: vertex} @@ -550,19 +589,24 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, fromVertex := Vertex(fromNode.PubKeyBytes) - // If the edge is currently disabled, then we'll stop here, as - // we shouldn't attempt to route through it. + // If this is not a local channel and it is disabled, we will + // skip it. + // TODO(halseth): also ignore disable flags for non-local + // channels if bandwidth hint is set? + isSourceChan := fromVertex == sourceVertex edgeFlags := lnwire.ChanUpdateFlag(edge.Flags) - if edgeFlags&lnwire.ChanUpdateDisabled != 0 { + isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0 + + if !isSourceChan && isDisabled { return } // If this vertex or edge has been black listed, then we'll // skip exploring this edge. - if _, ok := ignoredNodes[fromVertex]; ok { + if _, ok := r.ignoredNodes[fromVertex]; ok { return } - if _, ok := ignoredEdges[edge.ChannelID]; ok { + if _, ok := r.ignoredEdges[edge.ChannelID]; ok { return } @@ -609,7 +653,7 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, // Check if accumulated fees would exceed fee limit when this // node would be added to the path. totalFee := amountToReceive - amt - if totalFee > feeLimit { + if totalFee > r.feeLimit { return } @@ -697,7 +741,7 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, // We'll query the lower layer to see if we can obtain // any more up to date information concerning the // bandwidth of this edge. - edgeBandwidth, ok := bandwidthHints[edgeInfo.ChannelID] + edgeBandwidth, ok := g.bandwidthHints[edgeInfo.ChannelID] if !ok { // If we don't have a hint for this edge, then // we'll just use the known Capacity as the @@ -734,7 +778,8 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, // and use the payment amount as its capacity. bandWidth := partialPath.amountToReceive for _, reverseEdge := range additionalEdgesWithSrc[bestNode.PubKeyBytes] { - processEdge(reverseEdge.sourceNode, reverseEdge.edge, bandWidth, pivot) + processEdge(reverseEdge.sourceNode, reverseEdge.edge, + bandWidth, pivot) } } @@ -745,7 +790,8 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph, "destination") } - // Use the nextHop map to unravel the forward path from source to target. + // Use the nextHop map to unravel the forward path from source to + // target. pathEdges := make([]*channeldb.ChannelEdgePolicy, 0, len(next)) currentNode := sourceVertex for currentNode != targetVertex { // TODO(roasbeef): assumes no cycles @@ -802,8 +848,17 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph, // selfNode) to the target destination that's capable of carrying amt // satoshis along the path before fees are calculated. startingPath, err := findPath( - tx, graph, nil, source, target, ignoredVertexes, ignoredEdges, - amt, feeLimit, bandwidthHints, + &graphParams{ + tx: tx, + graph: graph, + bandwidthHints: bandwidthHints, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: feeLimit, + }, + source, target, amt, ) if err != nil { log.Errorf("Unable to find path: %v", err) @@ -874,9 +929,16 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph, // root path removed, we'll attempt to find another // shortest path from the spur node to the destination. spurPath, err := findPath( - tx, graph, nil, spurNode, target, - ignoredVertexes, ignoredEdges, amt, feeLimit, - bandwidthHints, + &graphParams{ + tx: tx, + graph: graph, + bandwidthHints: bandwidthHints, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: feeLimit, + }, spurNode, target, amt, ) // If we weren't able to find a path, we'll continue to diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 3b082a4f..7de58f9c 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -556,10 +556,10 @@ func TestFindLowestFeePath(t *testing.T) { } testGraphInstance, err := createTestGraphFromChannels(testChannels) - defer testGraphInstance.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer testGraphInstance.cleanUp() sourceNode, err := testGraphInstance.graph.SourceNode() if err != nil { @@ -578,8 +578,15 @@ func TestFindLowestFeePath(t *testing.T) { paymentAmt := lnwire.NewMSatFromSatoshis(100) target := testGraphInstance.aliasMap["target"] path, err := findPath( - nil, testGraphInstance.graph, nil, sourceNode, target, - ignoredVertexes, ignoredEdges, paymentAmt, noFeeLimit, nil, + &graphParams{ + graph: testGraphInstance.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, paymentAmt, ) if err != nil { t.Fatalf("unable to find path: %v", err) @@ -676,10 +683,10 @@ func TestBasicGraphPathFinding(t *testing.T) { t.Parallel() testGraphInstance, err := parseTestGraph(basicGraphFilePath) - defer testGraphInstance.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer testGraphInstance.cleanUp() // With the test graph loaded, we'll test some basic path finding using // the pre-generated graph. Consult the testdata/basic_graph.json file @@ -717,8 +724,15 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc paymentAmt := lnwire.NewMSatFromSatoshis(test.paymentAmt) target := graphInstance.aliasMap[test.target] path, err := findPath( - nil, graphInstance.graph, nil, sourceNode, target, - ignoredVertexes, ignoredEdges, paymentAmt, test.feeLimit, nil, + &graphParams{ + graph: graphInstance.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: test.feeLimit, + }, + sourceNode, target, paymentAmt, ) if test.expectFailureNoPath { if err == nil { @@ -858,10 +872,10 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) { t.Parallel() graph, err := parseTestGraph(basicGraphFilePath) - defer graph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer graph.cleanUp() sourceNode, err := graph.graph.SourceNode() if err != nil { @@ -905,8 +919,14 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) { // We should now be able to find a path from roasbeef to doge. path, err := findPath( - nil, graph.graph, additionalEdges, sourceNode, dogePubKey, nil, nil, - paymentAmt, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + additionalEdges: additionalEdges, + }, + &restrictParams{ + feeLimit: noFeeLimit, + }, + sourceNode, dogePubKey, paymentAmt, ) if err != nil { t.Fatalf("unable to find private path to doge: %v", err) @@ -921,10 +941,10 @@ func TestKShortestPathFinding(t *testing.T) { t.Parallel() graph, err := parseTestGraph(basicGraphFilePath) - defer graph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer graph.cleanUp() sourceNode, err := graph.graph.SourceNode() if err != nil { @@ -1246,10 +1266,10 @@ func TestNewRoutePathTooLong(t *testing.T) { // Ensure that potential paths which are over the maximum hop-limit are // rejected. graph, err := parseTestGraph(excessiveHopsGraphFilePath) - defer graph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer graph.cleanUp() sourceNode, err := graph.graph.SourceNode() if err != nil { @@ -1261,12 +1281,19 @@ func TestNewRoutePathTooLong(t *testing.T) { paymentAmt := lnwire.NewMSatFromSatoshis(100) - // We start by confirming that routing a payment 20 hops away is possible. - // Alice should be able to find a valid route to ursula. + // We start by confirming that routing a payment 20 hops away is + // possible. Alice should be able to find a valid route to ursula. target := graph.aliasMap["ursula"] _, err = findPath( - nil, graph.graph, nil, sourceNode, target, ignoredVertexes, - ignoredEdges, paymentAmt, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, paymentAmt, ) if err != nil { t.Fatalf("path should have been found") @@ -1276,8 +1303,15 @@ func TestNewRoutePathTooLong(t *testing.T) { // presented to Alice. target = graph.aliasMap["vincent"] path, err := findPath( - nil, graph.graph, nil, sourceNode, target, ignoredVertexes, - ignoredEdges, paymentAmt, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, paymentAmt, ) if err == nil { t.Fatalf("should not have been able to find path, supposed to be "+ @@ -1291,10 +1325,10 @@ func TestPathNotAvailable(t *testing.T) { t.Parallel() graph, err := parseTestGraph(basicGraphFilePath) - defer graph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer graph.cleanUp() sourceNode, err := graph.graph.SourceNode() if err != nil { @@ -1318,8 +1352,15 @@ func TestPathNotAvailable(t *testing.T) { } _, err = findPath( - nil, graph.graph, nil, sourceNode, unknownNode, ignoredVertexes, - ignoredEdges, 100, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, unknownNode, 100, ) if !IsError(err, ErrNoPathFound) { t.Fatalf("path shouldn't have been found: %v", err) @@ -1330,10 +1371,10 @@ func TestPathInsufficientCapacity(t *testing.T) { t.Parallel() graph, err := parseTestGraph(basicGraphFilePath) - defer graph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer graph.cleanUp() sourceNode, err := graph.graph.SourceNode() if err != nil { @@ -1354,8 +1395,15 @@ func TestPathInsufficientCapacity(t *testing.T) { payAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin) _, err = findPath( - nil, graph.graph, nil, sourceNode, target, ignoredVertexes, - ignoredEdges, payAmt, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, ) if !IsError(err, ErrNoPathFound) { t.Fatalf("graph shouldn't be able to support payment: %v", err) @@ -1368,10 +1416,10 @@ func TestRouteFailMinHTLC(t *testing.T) { t.Parallel() graph, err := parseTestGraph(basicGraphFilePath) - defer graph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer graph.cleanUp() sourceNode, err := graph.graph.SourceNode() if err != nil { @@ -1386,8 +1434,15 @@ func TestRouteFailMinHTLC(t *testing.T) { target := graph.aliasMap["songoku"] payAmt := lnwire.MilliSatoshi(10) _, err = findPath( - nil, graph.graph, nil, sourceNode, target, ignoredVertexes, - ignoredEdges, payAmt, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, ) if !IsError(err, ErrNoPathFound) { t.Fatalf("graph shouldn't be able to support payment: %v", err) @@ -1396,15 +1451,17 @@ func TestRouteFailMinHTLC(t *testing.T) { // TestRouteFailDisabledEdge tests that if we attempt to route to an edge // that's disabled, then that edge is disqualified, and the routing attempt -// will fail. +// will fail. We also test that this is true only for non-local edges, as we'll +// ignore the disable flags, with the assumption that the correct bandwidth is +// found among the bandwidth hints. func TestRouteFailDisabledEdge(t *testing.T) { t.Parallel() graph, err := parseTestGraph(basicGraphFilePath) - defer graph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) } + defer graph.cleanUp() sourceNode, err := graph.graph.SourceNode() if err != nil { @@ -1418,35 +1475,208 @@ func TestRouteFailDisabledEdge(t *testing.T) { target := graph.aliasMap["sophon"] payAmt := lnwire.NewMSatFromSatoshis(105000) _, err = findPath( - nil, graph.graph, nil, sourceNode, target, ignoredVertexes, - ignoredEdges, payAmt, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, ) if err != nil { t.Fatalf("unable to find path: %v", err) } - // First, we'll modify the edge from roasbeef -> phamnuwen, to read that - // it's disabled. - _, _, phamnuwenEdge, err := graph.graph.FetchChannelEdgesByID(999991) + // Disable the edge roasbeef->phamnuwen. This should not impact the + // path finding, as we don't consider the disable flag for local + // channels (and roasbeef is the source). + roasToPham := uint64(999991) + _, e1, e2, err := graph.graph.FetchChannelEdgesByID(roasToPham) if err != nil { - t.Fatalf("unable to fetch goku's edge: %v", err) + t.Fatalf("unable to fetch edge: %v", err) } - phamnuwenEdge.Flags = lnwire.ChanUpdateDisabled | lnwire.ChanUpdateDirection - if err := graph.graph.UpdateEdgePolicy(phamnuwenEdge); err != nil { + e1.Flags |= lnwire.ChanUpdateDisabled + if err := graph.graph.UpdateEdgePolicy(e1); err != nil { + t.Fatalf("unable to update edge: %v", err) + } + e2.Flags |= lnwire.ChanUpdateDisabled + if err := graph.graph.UpdateEdgePolicy(e2); err != nil { t.Fatalf("unable to update edge: %v", err) } - // Now, if we attempt to route through that edge, we should get a - // failure as it is no longer eligible. _, err = findPath( - nil, graph.graph, nil, sourceNode, target, ignoredVertexes, - ignoredEdges, payAmt, noFeeLimit, nil, + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, + ) + if err != nil { + t.Fatalf("unable to find path: %v", err) + } + + // Now, we'll modify the edge from phamnuwen -> sophon, to read that + // it's disabled. + phamToSophon := uint64(99999) + _, e, _, err := graph.graph.FetchChannelEdgesByID(phamToSophon) + if err != nil { + t.Fatalf("unable to fetch edge: %v", err) + } + e.Flags |= lnwire.ChanUpdateDisabled + if err := graph.graph.UpdateEdgePolicy(e); err != nil { + t.Fatalf("unable to update edge: %v", err) + } + + // If we attempt to route through that edge, we should get a failure as + // it is no longer eligible. + _, err = findPath( + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, ) if !IsError(err, ErrNoPathFound) { t.Fatalf("graph shouldn't be able to support payment: %v", err) } } +// TestPathSourceEdgesBandwidth tests that explicitly passing in a set of +// bandwidth hints is used by the path finding algorithm to consider whether to +// use a local channel. +func TestPathSourceEdgesBandwidth(t *testing.T) { + t.Parallel() + + graph, err := parseTestGraph(basicGraphFilePath) + if err != nil { + t.Fatalf("unable to create graph: %v", err) + } + defer graph.cleanUp() + + sourceNode, err := graph.graph.SourceNode() + if err != nil { + t.Fatalf("unable to fetch source node: %v", err) + } + ignoredEdges := make(map[uint64]struct{}) + ignoredVertexes := make(map[Vertex]struct{}) + + // First, we'll try to route from roasbeef -> sophon. This should + // succeed without issue, and return a path via songoku, as that's the + // cheapest path. + target := graph.aliasMap["sophon"] + payAmt := lnwire.NewMSatFromSatoshis(50000) + path, err := findPath( + &graphParams{ + graph: graph.graph, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, + ) + if err != nil { + t.Fatalf("unable to find path: %v", err) + } + assertExpectedPath(t, path, "songoku", "sophon") + + // Now we'll set the bandwidth of the edge roasbeef->songoku and + // roasbeef->phamnuwen to 0. + roasToSongoku := uint64(12345) + roasToPham := uint64(999991) + bandwidths := map[uint64]lnwire.MilliSatoshi{ + roasToSongoku: 0, + roasToPham: 0, + } + + // Since both these edges has a bandwidth of zero, no path should be + // found. + _, err = findPath( + &graphParams{ + graph: graph.graph, + bandwidthHints: bandwidths, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, + ) + if !IsError(err, ErrNoPathFound) { + t.Fatalf("graph shouldn't be able to support payment: %v", err) + } + + // Set the bandwidth of roasbeef->phamnuwen high enough to carry the + // payment. + bandwidths[roasToPham] = 2 * payAmt + + // Now, if we attempt to route again, we should find the path via + // phamnuven, as the other source edge won't be considered. + path, err = findPath( + &graphParams{ + graph: graph.graph, + bandwidthHints: bandwidths, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, + ) + if err != nil { + t.Fatalf("unable to find path: %v", err) + } + assertExpectedPath(t, path, "phamnuwen", "sophon") + + // Finally, set the roasbeef->songoku bandwidth, but also set its + // disable flag. + bandwidths[roasToSongoku] = 2 * payAmt + _, e1, e2, err := graph.graph.FetchChannelEdgesByID(roasToSongoku) + if err != nil { + t.Fatalf("unable to fetch edge: %v", err) + } + e1.Flags |= lnwire.ChanUpdateDisabled + if err := graph.graph.UpdateEdgePolicy(e1); err != nil { + t.Fatalf("unable to update edge: %v", err) + } + e2.Flags |= lnwire.ChanUpdateDisabled + if err := graph.graph.UpdateEdgePolicy(e2); err != nil { + t.Fatalf("unable to update edge: %v", err) + } + + // Since we ignore disable flags for local channels, a path should + // still be found. + path, err = findPath( + &graphParams{ + graph: graph.graph, + bandwidthHints: bandwidths, + }, + &restrictParams{ + ignoredNodes: ignoredVertexes, + ignoredEdges: ignoredEdges, + feeLimit: noFeeLimit, + }, + sourceNode, target, payAmt, + ) + if err != nil { + t.Fatalf("unable to find path: %v", err) + } + assertExpectedPath(t, path, "songoku", "sophon") +} + func TestPathInsufficientCapacityWithFee(t *testing.T) { t.Parallel() @@ -1465,10 +1695,10 @@ func TestPathFindSpecExample(t *testing.T) { // height. const startingHeight = 100 ctx, cleanUp, err := createTestCtxFromFile(startingHeight, specExampleFilePath) - defer cleanUp() if err != nil { t.Fatalf("unable to create router: %v", err) } + defer cleanUp() const ( aliceFinalCLTV = 10 diff --git a/routing/router_test.go b/routing/router_test.go index 7a62754e..c4b8a0a4 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -1872,8 +1872,15 @@ func TestFindPathFeeWeighting(t *testing.T) { // the edge weighting, we should select the direct path over the 2 hop // path even though the direct path has a higher potential time lock. path, err := findPath( - nil, ctx.graph, nil, sourceNode, target, ignoreVertex, - ignoreEdge, amt, noFeeLimit, nil, + &graphParams{ + graph: ctx.graph, + }, + &restrictParams{ + ignoredNodes: ignoreVertex, + ignoredEdges: ignoreEdge, + feeLimit: noFeeLimit, + }, + sourceNode, target, amt, ) if err != nil { t.Fatalf("unable to find path: %v", err)