From 0113035f6d6365c9fbb189759bbeb6ac923b2d71 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 6 Jun 2018 20:33:55 -0700 Subject: [PATCH] routing: add new NewPaymentSessionFromRoutes function instead of interface In this commit, we modify the recent refactoring of the mission control sub-system to overload the existing payment session, rather than create a brand new one. This allows us to re-use more of the existing logic, and also feedback into mission control the failures incurred by any user selected routes. --- routing/missioncontrol.go | 46 +++++++++++++++++++++++++++++++++------ routing/router.go | 43 +++++------------------------------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index b9e97598..c6eaad9a 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -1,6 +1,7 @@ package routing import ( + "fmt" "sync" "time" @@ -71,8 +72,7 @@ type missionControl struct { // newMissionControl returns a new instance of missionControl. // // TODO(roasbeef): persist memory -func newMissionControl( - g *channeldb.ChannelGraph, selfNode *channeldb.LightningNode, +func newMissionControl(g *channeldb.ChannelGraph, selfNode *channeldb.LightningNode, qb func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi) *missionControl { return &missionControl{ @@ -165,6 +165,9 @@ type paymentSession struct { bandwidthHints map[uint64]lnwire.MilliSatoshi mc *missionControl + + haveRoutes bool + preBuiltRoutes []*Route } // NewPaymentSession creates a new payment session backed by the latest prune @@ -240,6 +243,18 @@ func (m *missionControl) NewPaymentSession(routeHints [][]HopHint, }, nil } +// NewPaymentSessionFromRoutes creates a new paymentSession instance that will +// skip all path finding, and will instead utilize a set of pre-built routes. +// This constructor allows callers to specify their own routes which can be +// used for things like channel rebalancing, and swaps. +func (m *missionControl) NewPaymentSessionFromRoutes(routes []*Route) *paymentSession { + return &paymentSession{ + haveRoutes: true, + preBuiltRoutes: routes, + mc: m, + } +} + // generateBandwidthHints is a helper function that's utilized the main // findPath function in order to obtain hints from the lower layer w.r.t to the // available bandwidth of edges on the network. Currently, we'll only obtain @@ -326,9 +341,25 @@ func (p *paymentSession) ReportChannelFailure(e uint64) { func (p *paymentSession) RequestRoute(payment *LightningPayment, height uint32, finalCltvDelta uint16) (*Route, error) { - // First, we'll obtain our current prune view snapshot. This view will - // only ever grow during the duration of this payment session, never - // shrinking. + switch { + // If we have a set of pre-built routes, then we'll just pop off the + // next route from the queue, and use it directly. + case p.haveRoutes && len(p.preBuiltRoutes) > 0: + nextRoute := p.preBuiltRoutes[0] + p.preBuiltRoutes[0] = nil // Set to nil to avoid GC leak. + p.preBuiltRoutes = p.preBuiltRoutes[1:] + + return nextRoute, nil + + // If we were instantiated with a set of pre-built routes, and we've + // run out, then we'll return a terminal error. + case p.haveRoutes && len(p.preBuiltRoutes) == 0: + return nil, fmt.Errorf("pre-built routes exhausted") + } + + // Otherwise we actually need to perform path finding, so we'll obtain + // our current prune view snapshot. This view will only ever grow + // during the duration of this payment session, never shrinking. pruneView := p.pruneViewSnapshot log.Debugf("Mission Control session using prune view of %v "+ @@ -352,8 +383,9 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, // With the next candidate path found, we'll attempt to turn this into // a route by applying the time-lock and fee requirements. sourceVertex := Vertex(p.mc.selfNode.PubKeyBytes) - route, err := newRoute(payment.Amount, sourceVertex, path, height, - finalCltvDelta) + route, err := newRoute( + payment.Amount, sourceVertex, path, height, finalCltvDelta, + ) if err != nil { // TODO(roasbeef): return which edge/vertex didn't work // out diff --git a/routing/router.go b/routing/router.go index dd2c1a33..0ee4b072 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1532,37 +1532,6 @@ type LightningPayment struct { // TODO(roasbeef): add e2e message? } -// PaymentSession imitates paymentSession created from mission control -// and enables SendPayment and SendToRoute to call sendPayment. -type PaymentSession interface { - RequestRoute(payment *LightningPayment, currentHeight uint32, - finalCLTVDelta uint16) (*Route, error) - - ReportVertexFailure(Vertex) - ReportChannelFailure(chanID uint64) -} - -// sendToRoutePaymentSession implements PaymentSession. -type sendToRoutePaymentSession struct { - currentRouteIndex int - routes []*Route -} - -func (s *sendToRoutePaymentSession) RequestRoute(payment *LightningPayment, - currentHeight uint32, finalCLTVDelta uint16) (*Route, error) { - - defer func() { s.currentRouteIndex++ }() - - if s.currentRouteIndex < len(s.routes) { - return s.routes[s.currentRouteIndex], nil - } - return nil, errors.New("unable to send payment along any of " + - "the given routes") -} - -func (s *sendToRoutePaymentSession) ReportVertexFailure(v Vertex) {} -func (s *sendToRoutePaymentSession) ReportChannelFailure(chanID uint64) {} - // SendPayment attempts to send a payment as described within the passed // LightningPayment. This function is blocking and will return either: when the // payment is successful, or all candidates routes have been attempted and @@ -1594,9 +1563,9 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route func (r *ChannelRouter) SendToRoute(routes []*Route, payment *LightningPayment) ([32]byte, *Route, error) { - paySession := &sendToRoutePaymentSession{ - routes: routes, - } + paySession := r.missionControl.NewPaymentSessionFromRoutes( + routes, + ) return r.sendPayment(payment, paySession) } @@ -1609,7 +1578,7 @@ func (r *ChannelRouter) SendToRoute(routes []*Route, // within the network to reach the destination. Additionally, the payment // preimage will also be returned. func (r *ChannelRouter) sendPayment(payment *LightningPayment, - paySession PaymentSession) ([32]byte, *Route, error) { + paySession *paymentSession) ([32]byte, *Route, error) { log.Tracef("Dispatching route for lightning payment: %v", newLogClosure(func() string { @@ -1960,7 +1929,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // pruneVertexFailure will attempt to prune a vertex from the current available // vertexes of the target payment session in response to an encountered routing // error. -func pruneVertexFailure(paySession PaymentSession, route *Route, +func pruneVertexFailure(paySession *paymentSession, route *Route, errSource *btcec.PublicKey, nextNode bool) { // By default, we'll try to prune the node that actually sent us the @@ -1986,7 +1955,7 @@ func pruneVertexFailure(paySession PaymentSession, route *Route, // pruneEdgeFailure will attempts to prune an edge from the current available // edges of the target payment session in response to an encountered routing // error. -func pruneEdgeFailure(paySession PaymentSession, route *Route, +func pruneEdgeFailure(paySession *paymentSession, route *Route, errSource *btcec.PublicKey) { // As this error indicates that the target channel was unable to carry