diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index 67d419d83..a99d76173 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -3,6 +3,8 @@ package routing import ( "sync" "time" + + "github.com/lightningnetwork/lnd/channeldb" ) const ( @@ -49,6 +51,10 @@ type missionControl struct { // to that particular vertex. failedVertexes map[vertex]time.Time + graph *channeldb.ChannelGraph + + selfNode *channeldb.LightningNode + sync.Mutex // TODO(roasbeef): further counters, if vertex continually unavailable, @@ -60,8 +66,12 @@ type missionControl struct { // newMissionControl returns a new instance of missionControl. // // TODO(roasbeef): persist memory -func newMissionControl() *missionControl { +func newMissionControl(g *channeldb.ChannelGraph, + s *channeldb.LightningNode) *missionControl { + return &missionControl{ + graph: g, + selfNode: s, failedEdges: make(map[uint64]time.Time), failedVertexes: make(map[vertex]time.Time), } @@ -92,6 +102,45 @@ func (m *missionControl) ReportChannelFailure(e uint64) { m.Unlock() } +// 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. +func (m *missionControl) RequestRoute(payment *LightningPayment, + height uint32) (*Route, error) { + + // First, we'll query mission control for it's current recommendation + // on the edges/vertexes to ignore during path finding. + pruneView := m.GraphPruneView() + + // 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 := findPath(nil, m.graph, m.selfNode, payment.Target, + pruneView.vertexes, pruneView.edges, 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 := newVertex(m.selfNode.PubKey) + route, err := newRoute(payment.Amount, sourceVertex, path, height) + if err != nil { + // TODO(roasbeef): return which edge/vertex didn't work + // out + return nil, err + } + + return route, err +} + // GraphPruneView returns a new graphPruneView instance which is to be // consulted during path finding. If a vertex/edge is found within the returned // prune view, it is to be ignored as a goroutine has had issues routing diff --git a/routing/router.go b/routing/router.go index 981fc2ee1..e2697e13d 100644 --- a/routing/router.go +++ b/routing/router.go @@ -234,7 +234,7 @@ func New(cfg Config) (*ChannelRouter, error) { networkUpdates: make(chan *routingMsg), topologyClients: make(map[uint64]*topologyClient), ntfnClientUpdates: make(chan *topologyClientUpdate), - missionControl: newMissionControl(), + missionControl: newMissionControl(cfg.Graph, selfNode), routeCache: make(map[routeTuple][]*Route), quit: make(chan struct{}), }, nil @@ -1189,19 +1189,13 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route // We'll continue until either our payment succeeds, or we encounter a // critical error during path finding. - sourceVertex := newVertex(r.selfNode.PubKey) for { - // First, we'll query mission control for it's current - // recommendation on the edges/vertexes to ignore during path - // finding. - pruneView := r.missionControl.GraphPruneView() - - // Taking into account this prune view, we'll attempt to locate - // a path to our destination, respecting the recommendations - // from missionControl. - path, err := findPath(nil, r.cfg.Graph, r.selfNode, - payment.Target, pruneView.vertexes, pruneView.edges, - payment.Amount) + // We'll kick things off by requesting a new route from mission + // control, which will incoroporate the current best known + // state of the channel graph and our past HTLC routing + // successes/failures. + route, err := r.missionControl.RequestRoute(payment, + uint32(currentHeight)) if err != nil { // If we're unable to successfully make a payment using // any of the routes we've found, then return an error. @@ -1214,17 +1208,6 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route return preImage, nil, err } - // With the next candiate path found, we'll attempt to turn - // this into a route by applying the time-lock and fee - // requirements. - route, err := newRoute(payment.Amount, sourceVertex, path, - uint32(currentHeight)) - if err != nil { - // TODO(roasbeef): return which edge/vertex didn't work - // out - return preImage, nil, err - } - log.Tracef("Attempting to send payment %x, using route: %v", payment.PaymentHash, newLogClosure(func() string { return spew.Sdump(route)