routing: global probability based mission control

Previously every payment had its own local mission control state which
was in effect only for that payment. In this commit most of the local
state is removed and payments all tap into the global mission control
probability estimator.

Furthermore the decay time of pruned edges and nodes is extended, so
that observations about the network can better benefit future payment
processes.

Last, the probability function is transformed from a binary output to a
gradual curve, allowing for a better trade off between candidate routes.
This commit is contained in:
Joost Jager
2019-03-19 17:09:27 +01:00
parent 3349d517aa
commit 7133f37bb8
12 changed files with 338 additions and 220 deletions

View File

@@ -319,6 +319,13 @@ func (e *EdgeLocator) String() string {
return fmt.Sprintf("%v:%v", e.ChannelID, e.Direction)
}
// edge is a combination of a channel and the node pubkeys of both of its
// endpoints.
type edge struct {
from, to route.Vertex
channel uint64
}
// ChannelRouter is the layer 3 router within the Lightning stack. Below the
// ChannelRouter is the HtlcSwitch, and below that is the Bitcoin blockchain
// itself. The primary role of the ChannelRouter is to respond to queries for
@@ -1769,7 +1776,9 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// Always determine chan id ourselves, because a channel
// update with id may not be available.
failedEdge, err := getFailedEdge(rt, route.Vertex(errVertex))
failedEdge, failedAmt, err := getFailedEdge(
rt, route.Vertex(errVertex),
)
if err != nil {
return true
}
@@ -1801,13 +1810,11 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// update to fail?
if !updateOk {
paySession.ReportEdgeFailure(
failedEdge,
failedEdge, 0,
)
}
paySession.ReportEdgePolicyFailure(
route.NewVertex(errSource), failedEdge,
)
paySession.ReportEdgePolicyFailure(failedEdge)
}
switch onionErr := fErr.FailureMessage.(type) {
@@ -1898,7 +1905,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// the update and continue.
case *lnwire.FailChannelDisabled:
r.applyChannelUpdate(&onionErr.Update, errSource)
paySession.ReportEdgeFailure(failedEdge)
paySession.ReportEdgeFailure(failedEdge, 0)
return false
// It's likely that the outgoing channel didn't have
@@ -1906,7 +1913,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// now, and continue onwards with our path finding.
case *lnwire.FailTemporaryChannelFailure:
r.applyChannelUpdate(onionErr.Update, errSource)
paySession.ReportEdgeFailure(failedEdge)
paySession.ReportEdgeFailure(failedEdge, failedAmt)
return false
// If the send fail due to a node not having the
@@ -1931,7 +1938,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// returning errors in order to attempt to black list
// another node.
case *lnwire.FailUnknownNextPeer:
paySession.ReportEdgeFailure(failedEdge)
paySession.ReportEdgeFailure(failedEdge, 0)
return false
// If the node wasn't able to forward for which ever
@@ -1962,14 +1969,12 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// we'll prune the channel in both directions and
// continue with the rest of the routes.
case *lnwire.FailPermanentChannelFailure:
paySession.ReportEdgeFailure(&EdgeLocator{
ChannelID: failedEdge.ChannelID,
Direction: 0,
})
paySession.ReportEdgeFailure(&EdgeLocator{
ChannelID: failedEdge.ChannelID,
Direction: 1,
})
paySession.ReportEdgeFailure(failedEdge, 0)
paySession.ReportEdgeFailure(edge{
from: failedEdge.to,
to: failedEdge.from,
channel: failedEdge.channel,
}, 0)
return false
default:
@@ -1979,12 +1984,14 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// 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
// associated with the outgoing channel of the error node.
func getFailedEdge(route *route.Route, errSource route.Vertex) (
*EdgeLocator, error) {
// associated with the outgoing channel of the error node. As a second result,
// it returns the amount sent over the edge.
func getFailedEdge(route *route.Route, errSource route.Vertex) (edge,
lnwire.MilliSatoshi, error) {
hopCount := len(route.Hops)
fromNode := route.SourcePubKey
amt := route.TotalAmount
for i, hop := range route.Hops {
toNode := hop.PubKeyBytes
@@ -2003,17 +2010,18 @@ func getFailedEdge(route *route.Route, errSource route.Vertex) (
// 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
return edge{
from: fromNode,
to: toNode,
channel: hop.ChannelID,
}, amt, nil
}
fromNode = toNode
amt = hop.AmtToForward
}
return nil, fmt.Errorf("cannot find error source node in route")
return edge{}, 0, fmt.Errorf("cannot find error source node in route")
}
// applyChannelUpdate validates a channel update and if valid, applies it to the