054e42f680
This commit exposes the three main parameters that influence mission control and path finding to the user as command line or config file flags. It allows for fine-tuning for optimal results.
213 lines
7.9 KiB
Go
213 lines
7.9 KiB
Go
package routing
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
|
)
|
|
|
|
// PaymentSession is used during SendPayment attempts to provide routes to
|
|
// attempt. It also defines methods to give the PaymentSession additional
|
|
// information learned during the previous attempts.
|
|
type PaymentSession interface {
|
|
// RequestRoute returns the next route to attempt for routing the
|
|
// specified HTLC payment to the target node.
|
|
RequestRoute(payment *LightningPayment,
|
|
height uint32, finalCltvDelta uint16) (*route.Route, error)
|
|
|
|
// ReportVertexFailure reports to the PaymentSession that the passsed
|
|
// vertex failed to route the previous payment attempt. The
|
|
// PaymentSession will use this information to produce a better next
|
|
// route.
|
|
ReportVertexFailure(v route.Vertex)
|
|
|
|
// ReportEdgeFailure reports to the PaymentSession that the passed edge
|
|
// failed to route the previous payment attempt. A minimum penalization
|
|
// amount is included to attenuate the failure. This is set to a
|
|
// non-zero value for channel balance failures. The PaymentSession will
|
|
// use this information to produce a better next route.
|
|
ReportEdgeFailure(failedEdge edge, minPenalizeAmt lnwire.MilliSatoshi)
|
|
|
|
// ReportEdgePolicyFailure reports to the PaymentSession that we
|
|
// received a failure message that relates to a channel policy. For
|
|
// these types of failures, the PaymentSession can decide whether to to
|
|
// keep the edge included in the next attempted route. The
|
|
// PaymentSession will use this information to produce a better next
|
|
// route.
|
|
ReportEdgePolicyFailure(failedEdge edge)
|
|
}
|
|
|
|
// paymentSession is used during an HTLC routings session to prune the local
|
|
// chain view in response to failures, and also report those failures back to
|
|
// MissionControl. The snapshot copied for this session will only ever grow,
|
|
// and will now be pruned after a decay like the main view within mission
|
|
// control. We do this as we want to avoid the case where we continually try a
|
|
// bad edge or route multiple times in a session. This can lead to an infinite
|
|
// loop if payment attempts take long enough. An additional set of edges can
|
|
// also be provided to assist in reaching the payment's destination.
|
|
type paymentSession struct {
|
|
additionalEdges map[route.Vertex][]*channeldb.ChannelEdgePolicy
|
|
|
|
bandwidthHints map[uint64]lnwire.MilliSatoshi
|
|
|
|
// errFailedFeeChans is a map of the short channel IDs that were the
|
|
// 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
|
|
// require pruning, but any subsequent ones do.
|
|
errFailedPolicyChans map[nodeChannel]struct{}
|
|
|
|
mc *MissionControl
|
|
|
|
preBuiltRoute *route.Route
|
|
preBuiltRouteTried bool
|
|
|
|
pathFinder pathFinder
|
|
}
|
|
|
|
// A compile time assertion to ensure paymentSession meets the PaymentSession
|
|
// interface.
|
|
var _ PaymentSession = (*paymentSession)(nil)
|
|
|
|
// ReportVertexFailure adds a vertex to the graph prune view after a client
|
|
// reports a routing failure localized to the vertex. The time the vertex was
|
|
// added is noted, as it'll be pruned from the shared view after a period of
|
|
// vertexDecay. However, the vertex will remain pruned for the *local* session.
|
|
// This ensures we don't retry this vertex during the payment attempt.
|
|
//
|
|
// NOTE: Part of the PaymentSession interface.
|
|
func (p *paymentSession) ReportVertexFailure(v route.Vertex) {
|
|
p.mc.reportVertexFailure(v)
|
|
}
|
|
|
|
// ReportEdgeFailure adds a channel to the graph prune view. The time the
|
|
// channel was added is noted, as it'll be pruned from the global view after a
|
|
// period of edgeDecay. However, the edge will remain pruned for the duration
|
|
// of the *local* session. This ensures that we don't flap by continually
|
|
// retrying an edge after its pruning has expired.
|
|
//
|
|
// TODO(roasbeef): also add value attempted to send and capacity of channel
|
|
//
|
|
// NOTE: Part of the PaymentSession interface.
|
|
func (p *paymentSession) ReportEdgeFailure(failedEdge edge,
|
|
minPenalizeAmt lnwire.MilliSatoshi) {
|
|
|
|
p.mc.reportEdgeFailure(failedEdge, minPenalizeAmt)
|
|
}
|
|
|
|
// ReportEdgePolicyFailure handles a failure message that relates to a
|
|
// channel policy. For these types of failures, the policy is updated and we
|
|
// want to keep it included during path finding. This function does mark the
|
|
// 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
|
|
// new channel updates.
|
|
//
|
|
// NOTE: Part of the PaymentSession interface.
|
|
//
|
|
// TODO(joostjager): Move this logic into global mission control.
|
|
func (p *paymentSession) ReportEdgePolicyFailure(failedEdge edge) {
|
|
key := nodeChannel{
|
|
node: failedEdge.from,
|
|
channel: failedEdge.channel,
|
|
}
|
|
|
|
// Check to see if we've already reported a policy related failure for
|
|
// this channel. If so, then we'll prune out the vertex.
|
|
_, ok := p.errFailedPolicyChans[key]
|
|
if ok {
|
|
// TODO(joostjager): is this aggressive pruning still necessary?
|
|
// Just pruning edges may also work unless there is a huge
|
|
// number of failing channels from that node?
|
|
p.ReportVertexFailure(key.node)
|
|
|
|
return
|
|
}
|
|
|
|
// Finally, we'll record a policy failure from this node and move on.
|
|
p.errFailedPolicyChans[key] = struct{}{}
|
|
}
|
|
|
|
// RequestRoute returns a route which is likely to be capable for successfully
|
|
// routing the specified HTLC payment to the target node. Initially the first
|
|
// set of paths returned from this method may encounter routing failure along
|
|
// the way, however as more payments are sent, mission control will start to
|
|
// build an up to date view of the network itself. With each payment a new area
|
|
// will be explored, which feeds into the recommendations made for routing.
|
|
//
|
|
// NOTE: This function is safe for concurrent access.
|
|
// NOTE: Part of the PaymentSession interface.
|
|
func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
|
height uint32, finalCltvDelta uint16) (*route.Route, error) {
|
|
|
|
switch {
|
|
|
|
// If we have a pre-built route, use that directly.
|
|
case p.preBuiltRoute != nil && !p.preBuiltRouteTried:
|
|
p.preBuiltRouteTried = true
|
|
|
|
return p.preBuiltRoute, nil
|
|
|
|
// If the pre-built route has been tried already, the payment session is
|
|
// over.
|
|
case p.preBuiltRoute != nil:
|
|
return nil, fmt.Errorf("pre-built route already tried")
|
|
}
|
|
|
|
// If a route cltv limit was specified, we need to subtract the final
|
|
// delta before passing it into path finding. The optimal path is
|
|
// independent of the final cltv delta and the path finding algorithm is
|
|
// unaware of this value.
|
|
var cltvLimit *uint32
|
|
if payment.CltvLimit != nil {
|
|
limit := *payment.CltvLimit - uint32(finalCltvDelta)
|
|
cltvLimit = &limit
|
|
}
|
|
|
|
// TODO(roasbeef): sync logic amongst dist sys
|
|
|
|
// Taking into account this prune view, we'll attempt to locate a path
|
|
// to our destination, respecting the recommendations from
|
|
// MissionControl.
|
|
path, err := p.pathFinder(
|
|
&graphParams{
|
|
graph: p.mc.graph,
|
|
additionalEdges: p.additionalEdges,
|
|
bandwidthHints: p.bandwidthHints,
|
|
},
|
|
&RestrictParams{
|
|
ProbabilitySource: p.mc.getEdgeProbability,
|
|
FeeLimit: payment.FeeLimit,
|
|
OutgoingChannelID: payment.OutgoingChannelID,
|
|
CltvLimit: cltvLimit,
|
|
PaymentAttemptPenalty: p.mc.cfg.PaymentAttemptPenalty,
|
|
MinProbability: p.mc.cfg.MinRouteProbability,
|
|
},
|
|
p.mc.selfNode.PubKeyBytes, payment.Target,
|
|
payment.Amount,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// With the next candidate path found, we'll attempt to turn this into
|
|
// a route by applying the time-lock and fee requirements.
|
|
sourceVertex := route.Vertex(p.mc.selfNode.PubKeyBytes)
|
|
route, err := newRoute(
|
|
payment.Amount, sourceVertex, path, height, finalCltvDelta,
|
|
)
|
|
if err != nil {
|
|
// TODO(roasbeef): return which edge/vertex didn't work
|
|
// out
|
|
return nil, err
|
|
}
|
|
|
|
return route, err
|
|
}
|
|
|
|
// nodeChannel is a combination of the node pubkey and one of its channels.
|
|
type nodeChannel struct {
|
|
node route.Vertex
|
|
channel uint64
|
|
}
|