Merge pull request #2115 from halseth/disable-bit-decoupling
path finding: Disable bit decoupling
This commit is contained in:
commit
810afa937a
@ -406,9 +406,17 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
|||||||
// to our destination, respecting the recommendations from
|
// to our destination, respecting the recommendations from
|
||||||
// missionControl.
|
// missionControl.
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, p.mc.graph, p.additionalEdges, p.mc.selfNode,
|
&graphParams{
|
||||||
payment.Target, pruneView.vertexes, pruneView.edges,
|
graph: p.mc.graph,
|
||||||
payment.Amount, payment.FeeLimit, p.bandwidthHints,
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -446,6 +446,46 @@ func edgeWeight(lockedAmt lnwire.MilliSatoshi, fee lnwire.MilliSatoshi,
|
|||||||
return int64(fee) + timeLockPenalty
|
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
|
// 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
|
// ChannelGraph to the target node that's capable of supporting a payment of
|
||||||
// `amt` value. The current approach implemented is modified version 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
|
// destination node back to source. This is to properly accumulate fees
|
||||||
// that need to be paid along the path and accurately check the amount
|
// that need to be paid along the path and accurately check the amount
|
||||||
// to forward at every node against the available bandwidth.
|
// to forward at every node against the available bandwidth.
|
||||||
func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
func findPath(g *graphParams, r *restrictParams,
|
||||||
additionalEdges map[Vertex][]*channeldb.ChannelEdgePolicy,
|
|
||||||
sourceNode *channeldb.LightningNode, target *btcec.PublicKey,
|
sourceNode *channeldb.LightningNode, target *btcec.PublicKey,
|
||||||
ignoredNodes map[Vertex]struct{}, ignoredEdges map[uint64]struct{},
|
amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) {
|
||||||
amt lnwire.MilliSatoshi, feeLimit lnwire.MilliSatoshi,
|
|
||||||
bandwidthHints map[uint64]lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) {
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
tx := g.tx
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
tx, err = graph.Database().Begin(false)
|
tx, err = g.graph.Database().Begin(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// also returns the source node, so there is no need to add the source
|
||||||
// node explicitly.
|
// node explicitly.
|
||||||
distance := make(map[Vertex]nodeWithDist)
|
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
|
// TODO(roasbeef): with larger graph can just use disk seeks
|
||||||
// with a visited map
|
// with a visited map
|
||||||
distance[Vertex(node.PubKeyBytes)] = nodeWithDist{
|
distance[Vertex(node.PubKeyBytes)] = nodeWithDist{
|
||||||
@ -496,7 +535,7 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
|||||||
}
|
}
|
||||||
|
|
||||||
additionalEdgesWithSrc := make(map[Vertex][]*edgePolicyWithSource)
|
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
|
// We'll also include all the nodes found within the additional
|
||||||
// edges that are not known to us yet in the distance map.
|
// edges that are not known to us yet in the distance map.
|
||||||
node := &channeldb.LightningNode{PubKeyBytes: vertex}
|
node := &channeldb.LightningNode{PubKeyBytes: vertex}
|
||||||
@ -550,19 +589,24 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
|||||||
|
|
||||||
fromVertex := Vertex(fromNode.PubKeyBytes)
|
fromVertex := Vertex(fromNode.PubKeyBytes)
|
||||||
|
|
||||||
// If the edge is currently disabled, then we'll stop here, as
|
// If this is not a local channel and it is disabled, we will
|
||||||
// we shouldn't attempt to route through it.
|
// 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)
|
edgeFlags := lnwire.ChanUpdateFlag(edge.Flags)
|
||||||
if edgeFlags&lnwire.ChanUpdateDisabled != 0 {
|
isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0
|
||||||
|
|
||||||
|
if !isSourceChan && isDisabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this vertex or edge has been black listed, then we'll
|
// If this vertex or edge has been black listed, then we'll
|
||||||
// skip exploring this edge.
|
// skip exploring this edge.
|
||||||
if _, ok := ignoredNodes[fromVertex]; ok {
|
if _, ok := r.ignoredNodes[fromVertex]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, ok := ignoredEdges[edge.ChannelID]; ok {
|
if _, ok := r.ignoredEdges[edge.ChannelID]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,7 +653,7 @@ func findPath(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
|||||||
// Check if accumulated fees would exceed fee limit when this
|
// Check if accumulated fees would exceed fee limit when this
|
||||||
// node would be added to the path.
|
// node would be added to the path.
|
||||||
totalFee := amountToReceive - amt
|
totalFee := amountToReceive - amt
|
||||||
if totalFee > feeLimit {
|
if totalFee > r.feeLimit {
|
||||||
return
|
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
|
// We'll query the lower layer to see if we can obtain
|
||||||
// any more up to date information concerning the
|
// any more up to date information concerning the
|
||||||
// bandwidth of this edge.
|
// bandwidth of this edge.
|
||||||
edgeBandwidth, ok := bandwidthHints[edgeInfo.ChannelID]
|
edgeBandwidth, ok := g.bandwidthHints[edgeInfo.ChannelID]
|
||||||
if !ok {
|
if !ok {
|
||||||
// If we don't have a hint for this edge, then
|
// If we don't have a hint for this edge, then
|
||||||
// we'll just use the known Capacity as the
|
// 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.
|
// and use the payment amount as its capacity.
|
||||||
bandWidth := partialPath.amountToReceive
|
bandWidth := partialPath.amountToReceive
|
||||||
for _, reverseEdge := range additionalEdgesWithSrc[bestNode.PubKeyBytes] {
|
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")
|
"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))
|
pathEdges := make([]*channeldb.ChannelEdgePolicy, 0, len(next))
|
||||||
currentNode := sourceVertex
|
currentNode := sourceVertex
|
||||||
for currentNode != targetVertex { // TODO(roasbeef): assumes no cycles
|
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
|
// selfNode) to the target destination that's capable of carrying amt
|
||||||
// satoshis along the path before fees are calculated.
|
// satoshis along the path before fees are calculated.
|
||||||
startingPath, err := findPath(
|
startingPath, err := findPath(
|
||||||
tx, graph, nil, source, target, ignoredVertexes, ignoredEdges,
|
&graphParams{
|
||||||
amt, feeLimit, bandwidthHints,
|
tx: tx,
|
||||||
|
graph: graph,
|
||||||
|
bandwidthHints: bandwidthHints,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: feeLimit,
|
||||||
|
},
|
||||||
|
source, target, amt,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to find path: %v", err)
|
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
|
// root path removed, we'll attempt to find another
|
||||||
// shortest path from the spur node to the destination.
|
// shortest path from the spur node to the destination.
|
||||||
spurPath, err := findPath(
|
spurPath, err := findPath(
|
||||||
tx, graph, nil, spurNode, target,
|
&graphParams{
|
||||||
ignoredVertexes, ignoredEdges, amt, feeLimit,
|
tx: tx,
|
||||||
bandwidthHints,
|
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
|
// If we weren't able to find a path, we'll continue to
|
||||||
|
@ -556,10 +556,10 @@ func TestFindLowestFeePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testGraphInstance, err := createTestGraphFromChannels(testChannels)
|
testGraphInstance, err := createTestGraphFromChannels(testChannels)
|
||||||
defer testGraphInstance.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer testGraphInstance.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := testGraphInstance.graph.SourceNode()
|
sourceNode, err := testGraphInstance.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -578,8 +578,15 @@ func TestFindLowestFeePath(t *testing.T) {
|
|||||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||||
target := testGraphInstance.aliasMap["target"]
|
target := testGraphInstance.aliasMap["target"]
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, testGraphInstance.graph, nil, sourceNode, target,
|
&graphParams{
|
||||||
ignoredVertexes, ignoredEdges, paymentAmt, noFeeLimit, nil,
|
graph: testGraphInstance.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, paymentAmt,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find path: %v", err)
|
t.Fatalf("unable to find path: %v", err)
|
||||||
@ -676,10 +683,10 @@ func TestBasicGraphPathFinding(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
testGraphInstance, err := parseTestGraph(basicGraphFilePath)
|
testGraphInstance, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer testGraphInstance.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer testGraphInstance.cleanUp()
|
||||||
|
|
||||||
// With the test graph loaded, we'll test some basic path finding using
|
// With the test graph loaded, we'll test some basic path finding using
|
||||||
// the pre-generated graph. Consult the testdata/basic_graph.json file
|
// 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)
|
paymentAmt := lnwire.NewMSatFromSatoshis(test.paymentAmt)
|
||||||
target := graphInstance.aliasMap[test.target]
|
target := graphInstance.aliasMap[test.target]
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, graphInstance.graph, nil, sourceNode, target,
|
&graphParams{
|
||||||
ignoredVertexes, ignoredEdges, paymentAmt, test.feeLimit, nil,
|
graph: graphInstance.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: test.feeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, paymentAmt,
|
||||||
)
|
)
|
||||||
if test.expectFailureNoPath {
|
if test.expectFailureNoPath {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -858,10 +872,10 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer graph.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer graph.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := graph.graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
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.
|
// We should now be able to find a path from roasbeef to doge.
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, graph.graph, additionalEdges, sourceNode, dogePubKey, nil, nil,
|
&graphParams{
|
||||||
paymentAmt, noFeeLimit, nil,
|
graph: graph.graph,
|
||||||
|
additionalEdges: additionalEdges,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, dogePubKey, paymentAmt,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find private path to doge: %v", err)
|
t.Fatalf("unable to find private path to doge: %v", err)
|
||||||
@ -921,10 +941,10 @@ func TestKShortestPathFinding(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer graph.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer graph.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := graph.graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1246,10 +1266,10 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
// Ensure that potential paths which are over the maximum hop-limit are
|
// Ensure that potential paths which are over the maximum hop-limit are
|
||||||
// rejected.
|
// rejected.
|
||||||
graph, err := parseTestGraph(excessiveHopsGraphFilePath)
|
graph, err := parseTestGraph(excessiveHopsGraphFilePath)
|
||||||
defer graph.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer graph.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := graph.graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1261,12 +1281,19 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
|
|
||||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||||
|
|
||||||
// We start by confirming that routing a payment 20 hops away is possible.
|
// We start by confirming that routing a payment 20 hops away is
|
||||||
// Alice should be able to find a valid route to ursula.
|
// possible. Alice should be able to find a valid route to ursula.
|
||||||
target := graph.aliasMap["ursula"]
|
target := graph.aliasMap["ursula"]
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
&graphParams{
|
||||||
ignoredEdges, paymentAmt, noFeeLimit, nil,
|
graph: graph.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, paymentAmt,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("path should have been found")
|
t.Fatalf("path should have been found")
|
||||||
@ -1276,8 +1303,15 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
// presented to Alice.
|
// presented to Alice.
|
||||||
target = graph.aliasMap["vincent"]
|
target = graph.aliasMap["vincent"]
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
&graphParams{
|
||||||
ignoredEdges, paymentAmt, noFeeLimit, nil,
|
graph: graph.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, paymentAmt,
|
||||||
)
|
)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("should not have been able to find path, supposed to be "+
|
t.Fatalf("should not have been able to find path, supposed to be "+
|
||||||
@ -1291,10 +1325,10 @@ func TestPathNotAvailable(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer graph.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer graph.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := graph.graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1318,8 +1352,15 @@ func TestPathNotAvailable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph.graph, nil, sourceNode, unknownNode, ignoredVertexes,
|
&graphParams{
|
||||||
ignoredEdges, 100, noFeeLimit, nil,
|
graph: graph.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, unknownNode, 100,
|
||||||
)
|
)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
t.Fatalf("path shouldn't have been found: %v", err)
|
t.Fatalf("path shouldn't have been found: %v", err)
|
||||||
@ -1330,10 +1371,10 @@ func TestPathInsufficientCapacity(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer graph.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer graph.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := graph.graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1354,8 +1395,15 @@ func TestPathInsufficientCapacity(t *testing.T) {
|
|||||||
|
|
||||||
payAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
|
payAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
&graphParams{
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
graph: graph.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, payAmt,
|
||||||
)
|
)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
||||||
@ -1368,10 +1416,10 @@ func TestRouteFailMinHTLC(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer graph.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer graph.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := graph.graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1386,8 +1434,15 @@ func TestRouteFailMinHTLC(t *testing.T) {
|
|||||||
target := graph.aliasMap["songoku"]
|
target := graph.aliasMap["songoku"]
|
||||||
payAmt := lnwire.MilliSatoshi(10)
|
payAmt := lnwire.MilliSatoshi(10)
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
&graphParams{
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
graph: graph.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, payAmt,
|
||||||
)
|
)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
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
|
// TestRouteFailDisabledEdge tests that if we attempt to route to an edge
|
||||||
// that's disabled, then that edge is disqualified, and the routing attempt
|
// 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) {
|
func TestRouteFailDisabledEdge(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
graph, err := parseTestGraph(basicGraphFilePath)
|
graph, err := parseTestGraph(basicGraphFilePath)
|
||||||
defer graph.cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create graph: %v", err)
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
}
|
}
|
||||||
|
defer graph.cleanUp()
|
||||||
|
|
||||||
sourceNode, err := graph.graph.SourceNode()
|
sourceNode, err := graph.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1418,35 +1475,208 @@ func TestRouteFailDisabledEdge(t *testing.T) {
|
|||||||
target := graph.aliasMap["sophon"]
|
target := graph.aliasMap["sophon"]
|
||||||
payAmt := lnwire.NewMSatFromSatoshis(105000)
|
payAmt := lnwire.NewMSatFromSatoshis(105000)
|
||||||
_, err = findPath(
|
_, err = findPath(
|
||||||
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
&graphParams{
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
graph: graph.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoredVertexes,
|
||||||
|
ignoredEdges: ignoredEdges,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, payAmt,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find path: %v", err)
|
t.Fatalf("unable to find path: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, we'll modify the edge from roasbeef -> phamnuwen, to read that
|
// Disable the edge roasbeef->phamnuwen. This should not impact the
|
||||||
// it's disabled.
|
// path finding, as we don't consider the disable flag for local
|
||||||
_, _, phamnuwenEdge, err := graph.graph.FetchChannelEdgesByID(999991)
|
// channels (and roasbeef is the source).
|
||||||
|
roasToPham := uint64(999991)
|
||||||
|
_, e1, e2, err := graph.graph.FetchChannelEdgesByID(roasToPham)
|
||||||
if err != nil {
|
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
|
e1.Flags |= lnwire.ChanUpdateDisabled
|
||||||
if err := graph.graph.UpdateEdgePolicy(phamnuwenEdge); err != nil {
|
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)
|
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(
|
_, err = findPath(
|
||||||
nil, graph.graph, nil, sourceNode, target, ignoredVertexes,
|
&graphParams{
|
||||||
ignoredEdges, payAmt, noFeeLimit, nil,
|
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) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
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) {
|
func TestPathInsufficientCapacityWithFee(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -1465,10 +1695,10 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||||||
// height.
|
// height.
|
||||||
const startingHeight = 100
|
const startingHeight = 100
|
||||||
ctx, cleanUp, err := createTestCtxFromFile(startingHeight, specExampleFilePath)
|
ctx, cleanUp, err := createTestCtxFromFile(startingHeight, specExampleFilePath)
|
||||||
defer cleanUp()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
}
|
}
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
const (
|
const (
|
||||||
aliceFinalCLTV = 10
|
aliceFinalCLTV = 10
|
||||||
|
@ -1872,8 +1872,15 @@ func TestFindPathFeeWeighting(t *testing.T) {
|
|||||||
// the edge weighting, we should select the direct path over the 2 hop
|
// 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 even though the direct path has a higher potential time lock.
|
||||||
path, err := findPath(
|
path, err := findPath(
|
||||||
nil, ctx.graph, nil, sourceNode, target, ignoreVertex,
|
&graphParams{
|
||||||
ignoreEdge, amt, noFeeLimit, nil,
|
graph: ctx.graph,
|
||||||
|
},
|
||||||
|
&restrictParams{
|
||||||
|
ignoredNodes: ignoreVertex,
|
||||||
|
ignoredEdges: ignoreEdge,
|
||||||
|
feeLimit: noFeeLimit,
|
||||||
|
},
|
||||||
|
sourceNode, target, amt,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find path: %v", err)
|
t.Fatalf("unable to find path: %v", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user