Merge pull request #2243 from joostjager/prune-single-direction
routing: prune single direction
This commit is contained in:
commit
45f49899ae
@ -47,7 +47,7 @@ type missionControl struct {
|
|||||||
// it was added to the prune view. Edges are added to this map if a
|
// it was added to the prune view. Edges are added to this map if a
|
||||||
// caller reports to missionControl a failure localized to that edge
|
// caller reports to missionControl a failure localized to that edge
|
||||||
// when sending a payment.
|
// when sending a payment.
|
||||||
failedEdges map[uint64]time.Time
|
failedEdges map[edgeLocator]time.Time
|
||||||
|
|
||||||
// failedVertexes maps a node's public key that should be pruned, to
|
// failedVertexes maps a node's public key that should be pruned, to
|
||||||
// the time that it was added to the prune view. Vertexes are added to
|
// the time that it was added to the prune view. Vertexes are added to
|
||||||
@ -76,7 +76,7 @@ func newMissionControl(g *channeldb.ChannelGraph, selfNode *channeldb.LightningN
|
|||||||
qb func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi) *missionControl {
|
qb func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi) *missionControl {
|
||||||
|
|
||||||
return &missionControl{
|
return &missionControl{
|
||||||
failedEdges: make(map[uint64]time.Time),
|
failedEdges: make(map[edgeLocator]time.Time),
|
||||||
failedVertexes: make(map[Vertex]time.Time),
|
failedVertexes: make(map[Vertex]time.Time),
|
||||||
selfNode: selfNode,
|
selfNode: selfNode,
|
||||||
queryBandwidth: qb,
|
queryBandwidth: qb,
|
||||||
@ -90,7 +90,7 @@ func newMissionControl(g *channeldb.ChannelGraph, selfNode *channeldb.LightningN
|
|||||||
// state of the wider network from the PoV of mission control compiled via HTLC
|
// state of the wider network from the PoV of mission control compiled via HTLC
|
||||||
// routing attempts in the past.
|
// routing attempts in the past.
|
||||||
type graphPruneView struct {
|
type graphPruneView struct {
|
||||||
edges map[uint64]struct{}
|
edges map[edgeLocator]struct{}
|
||||||
|
|
||||||
vertexes map[Vertex]struct{}
|
vertexes map[Vertex]struct{}
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ func (m *missionControl) GraphPruneView() graphPruneView {
|
|||||||
|
|
||||||
// We'll also do the same for edges, but use the edgeDecay this time
|
// We'll also do the same for edges, but use the edgeDecay this time
|
||||||
// rather than the decay for vertexes.
|
// rather than the decay for vertexes.
|
||||||
edges := make(map[uint64]struct{})
|
edges := make(map[edgeLocator]struct{})
|
||||||
for edge, pruneTime := range m.failedEdges {
|
for edge, pruneTime := range m.failedEdges {
|
||||||
if now.Sub(pruneTime) >= edgeDecay {
|
if now.Sub(pruneTime) >= edgeDecay {
|
||||||
log.Tracef("Pruning decayed failure report for edge %v "+
|
log.Tracef("Pruning decayed failure report for edge %v "+
|
||||||
@ -164,11 +164,11 @@ type paymentSession struct {
|
|||||||
|
|
||||||
bandwidthHints map[uint64]lnwire.MilliSatoshi
|
bandwidthHints map[uint64]lnwire.MilliSatoshi
|
||||||
|
|
||||||
// errFailedFeeChans is a map of the short channel ID's that were the
|
// errFailedFeeChans is a map of the short channel IDs that were the
|
||||||
// source of policy related routing failures during this payment attempt.
|
// source of policy related routing failures during this payment attempt.
|
||||||
// We'll use this map to prune out channels when the first error may not
|
// We'll use this map to prune out channels when the first error may not
|
||||||
// require pruning, but any subsequent ones do.
|
// require pruning, but any subsequent ones do.
|
||||||
errFailedPolicyChans map[uint64]struct{}
|
errFailedPolicyChans map[edgeLocator]struct{}
|
||||||
|
|
||||||
mc *missionControl
|
mc *missionControl
|
||||||
|
|
||||||
@ -245,7 +245,7 @@ func (m *missionControl) NewPaymentSession(routeHints [][]HopHint,
|
|||||||
pruneViewSnapshot: viewSnapshot,
|
pruneViewSnapshot: viewSnapshot,
|
||||||
additionalEdges: edges,
|
additionalEdges: edges,
|
||||||
bandwidthHints: bandwidthHints,
|
bandwidthHints: bandwidthHints,
|
||||||
errFailedPolicyChans: make(map[uint64]struct{}),
|
errFailedPolicyChans: make(map[edgeLocator]struct{}),
|
||||||
mc: m,
|
mc: m,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -259,7 +259,7 @@ func (m *missionControl) NewPaymentSessionFromRoutes(routes []*Route) *paymentSe
|
|||||||
pruneViewSnapshot: m.GraphPruneView(),
|
pruneViewSnapshot: m.GraphPruneView(),
|
||||||
haveRoutes: true,
|
haveRoutes: true,
|
||||||
preBuiltRoutes: routes,
|
preBuiltRoutes: routes,
|
||||||
errFailedPolicyChans: make(map[uint64]struct{}),
|
errFailedPolicyChans: make(map[edgeLocator]struct{}),
|
||||||
mc: m,
|
mc: m,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,17 +325,17 @@ func (p *paymentSession) ReportVertexFailure(v Vertex) {
|
|||||||
// retrying an edge after its pruning has expired.
|
// retrying an edge after its pruning has expired.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): also add value attempted to send and capacity of channel
|
// TODO(roasbeef): also add value attempted to send and capacity of channel
|
||||||
func (p *paymentSession) ReportChannelFailure(e uint64) {
|
func (p *paymentSession) ReportEdgeFailure(e *edgeLocator) {
|
||||||
log.Debugf("Reporting edge %v failure to Mission Control", e)
|
log.Debugf("Reporting edge %v failure to Mission Control", e)
|
||||||
|
|
||||||
// First, we'll add the failed edge to our local prune view snapshot.
|
// First, we'll add the failed edge to our local prune view snapshot.
|
||||||
p.pruneViewSnapshot.edges[e] = struct{}{}
|
p.pruneViewSnapshot.edges[*e] = struct{}{}
|
||||||
|
|
||||||
// With the edge added, we'll now report back to the global prune view,
|
// With the edge added, we'll now report back to the global prune view,
|
||||||
// with this new piece of information so it can be utilized for new
|
// with this new piece of information so it can be utilized for new
|
||||||
// payment sessions.
|
// payment sessions.
|
||||||
p.mc.Lock()
|
p.mc.Lock()
|
||||||
p.mc.failedEdges[e] = time.Now()
|
p.mc.failedEdges[*e] = time.Now()
|
||||||
p.mc.Unlock()
|
p.mc.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,12 +345,12 @@ func (p *paymentSession) ReportChannelFailure(e uint64) {
|
|||||||
// edge as 'policy failed once'. The next time it fails, the whole node will be
|
// edge as 'policy failed once'. The next time it fails, the whole node will be
|
||||||
// pruned. This is to prevent nodes from keeping us busy by continuously sending
|
// pruned. This is to prevent nodes from keeping us busy by continuously sending
|
||||||
// new channel updates.
|
// new channel updates.
|
||||||
func (p *paymentSession) ReportChannelPolicyFailure(
|
func (p *paymentSession) ReportEdgePolicyFailure(
|
||||||
errSource Vertex, failedChanID uint64) {
|
errSource Vertex, failedEdge *edgeLocator) {
|
||||||
|
|
||||||
// Check to see if we've already reported a policy related failure for
|
// Check to see if we've already reported a policy related failure for
|
||||||
// this channel. If so, then we'll prune out the vertex.
|
// this channel. If so, then we'll prune out the vertex.
|
||||||
_, ok := p.errFailedPolicyChans[failedChanID]
|
_, ok := p.errFailedPolicyChans[*failedEdge]
|
||||||
if ok {
|
if ok {
|
||||||
// TODO(joostjager): is this aggresive pruning still necessary?
|
// TODO(joostjager): is this aggresive pruning still necessary?
|
||||||
// Just pruning edges may also work unless there is a huge
|
// Just pruning edges may also work unless there is a huge
|
||||||
@ -361,7 +361,7 @@ func (p *paymentSession) ReportChannelPolicyFailure(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we'll record a policy failure from this node and move on.
|
// Finally, we'll record a policy failure from this node and move on.
|
||||||
p.errFailedPolicyChans[failedChanID] = struct{}{}
|
p.errFailedPolicyChans[*failedEdge] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestRoute returns a route which is likely to be capable for successfully
|
// RequestRoute returns a route which is likely to be capable for successfully
|
||||||
@ -442,7 +442,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
|||||||
// if no payment attempts have been made.
|
// if no payment attempts have been made.
|
||||||
func (m *missionControl) ResetHistory() {
|
func (m *missionControl) ResetHistory() {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
m.failedEdges = make(map[uint64]time.Time)
|
m.failedEdges = make(map[edgeLocator]time.Time)
|
||||||
m.failedVertexes = make(map[Vertex]time.Time)
|
m.failedVertexes = make(map[Vertex]time.Time)
|
||||||
m.Unlock()
|
m.Unlock()
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,10 @@ type Route struct {
|
|||||||
// amount of fees.
|
// amount of fees.
|
||||||
TotalAmount lnwire.MilliSatoshi
|
TotalAmount lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// SourcePubKey is the pubkey of the node where this route originates
|
||||||
|
// from.
|
||||||
|
SourcePubKey Vertex
|
||||||
|
|
||||||
// Hops contains details concerning the specific forwarding details at
|
// Hops contains details concerning the specific forwarding details at
|
||||||
// each hop.
|
// each hop.
|
||||||
Hops []*Hop
|
Hops []*Hop
|
||||||
@ -158,16 +162,6 @@ type Route struct {
|
|||||||
// is present in this route or not. Channels are identified by the
|
// is present in this route or not. Channels are identified by the
|
||||||
// uint64 version of the short channel ID.
|
// uint64 version of the short channel ID.
|
||||||
chanIndex map[uint64]struct{}
|
chanIndex map[uint64]struct{}
|
||||||
|
|
||||||
// nextHop maps a node, to the next channel that it will pass the HTLC
|
|
||||||
// off to. With this map, we can easily look up the next outgoing
|
|
||||||
// channel or node for pruning purposes.
|
|
||||||
nextHopMap map[Vertex]*Hop
|
|
||||||
|
|
||||||
// prevHop maps a node, to the channel that was directly before it
|
|
||||||
// within the route. With this map, we can easily look up the previous
|
|
||||||
// channel or node for pruning purposes.
|
|
||||||
prevHopMap map[Vertex]*Hop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HopFee returns the fee charged by the route hop indicated by hopIndex.
|
// HopFee returns the fee charged by the route hop indicated by hopIndex.
|
||||||
@ -183,29 +177,6 @@ func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
|
|||||||
return incomingAmt - r.Hops[hopIndex].AmtToForward
|
return incomingAmt - r.Hops[hopIndex].AmtToForward
|
||||||
}
|
}
|
||||||
|
|
||||||
// nextHopVertex returns the next hop (by Vertex) after the target node. If the
|
|
||||||
// target node is not found in the route, then false is returned.
|
|
||||||
func (r *Route) nextHopVertex(n *btcec.PublicKey) (Vertex, bool) {
|
|
||||||
hop, ok := r.nextHopMap[NewVertex(n)]
|
|
||||||
return Vertex(hop.PubKeyBytes), ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextHopChannel returns the uint64 channel ID of the next hop after the
|
|
||||||
// target node. If the target node is not found in the route, then false is
|
|
||||||
// returned.
|
|
||||||
func (r *Route) nextHopChannel(n *btcec.PublicKey) (*Hop, bool) {
|
|
||||||
hop, ok := r.nextHopMap[NewVertex(n)]
|
|
||||||
return hop, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevHopChannel returns the uint64 channel ID of the before hop after the
|
|
||||||
// target node. If the target node is not found in the route, then false is
|
|
||||||
// returned.
|
|
||||||
func (r *Route) prevHopChannel(n *btcec.PublicKey) (*Hop, bool) {
|
|
||||||
hop, ok := r.prevHopMap[NewVertex(n)]
|
|
||||||
return hop, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// containsNode returns true if a node is present in the target route, and
|
// containsNode returns true if a node is present in the target route, and
|
||||||
// false otherwise.
|
// false otherwise.
|
||||||
func (r *Route) containsNode(v Vertex) bool {
|
func (r *Route) containsNode(v Vertex) bool {
|
||||||
@ -381,31 +352,21 @@ func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
|
|||||||
// that is send from the source and the final amount that is received
|
// that is send from the source and the final amount that is received
|
||||||
// by the destination.
|
// by the destination.
|
||||||
route := &Route{
|
route := &Route{
|
||||||
|
SourcePubKey: sourceVertex,
|
||||||
Hops: hops,
|
Hops: hops,
|
||||||
TotalTimeLock: timeLock,
|
TotalTimeLock: timeLock,
|
||||||
TotalAmount: amtToSend,
|
TotalAmount: amtToSend,
|
||||||
TotalFees: amtToSend - hops[len(hops)-1].AmtToForward,
|
TotalFees: amtToSend - hops[len(hops)-1].AmtToForward,
|
||||||
nodeIndex: make(map[Vertex]struct{}),
|
nodeIndex: make(map[Vertex]struct{}),
|
||||||
chanIndex: make(map[uint64]struct{}),
|
chanIndex: make(map[uint64]struct{}),
|
||||||
nextHopMap: make(map[Vertex]*Hop),
|
|
||||||
prevHopMap: make(map[Vertex]*Hop),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then we'll update the node and channel index, to indicate that this
|
// Then we'll update the node and channel index, to indicate that this
|
||||||
// Vertex and incoming channel link are present within this route.
|
// Vertex and incoming channel link are present within this route.
|
||||||
// Also, the prev and next hop maps will be populated.
|
for _, hop := range hops {
|
||||||
prevNode := sourceVertex
|
|
||||||
for i := 0; i < len(hops); i++ {
|
|
||||||
hop := hops[i]
|
|
||||||
|
|
||||||
v := Vertex(hop.PubKeyBytes)
|
v := Vertex(hop.PubKeyBytes)
|
||||||
|
|
||||||
route.nodeIndex[v] = struct{}{}
|
route.nodeIndex[v] = struct{}{}
|
||||||
route.chanIndex[hop.ChannelID] = struct{}{}
|
route.chanIndex[hop.ChannelID] = struct{}{}
|
||||||
route.prevHopMap[v] = hop
|
|
||||||
route.nextHopMap[prevNode] = hop
|
|
||||||
|
|
||||||
prevNode = v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return route
|
return route
|
||||||
@ -479,7 +440,7 @@ type restrictParams struct {
|
|||||||
|
|
||||||
// ignoredEdges is an optional set of edges that should be ignored if
|
// ignoredEdges is an optional set of edges that should be ignored if
|
||||||
// encountered during path finding.
|
// encountered during path finding.
|
||||||
ignoredEdges map[uint64]struct{}
|
ignoredEdges map[edgeLocator]struct{}
|
||||||
|
|
||||||
// feeLimit is a maximum fee amount allowed to be used on the path from
|
// feeLimit is a maximum fee amount allowed to be used on the path from
|
||||||
// the source to the target.
|
// the source to the target.
|
||||||
@ -606,7 +567,9 @@ func findPath(g *graphParams, r *restrictParams,
|
|||||||
if _, ok := r.ignoredNodes[fromVertex]; ok {
|
if _, ok := r.ignoredNodes[fromVertex]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, ok := r.ignoredEdges[edge.ChannelID]; ok {
|
|
||||||
|
locator := newEdgeLocator(edge)
|
||||||
|
if _, ok := r.ignoredEdges[*locator]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,7 +797,7 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
|||||||
amt lnwire.MilliSatoshi, feeLimit lnwire.MilliSatoshi, numPaths uint32,
|
amt lnwire.MilliSatoshi, feeLimit lnwire.MilliSatoshi, numPaths uint32,
|
||||||
bandwidthHints map[uint64]lnwire.MilliSatoshi) ([][]*channeldb.ChannelEdgePolicy, error) {
|
bandwidthHints map[uint64]lnwire.MilliSatoshi) ([][]*channeldb.ChannelEdgePolicy, error) {
|
||||||
|
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
// TODO(roasbeef): modifying ordering within heap to eliminate final
|
// TODO(roasbeef): modifying ordering within heap to eliminate final
|
||||||
@ -889,7 +852,7 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
|||||||
// we'll exclude from the next path finding attempt.
|
// we'll exclude from the next path finding attempt.
|
||||||
// These are required to ensure the paths are unique
|
// These are required to ensure the paths are unique
|
||||||
// and loopless.
|
// and loopless.
|
||||||
ignoredEdges = make(map[uint64]struct{})
|
ignoredEdges = make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes = make(map[Vertex]struct{})
|
ignoredVertexes = make(map[Vertex]struct{})
|
||||||
|
|
||||||
// Our spur node is the i-th node in the prior shortest
|
// Our spur node is the i-th node in the prior shortest
|
||||||
@ -907,8 +870,11 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
|
|||||||
// shortest path, then we'll remove the edge
|
// shortest path, then we'll remove the edge
|
||||||
// directly _after_ our spur node from the
|
// directly _after_ our spur node from the
|
||||||
// graph so we don't repeat paths.
|
// graph so we don't repeat paths.
|
||||||
if len(path) > i+1 && isSamePath(rootPath, path[:i+1]) {
|
if len(path) > i+1 &&
|
||||||
ignoredEdges[path[i+1].ChannelID] = struct{}{}
|
isSamePath(rootPath, path[:i+1]) {
|
||||||
|
|
||||||
|
locator := newEdgeLocator(path[i+1])
|
||||||
|
ignoredEdges[*locator] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -232,6 +233,13 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(node1Bytes, node2Bytes) == 1 {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"channel %v node order incorrect",
|
||||||
|
edge.ChannelID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fundingTXID := strings.Split(edge.ChannelPoint, ":")[0]
|
fundingTXID := strings.Split(edge.ChannelPoint, ":")[0]
|
||||||
txidBytes, err := chainhash.NewHashFromStr(fundingTXID)
|
txidBytes, err := chainhash.NewHashFromStr(fundingTXID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -567,7 +575,7 @@ func TestFindLowestFeePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
sourceVertex := Vertex(sourceNode.PubKeyBytes)
|
sourceVertex := Vertex(sourceNode.PubKeyBytes)
|
||||||
|
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -713,7 +721,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
|||||||
}
|
}
|
||||||
sourceVertex := Vertex(sourceNode.PubKeyBytes)
|
sourceVertex := Vertex(sourceNode.PubKeyBytes)
|
||||||
|
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -837,35 +845,6 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
|||||||
t.Fatalf("expected time lock of %v, instead have %v", 2,
|
t.Fatalf("expected time lock of %v, instead have %v", 2,
|
||||||
route.TotalTimeLock)
|
route.TotalTimeLock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The next and prev hop maps should be properly set.
|
|
||||||
for i := 0; i < expectedHopCount; i++ {
|
|
||||||
prevChan, ok := route.prevHopChannel(aliases[expectedHops[i].alias])
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("hop didn't have prev chan but should have")
|
|
||||||
}
|
|
||||||
if prevChan.ChannelID != route.Hops[i].ChannelID {
|
|
||||||
t.Fatalf("incorrect prev chan: expected %v, got %v",
|
|
||||||
prevChan.ChannelID, route.Hops[i].ChannelID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < expectedHopCount-1; i++ {
|
|
||||||
nextChan, ok := route.nextHopChannel(aliases[expectedHops[i].alias])
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("hop didn't have prev chan but should have")
|
|
||||||
}
|
|
||||||
if nextChan.ChannelID != route.Hops[i+1].ChannelID {
|
|
||||||
t.Fatalf("incorrect prev chan: expected %v, got %v",
|
|
||||||
nextChan.ChannelID, route.Hops[i+1].ChannelID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final hop shouldn't have a next chan
|
|
||||||
if _, ok := route.nextHopChannel(aliases[expectedHops[lastHopIndex].alias]); ok {
|
|
||||||
t.Fatalf("incorrect next hop map, no vertexes should " +
|
|
||||||
"be after sophon")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathFindingWithAdditionalEdges(t *testing.T) {
|
func TestPathFindingWithAdditionalEdges(t *testing.T) {
|
||||||
@ -1276,7 +1255,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||||
@ -1335,7 +1314,7 @@ func TestPathNotAvailable(t *testing.T) {
|
|||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
// With the test graph loaded, we'll test that queries for target that
|
// With the test graph loaded, we'll test that queries for target that
|
||||||
@ -1380,7 +1359,7 @@ func TestPathInsufficientCapacity(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
// Next, test that attempting to find a path in which the current
|
// Next, test that attempting to find a path in which the current
|
||||||
@ -1425,7 +1404,7 @@ func TestRouteFailMinHTLC(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
// We'll not attempt to route an HTLC of 10 SAT from roasbeef to Son
|
// We'll not attempt to route an HTLC of 10 SAT from roasbeef to Son
|
||||||
@ -1467,7 +1446,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
// First, we'll try to route from roasbeef -> sophon. This should
|
// First, we'll try to route from roasbeef -> sophon. This should
|
||||||
@ -1567,7 +1546,7 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
}
|
}
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[edgeLocator]struct{})
|
||||||
ignoredVertexes := make(map[Vertex]struct{})
|
ignoredVertexes := make(map[Vertex]struct{})
|
||||||
|
|
||||||
// First, we'll try to route from roasbeef -> sophon. This should
|
// First, we'll try to route from roasbeef -> sophon. This should
|
||||||
|
@ -213,6 +213,45 @@ func newRouteTuple(amt lnwire.MilliSatoshi, dest []byte) routeTuple {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// edgeLocator is a struct used to identify a specific edge. The direction
|
||||||
|
// fields takes the value of 0 or 1 and is identical in definition to the
|
||||||
|
// channel direction flag. A value of 0 means the direction from the lower node
|
||||||
|
// pubkey to the higher.
|
||||||
|
type edgeLocator struct {
|
||||||
|
channelID uint64
|
||||||
|
direction uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEdgeLocatorByPubkeys returns an edgeLocator based on its end point
|
||||||
|
// pubkeys.
|
||||||
|
func newEdgeLocatorByPubkeys(channelID uint64, fromNode, toNode *Vertex) *edgeLocator {
|
||||||
|
// Determine direction based on lexicographical ordering of both
|
||||||
|
// pubkeys.
|
||||||
|
var direction uint8
|
||||||
|
if bytes.Compare(fromNode[:], toNode[:]) == 1 {
|
||||||
|
direction = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return &edgeLocator{
|
||||||
|
channelID: channelID,
|
||||||
|
direction: direction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEdgeLocator extracts an edgeLocator based for a full edge policy
|
||||||
|
// structure.
|
||||||
|
func newEdgeLocator(edge *channeldb.ChannelEdgePolicy) *edgeLocator {
|
||||||
|
return &edgeLocator{
|
||||||
|
channelID: edge.ChannelID,
|
||||||
|
direction: uint8(edge.Flags & lnwire.ChanUpdateDirection),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human readable version of the edgeLocator values.
|
||||||
|
func (e *edgeLocator) String() string {
|
||||||
|
return fmt.Sprintf("%v:%v", e.channelID, e.direction)
|
||||||
|
}
|
||||||
|
|
||||||
// ChannelRouter is the layer 3 router within the Lightning stack. Below the
|
// ChannelRouter is the layer 3 router within the Lightning stack. Below the
|
||||||
// ChannelRouter is the HtlcSwitch, and below that is the Bitcoin blockchain
|
// ChannelRouter is the HtlcSwitch, and below that is the Bitcoin blockchain
|
||||||
// itself. The primary role of the ChannelRouter is to respond to queries for
|
// itself. The primary role of the ChannelRouter is to respond to queries for
|
||||||
@ -1755,12 +1794,11 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
errVertex := NewVertex(errSource)
|
errVertex := NewVertex(errSource)
|
||||||
|
|
||||||
log.Tracef("node=%x reported failure when sending "+
|
log.Tracef("node=%x reported failure when sending "+
|
||||||
"htlc=%x", errSource.SerializeCompressed(),
|
"htlc=%x", errVertex, payment.PaymentHash[:])
|
||||||
payment.PaymentHash[:])
|
|
||||||
|
|
||||||
// Always determine chan id ourselves, because a channel
|
// Always determine chan id ourselves, because a channel
|
||||||
// update with id may not be available.
|
// update with id may not be available.
|
||||||
failedChanID, err := getFailedChannelID(route, errSource)
|
failedEdge, err := getFailedEdge(route, errVertex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return preImage, nil, err
|
return preImage, nil, err
|
||||||
}
|
}
|
||||||
@ -1791,13 +1829,13 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// Or is there a valid reason for the channel
|
// Or is there a valid reason for the channel
|
||||||
// update to fail?
|
// update to fail?
|
||||||
if !updateOk {
|
if !updateOk {
|
||||||
paySession.ReportChannelFailure(
|
paySession.ReportEdgeFailure(
|
||||||
failedChanID,
|
failedEdge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
paySession.ReportChannelPolicyFailure(
|
paySession.ReportEdgePolicyFailure(
|
||||||
NewVertex(errSource), failedChanID,
|
NewVertex(errSource), failedEdge,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1887,7 +1925,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// the update and continue.
|
// the update and continue.
|
||||||
case *lnwire.FailChannelDisabled:
|
case *lnwire.FailChannelDisabled:
|
||||||
r.applyChannelUpdate(&onionErr.Update, errSource)
|
r.applyChannelUpdate(&onionErr.Update, errSource)
|
||||||
paySession.ReportChannelFailure(failedChanID)
|
paySession.ReportEdgeFailure(failedEdge)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
// It's likely that the outgoing channel didn't have
|
// It's likely that the outgoing channel didn't have
|
||||||
@ -1895,7 +1933,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// now, and continue onwards with our path finding.
|
// now, and continue onwards with our path finding.
|
||||||
case *lnwire.FailTemporaryChannelFailure:
|
case *lnwire.FailTemporaryChannelFailure:
|
||||||
r.applyChannelUpdate(onionErr.Update, errSource)
|
r.applyChannelUpdate(onionErr.Update, errSource)
|
||||||
paySession.ReportChannelFailure(failedChanID)
|
paySession.ReportEdgeFailure(failedEdge)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
// If the send fail due to a node not having the
|
// If the send fail due to a node not having the
|
||||||
@ -1920,7 +1958,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
// returning errors in order to attempt to black list
|
// returning errors in order to attempt to black list
|
||||||
// another node.
|
// another node.
|
||||||
case *lnwire.FailUnknownNextPeer:
|
case *lnwire.FailUnknownNextPeer:
|
||||||
paySession.ReportChannelFailure(failedChanID)
|
paySession.ReportEdgeFailure(failedEdge)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
// If the node wasn't able to forward for which ever
|
// If the node wasn't able to forward for which ever
|
||||||
@ -1948,10 +1986,17 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
// If we get a permanent channel or node failure, then
|
// If we get a permanent channel or node failure, then
|
||||||
// we'll note this (exclude the vertex/edge), and
|
// we'll prune the channel in both directions and
|
||||||
// continue with the rest of the routes.
|
// continue with the rest of the routes.
|
||||||
case *lnwire.FailPermanentChannelFailure:
|
case *lnwire.FailPermanentChannelFailure:
|
||||||
paySession.ReportChannelFailure(failedChanID)
|
paySession.ReportEdgeFailure(&edgeLocator{
|
||||||
|
channelID: failedEdge.channelID,
|
||||||
|
direction: 0,
|
||||||
|
})
|
||||||
|
paySession.ReportEdgeFailure(&edgeLocator{
|
||||||
|
channelID: failedEdge.channelID,
|
||||||
|
direction: 1,
|
||||||
|
})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1963,31 +2008,43 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFailedChannelID tries to locate the failing channel given a route and the
|
// getFailedEdge tries to locate the failing channel given a route and the
|
||||||
// pubkey of the node that sent the error. It will assume that the error is
|
// pubkey of the node that sent the error. It will assume that the error is
|
||||||
// associated with the outgoing channel of the error node.
|
// associated with the outgoing channel of the error node.
|
||||||
func getFailedChannelID(route *Route, errSource *btcec.PublicKey) (
|
func getFailedEdge(route *Route, errSource Vertex) (
|
||||||
uint64, error) {
|
*edgeLocator, error) {
|
||||||
|
|
||||||
// As this error indicates that the target channel was unable to carry
|
hopCount := len(route.Hops)
|
||||||
// this HTLC (for w/e reason), we'll query the index to find the
|
fromNode := route.SourcePubKey
|
||||||
// _outgoing_ channel the source of the error was meant to pass the
|
for i, hop := range route.Hops {
|
||||||
// HTLC along to.
|
toNode := hop.PubKeyBytes
|
||||||
if badChan, ok := route.nextHopChannel(errSource); ok {
|
|
||||||
return badChan.ChannelID, nil
|
// Determine if we have a failure from the final hop.
|
||||||
|
//
|
||||||
|
// TODO(joostjager): In this case, certain types of errors are
|
||||||
|
// not expected. For example FailUnknownNextPeer. This could be
|
||||||
|
// a reason to prune the node?
|
||||||
|
finalHopFailing := i == hopCount-1 && errSource == toNode
|
||||||
|
|
||||||
|
// As this error indicates that the target channel was unable to
|
||||||
|
// carry this HTLC (for w/e reason), we'll return the _outgoing_
|
||||||
|
// channel that the source of the error was meant to pass the
|
||||||
|
// HTLC along to.
|
||||||
|
//
|
||||||
|
// If the errSource is the final hop, we assume that the failing
|
||||||
|
// channel is the incoming channel.
|
||||||
|
if errSource == fromNode || finalHopFailing {
|
||||||
|
return newEdgeLocatorByPubkeys(
|
||||||
|
hop.ChannelID,
|
||||||
|
&fromNode,
|
||||||
|
&toNode,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fromNode = toNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we weren't able to find the hop *after* this node, then we'll
|
return nil, fmt.Errorf("cannot find error source node in route")
|
||||||
// attempt to disable the previous channel.
|
|
||||||
//
|
|
||||||
// TODO(joostjager): errSource must be the final hop then? In that case,
|
|
||||||
// certain types of errors are not expected. For example
|
|
||||||
// FailUnknownNextPeer. This could be a reason to prune the node?
|
|
||||||
if prevChan, ok := route.prevHopChannel(errSource); ok {
|
|
||||||
return prevChan.ChannelID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, fmt.Errorf("cannot find channel in route")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyChannelUpdate validates a channel update and if valid, applies it to the
|
// applyChannelUpdate validates a channel update and if valid, applies it to the
|
||||||
|
@ -1859,7 +1859,7 @@ func TestFindPathFeeWeighting(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ignoreVertex := make(map[Vertex]struct{})
|
ignoreVertex := make(map[Vertex]struct{})
|
||||||
ignoreEdge := make(map[uint64]struct{})
|
ignoreEdge := make(map[edgeLocator]struct{})
|
||||||
|
|
||||||
amt := lnwire.MilliSatoshi(100)
|
amt := lnwire.MilliSatoshi(100)
|
||||||
|
|
||||||
|
28
routing/testdata/basic_graph.json
vendored
28
routing/testdata/basic_graph.json
vendored
@ -142,11 +142,11 @@
|
|||||||
"capacity": 120000
|
"capacity": 120000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node_1": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||||
"node_2": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||||
"channel_id": 12345,
|
"channel_id": 12345,
|
||||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||||
"flags": 0,
|
"flags": 1,
|
||||||
"expiry": 1,
|
"expiry": 1,
|
||||||
"min_htlc": 1000,
|
"min_htlc": 1000,
|
||||||
"fee_base_msat": 10,
|
"fee_base_msat": 10,
|
||||||
@ -154,11 +154,11 @@
|
|||||||
"capacity": 100000
|
"capacity": 100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node_1": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||||
"node_2": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||||
"channel_id": 12345,
|
"channel_id": 12345,
|
||||||
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
"channel_point": "89dc56859c6a082d15ba1a7f6cb6be3fea62e1746e2cb8497b1189155c21a233:0",
|
||||||
"flags": 1,
|
"flags": 0,
|
||||||
"expiry": 1,
|
"expiry": 1,
|
||||||
"min_htlc": 1,
|
"min_htlc": 1,
|
||||||
"fee_base_msat": 10,
|
"fee_base_msat": 10,
|
||||||
@ -166,7 +166,7 @@
|
|||||||
"capacity": 100000
|
"capacity": 100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||||
"channel_id": 3495345,
|
"channel_id": 3495345,
|
||||||
"channel_point": "9f155756b33a0a6827713965babbd561b55f9520444ac5db0cf7cb2eb0deb5bc:0",
|
"channel_point": "9f155756b33a0a6827713965babbd561b55f9520444ac5db0cf7cb2eb0deb5bc:0",
|
||||||
@ -178,7 +178,7 @@
|
|||||||
"capacity": 110000
|
"capacity": 110000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
"node_1": "032b480de5d002f1a8fd1fe1bbf0a0f1b07760f65f052e66d56f15d71097c01add",
|
||||||
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
"node_2": "036264734b40c9e91d3d990a8cdfbbe23b5b0b7ad3cd0e080a25dcd05d39eeb7eb",
|
||||||
"channel_id": 3495345,
|
"channel_id": 3495345,
|
||||||
"channel_point": "9f155756b33a0a6827713965babbd561b55f9520444ac5db0cf7cb2eb0deb5bc:0",
|
"channel_point": "9f155756b33a0a6827713965babbd561b55f9520444ac5db0cf7cb2eb0deb5bc:0",
|
||||||
@ -214,11 +214,11 @@
|
|||||||
"capacity": 10000
|
"capacity": 10000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node_1": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||||
"node_2": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||||
"channel_id": 689530843,
|
"channel_id": 689530843,
|
||||||
"channel_point": "25376aa6cb81913ad30416bd22d4083241bd6d68e811d0284d3c3a17795c458a:0",
|
"channel_point": "25376aa6cb81913ad30416bd22d4083241bd6d68e811d0284d3c3a17795c458a:0",
|
||||||
"flags": 0,
|
"flags": 1,
|
||||||
"expiry": 10,
|
"expiry": 10,
|
||||||
"min_htlc": 1,
|
"min_htlc": 1,
|
||||||
"fee_base_msat": 10,
|
"fee_base_msat": 10,
|
||||||
@ -226,11 +226,11 @@
|
|||||||
"capacity": 100000
|
"capacity": 100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"node_1": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
"node_1": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
||||||
"node_2": "02e7b1aaac10977c38e9c61c74dc66840de211bcec3021603e7977bc5e28edabfd",
|
"node_2": "0367cec75158a4129177bfb8b269cb586efe93d751b43800d456485e81c2620ca6",
|
||||||
"channel_id": 689530843,
|
"channel_id": 689530843,
|
||||||
"channel_point": "25376aa6cb81913ad30416bd22d4083241bd6d68e811d0284d3c3a17795c458a:0",
|
"channel_point": "25376aa6cb81913ad30416bd22d4083241bd6d68e811d0284d3c3a17795c458a:0",
|
||||||
"flags": 1,
|
"flags": 0,
|
||||||
"expiry": 1,
|
"expiry": 1,
|
||||||
"min_htlc": 1,
|
"min_htlc": 1,
|
||||||
"fee_base_msat": 10,
|
"fee_base_msat": 10,
|
||||||
|
Loading…
Reference in New Issue
Block a user