routing: add outgoing channel restriction

This commit is contained in:
Joost Jager committed 2019-02-08 23:05:56 +01:00
1 parent c1ab49909f
commit 7c30a8c493
9 files changed
+735 -579

No files matched your search

+14 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/btcsuite/btcd/btcec"
"github.com/coreos/bbolt"
"github.com/lightningnetwork/lightning-onion"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
)
@@ -453,6 +453,10 @@ type restrictParams struct {
// feeLimit is a maximum fee amount allowed to be used on the path from
// the source to the target.
feeLimit lnwire.MilliSatoshi
// outgoingChannelID is the channel that needs to be taken to the first
// hop. If nil, any channel may be used.
outgoingChannelID *uint64
}
// findPath attempts to find a path from the source node within the
@@ -563,6 +567,7 @@ func findPath(g *graphParams, r *restrictParams,
// TODO(halseth): also ignore disable flags for non-local
// channels if bandwidth hint is set?
isSourceChan := fromVertex == sourceVertex
edgeFlags := edge.ChannelFlags
isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0
@@ -570,6 +575,14 @@ func findPath(g *graphParams, r *restrictParams,
return
}
// If we have an outgoing channel restriction and this is not
// the specified channel, skip it.
if isSourceChan && r.outgoingChannelID != nil &&
*r.outgoingChannelID != edge.ChannelID {
return
}
// If this vertex or edge has been black listed, then we'll
// skip exploring this edge.
if _, ok := r.ignoredNodes[fromVertex]; ok {
+92
View File
@@ -1918,3 +1918,95 @@ func TestNewRouteFromEmptyHops(t *testing.T) {
t.Fatalf("expected empty hops error: instead got: %v", err)
}
}
// TestRestrictOutgoingChannel asserts that a outgoing channel restriction is
// obeyed by the path finding algorithm.
func TestRestrictOutgoingChannel(t *testing.T) {
t.Parallel()
// Set up a test graph with three possible paths from roasbeef to
// target. The path through channel 2 is the highest cost path.
testChannels := []*testChannel{
symmetricTestChannel("roasbeef", "a", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
}, 1),
symmetricTestChannel("a", "target", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
}),
symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 800,
MinHTLC: 1,
}, 2),
symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 600,
MinHTLC: 1,
}, 3),
symmetricTestChannel("b", "target", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
}),
}
testGraphInstance, err := createTestGraphFromChannels(testChannels)
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraphInstance.cleanUp()
sourceNode, err := testGraphInstance.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
sourceVertex := Vertex(sourceNode.PubKeyBytes)
ignoredEdges := make(map[edgeLocator]struct{})
ignoredVertexes := make(map[Vertex]struct{})
const (
startingHeight = 100
finalHopCLTV = 1
)
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := testGraphInstance.aliasMap["target"]
outgoingChannelID := uint64(2)
// Find the best path given the restriction to only use channel 2 as the
// outgoing channel.
path, err := findPath(
&graphParams{
graph: testGraphInstance.graph,
},
&restrictParams{
ignoredNodes: ignoredVertexes,
ignoredEdges: ignoredEdges,
feeLimit: noFeeLimit,
outgoingChannelID: &outgoingChannelID,
},
sourceNode, target, paymentAmt,
)
if err != nil {
t.Fatalf("unable to find path: %v", err)
}
route, err := newRoute(
paymentAmt, infinity, sourceVertex, path, startingHeight,
finalHopCLTV,
)
if err != nil {
t.Fatalf("unable to create path: %v", err)
}
// Assert that the route starts with channel 2, in line with the
// specified restriction.
if route.Hops[0].ChannelID != 2 {
t.Fatalf("expected route to pass through channel 2, "+
"but channel %v was selected instead", route.Hops[0].ChannelID)
}
}
+4 -3
View File
@@ -148,9 +148,10 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
bandwidthHints: p.bandwidthHints,
},
&restrictParams{
ignoredNodes: pruneView.vertexes,
ignoredEdges: pruneView.edges,
feeLimit: payment.FeeLimit,
ignoredNodes: pruneView.vertexes,
ignoredEdges: pruneView.edges,
feeLimit: payment.FeeLimit,
outgoingChannelID: payment.OutgoingChannelID,
},
p.mc.selfNode, payment.Target, payment.Amount,
)
+5 -1
View File
@@ -17,7 +17,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lightning-onion"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input"
@@ -1612,6 +1612,10 @@ type LightningPayment struct {
// destination successfully.
RouteHints [][]HopHint
// OutgoingChannelID is the channel that needs to be taken to the first
// hop. If nil, any channel may be used.
OutgoingChannelID *uint64
// TODO(roasbeef): add e2e message?
}