From 37e275169537d48094fd3051d46b6dcc40545050 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 18 Jun 2019 18:30:56 +0200 Subject: [PATCH] routing+routerrpc: isolate payment session source from mission control --- lnrpc/routerrpc/config.go | 28 ++++++ lnrpc/routerrpc/config_active.go | 7 +- lnrpc/routerrpc/config_default.go | 7 +- routing/missioncontrol.go | 157 +++--------------------------- routing/missioncontrol_test.go | 10 +- routing/mock_test.go | 17 ++++ routing/payment_session.go | 33 ++++--- routing/payment_session_source.go | 150 ++++++++++++++++++++++++++++ routing/payment_session_test.go | 14 ++- routing/router.go | 28 +++++- routing/router_test.go | 25 +++-- server.go | 23 ++++- 12 files changed, 308 insertions(+), 191 deletions(-) create mode 100644 lnrpc/routerrpc/config.go create mode 100644 routing/payment_session_source.go diff --git a/lnrpc/routerrpc/config.go b/lnrpc/routerrpc/config.go new file mode 100644 index 00000000..15dd6c40 --- /dev/null +++ b/lnrpc/routerrpc/config.go @@ -0,0 +1,28 @@ +package routerrpc + +import ( + "time" + + "github.com/lightningnetwork/lnd/lnwire" +) + +// RoutingConfig contains the configurable parameters that control routing. +type RoutingConfig struct { + // PenaltyHalfLife defines after how much time a penalized node or + // channel is back at 50% probability. + PenaltyHalfLife time.Duration + + // PaymentAttemptPenalty is the virtual cost in path finding weight + // units of executing a payment attempt that fails. It is used to trade + // off potentially better routes against their probability of + // succeeding. + PaymentAttemptPenalty lnwire.MilliSatoshi + + // MinProbability defines the minimum success probability of the + // returned route. + MinRouteProbability float64 + + // AprioriHopProbability is the assumed success probability of a hop in + // a route when no other information is available. + AprioriHopProbability float64 +} diff --git a/lnrpc/routerrpc/config_active.go b/lnrpc/routerrpc/config_active.go index 36877c36..b919364e 100644 --- a/lnrpc/routerrpc/config_active.go +++ b/lnrpc/routerrpc/config_active.go @@ -72,10 +72,9 @@ func DefaultConfig() *Config { } } -// GetMissionControlConfig returns the mission control config based on this sub -// server config. -func GetMissionControlConfig(cfg *Config) *routing.MissionControlConfig { - return &routing.MissionControlConfig{ +// GetRoutingConfig returns the routing config based on this sub server config. +func GetRoutingConfig(cfg *Config) *RoutingConfig { + return &RoutingConfig{ AprioriHopProbability: cfg.AprioriHopProbability, MinRouteProbability: cfg.MinRouteProbability, PaymentAttemptPenalty: lnwire.NewMSatFromSatoshis( diff --git a/lnrpc/routerrpc/config_default.go b/lnrpc/routerrpc/config_default.go index 81c4a577..a3a95021 100644 --- a/lnrpc/routerrpc/config_default.go +++ b/lnrpc/routerrpc/config_default.go @@ -14,10 +14,9 @@ func DefaultConfig() *Config { return &Config{} } -// GetMissionControlConfig returns the mission control config based on this sub -// server config. -func GetMissionControlConfig(cfg *Config) *routing.MissionControlConfig { - return &routing.MissionControlConfig{ +// GetRoutingConfig returns the routing config based on this sub server config. +func GetRoutingConfig(cfg *Config) *RoutingConfig { + return &RoutingConfig{ AprioriHopProbability: routing.DefaultAprioriHopProbability, MinRouteProbability: routing.DefaultMinRouteProbability, PaymentAttemptPenalty: routing.DefaultPaymentAttemptPenalty, diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index 17575a72..69a34b65 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -5,12 +5,10 @@ import ( "sync" "time" - "github.com/btcsuite/btcd/btcec" "github.com/coreos/bbolt" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" - "github.com/lightningnetwork/lnd/zpay32" ) const ( @@ -32,12 +30,6 @@ const ( type MissionControl struct { history map[route.Vertex]*nodeHistory - graph *channeldb.ChannelGraph - - selfNode *channeldb.LightningNode - - queryBandwidth func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi - // now is expected to return the current time. It is supplied as an // external function to enable deterministic unit tests. now func() time.Time @@ -52,10 +44,6 @@ type MissionControl struct { // TODO(roasbeef): also add favorable metrics for nodes } -// A compile time assertion to ensure MissionControl meets the -// PaymentSessionSource interface. -var _ PaymentSessionSource = (*MissionControl)(nil) - // MissionControlConfig defines parameters that control mission control // behaviour. type MissionControlConfig struct { @@ -63,16 +51,6 @@ type MissionControlConfig struct { // channel is back at 50% probability. PenaltyHalfLife time.Duration - // PaymentAttemptPenalty is the virtual cost in path finding weight - // units of executing a payment attempt that fails. It is used to trade - // off potentially better routes against their probability of - // succeeding. - PaymentAttemptPenalty lnwire.MilliSatoshi - - // MinProbability defines the minimum success probability of the - // returned route. - MinRouteProbability float64 - // AprioriHopProbability is the assumed success probability of a hop in // a route when no other information is available. AprioriHopProbability float64 @@ -143,126 +121,15 @@ type MissionControlChannelSnapshot struct { } // NewMissionControl returns a new instance of missionControl. -// -// TODO(roasbeef): persist memory -func NewMissionControl(g *channeldb.ChannelGraph, selfNode *channeldb.LightningNode, - qb func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi, - cfg *MissionControlConfig) *MissionControl { - +func NewMissionControl(cfg *MissionControlConfig) *MissionControl { log.Debugf("Instantiating mission control with config: "+ - "PenaltyHalfLife=%v, PaymentAttemptPenalty=%v, "+ - "MinRouteProbability=%v, AprioriHopProbability=%v", - cfg.PenaltyHalfLife, - int64(cfg.PaymentAttemptPenalty.ToSatoshis()), - cfg.MinRouteProbability, cfg.AprioriHopProbability) + "PenaltyHalfLife=%v, AprioriHopProbability=%v", + cfg.PenaltyHalfLife, cfg.AprioriHopProbability) return &MissionControl{ - history: make(map[route.Vertex]*nodeHistory), - selfNode: selfNode, - queryBandwidth: qb, - graph: g, - now: time.Now, - cfg: cfg, - } -} - -// NewPaymentSession creates a new payment session backed by the latest prune -// view from Mission Control. An optional set of routing hints can be provided -// in order to populate additional edges to explore when finding a path to the -// payment's destination. -func (m *MissionControl) NewPaymentSession(routeHints [][]zpay32.HopHint, - target route.Vertex) (PaymentSession, error) { - - edges := make(map[route.Vertex][]*channeldb.ChannelEdgePolicy) - - // Traverse through all of the available hop hints and include them in - // our edges map, indexed by the public key of the channel's starting - // node. - for _, routeHint := range routeHints { - // If multiple hop hints are provided within a single route - // hint, we'll assume they must be chained together and sorted - // in forward order in order to reach the target successfully. - for i, hopHint := range routeHint { - // In order to determine the end node of this hint, - // we'll need to look at the next hint's start node. If - // we've reached the end of the hints list, we can - // assume we've reached the destination. - endNode := &channeldb.LightningNode{} - if i != len(routeHint)-1 { - endNode.AddPubKey(routeHint[i+1].NodeID) - } else { - targetPubKey, err := btcec.ParsePubKey( - target[:], btcec.S256(), - ) - if err != nil { - return nil, err - } - endNode.AddPubKey(targetPubKey) - } - - // Finally, create the channel edge from the hop hint - // and add it to list of edges corresponding to the node - // at the start of the channel. - edge := &channeldb.ChannelEdgePolicy{ - Node: endNode, - ChannelID: hopHint.ChannelID, - FeeBaseMSat: lnwire.MilliSatoshi( - hopHint.FeeBaseMSat, - ), - FeeProportionalMillionths: lnwire.MilliSatoshi( - hopHint.FeeProportionalMillionths, - ), - TimeLockDelta: hopHint.CLTVExpiryDelta, - } - - v := route.NewVertex(hopHint.NodeID) - edges[v] = append(edges[v], edge) - } - } - - // We'll also obtain a set of bandwidthHints from the lower layer for - // each of our outbound channels. This will allow the path finding to - // skip any links that aren't active or just don't have enough - // bandwidth to carry the payment. - sourceNode, err := m.graph.SourceNode() - if err != nil { - return nil, err - } - bandwidthHints, err := generateBandwidthHints( - sourceNode, m.queryBandwidth, - ) - if err != nil { - return nil, err - } - - return &paymentSession{ - additionalEdges: edges, - bandwidthHints: bandwidthHints, - errFailedPolicyChans: make(map[nodeChannel]struct{}), - mc: m, - pathFinder: findPath, - }, nil -} - -// NewPaymentSessionForRoute creates a new paymentSession instance that is just -// used for failure reporting to missioncontrol. -func (m *MissionControl) NewPaymentSessionForRoute(preBuiltRoute *route.Route) PaymentSession { - return &paymentSession{ - errFailedPolicyChans: make(map[nodeChannel]struct{}), - mc: m, - preBuiltRoute: preBuiltRoute, - } -} - -// NewPaymentSessionEmpty creates a new paymentSession instance that is empty, -// and will be exhausted immediately. Used for failure reporting to -// missioncontrol for resumed payment we don't want to make more attempts for. -func (m *MissionControl) NewPaymentSessionEmpty() PaymentSession { - return &paymentSession{ - errFailedPolicyChans: make(map[nodeChannel]struct{}), - mc: m, - preBuiltRoute: &route.Route{}, - preBuiltRouteTried: true, + history: make(map[route.Vertex]*nodeHistory), + now: time.Now, + cfg: cfg, } } @@ -312,9 +179,9 @@ func (m *MissionControl) ResetHistory() { log.Debugf("Mission control history cleared") } -// getEdgeProbability is expected to return the success probability of a payment +// GetEdgeProbability is expected to return the success probability of a payment // from fromNode along edge. -func (m *MissionControl) getEdgeProbability(fromNode route.Vertex, +func (m *MissionControl) GetEdgeProbability(fromNode route.Vertex, edge EdgeLocator, amt lnwire.MilliSatoshi) float64 { m.Lock() @@ -391,8 +258,8 @@ func (m *MissionControl) createHistoryIfNotExists(vertex route.Vertex) *nodeHist return node } -// reportVertexFailure reports a node level failure. -func (m *MissionControl) reportVertexFailure(v route.Vertex) { +// ReportVertexFailure reports a node level failure. +func (m *MissionControl) ReportVertexFailure(v route.Vertex) { log.Debugf("Reporting vertex %v failure to Mission Control", v) now := m.now() @@ -404,10 +271,10 @@ func (m *MissionControl) reportVertexFailure(v route.Vertex) { history.lastFail = &now } -// reportEdgeFailure reports a channel level failure. +// ReportEdgeFailure reports a channel level failure. // // TODO(roasbeef): also add value attempted to send and capacity of channel -func (m *MissionControl) reportEdgeFailure(failedEdge edge, +func (m *MissionControl) ReportEdgeFailure(failedEdge edge, minPenalizeAmt lnwire.MilliSatoshi) { log.Debugf("Reporting channel %v failure to Mission Control", diff --git a/routing/missioncontrol_test.go b/routing/missioncontrol_test.go index 72c3b07a..f6f658b8 100644 --- a/routing/missioncontrol_test.go +++ b/routing/missioncontrol_test.go @@ -29,7 +29,7 @@ func createMcTestContext(t *testing.T) *mcTestContext { } mc := NewMissionControl( - nil, nil, nil, &MissionControlConfig{ + &MissionControlConfig{ PenaltyHalfLife: 30 * time.Minute, AprioriHopProbability: 0.8, }, @@ -47,7 +47,7 @@ func (ctx *mcTestContext) expectP(amt lnwire.MilliSatoshi, ctx.t.Helper() - p := ctx.mc.getEdgeProbability(mcTestNode, mcTestEdge, amt) + p := ctx.mc.GetEdgeProbability(mcTestNode, mcTestEdge, amt) if p != expected { ctx.t.Fatalf("unexpected probability %v", p) } @@ -70,7 +70,7 @@ func TestMissionControl(t *testing.T) { ctx.expectP(1000, 0.8) // Expect probability to be zero after reporting the edge as failed. - ctx.mc.reportEdgeFailure(testEdge, 1000) + ctx.mc.ReportEdgeFailure(testEdge, 1000) ctx.expectP(1000, 0) // As we reported with a min penalization amt, a lower amt than reported @@ -83,7 +83,7 @@ func TestMissionControl(t *testing.T) { // Edge fails again, this time without a min penalization amt. The edge // should be penalized regardless of amount. - ctx.mc.reportEdgeFailure(testEdge, 0) + ctx.mc.ReportEdgeFailure(testEdge, 0) ctx.expectP(1000, 0) ctx.expectP(500, 0) @@ -93,7 +93,7 @@ func TestMissionControl(t *testing.T) { // A node level failure should bring probability of every channel back // to zero. - ctx.mc.reportVertexFailure(testNode) + ctx.mc.ReportVertexFailure(testNode) ctx.expectP(1000, 0) // Check whether history snapshot looks sane. diff --git a/routing/mock_test.go b/routing/mock_test.go index f2f7de78..cf0a3f3b 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -93,6 +93,23 @@ func (m *mockPaymentSessionSource) NewPaymentSessionEmpty() PaymentSession { return &mockPaymentSession{} } +type mockMissionControl struct { +} + +var _ MissionController = (*mockMissionControl)(nil) + +func (m *mockMissionControl) ReportEdgeFailure(failedEdge edge, + minPenalizeAmt lnwire.MilliSatoshi) { +} + +func (m *mockMissionControl) ReportVertexFailure(v route.Vertex) {} + +func (m *mockMissionControl) GetEdgeProbability(fromNode route.Vertex, edge EdgeLocator, + amt lnwire.MilliSatoshi) float64 { + + return 0 +} + type mockPaymentSession struct { routes []*route.Route } diff --git a/routing/payment_session.go b/routing/payment_session.go index 7508e30a..4a2c0a5b 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -58,7 +58,7 @@ type paymentSession struct { // require pruning, but any subsequent ones do. errFailedPolicyChans map[nodeChannel]struct{} - mc *MissionControl + sessionSource *SessionSource preBuiltRoute *route.Route preBuiltRouteTried bool @@ -78,7 +78,7 @@ var _ PaymentSession = (*paymentSession)(nil) // // NOTE: Part of the PaymentSession interface. func (p *paymentSession) ReportVertexFailure(v route.Vertex) { - p.mc.reportVertexFailure(v) + p.sessionSource.MissionControl.ReportVertexFailure(v) } // ReportEdgeFailure adds a channel to the graph prune view. The time the @@ -93,7 +93,9 @@ func (p *paymentSession) ReportVertexFailure(v route.Vertex) { func (p *paymentSession) ReportEdgeFailure(failedEdge edge, minPenalizeAmt lnwire.MilliSatoshi) { - p.mc.reportEdgeFailure(failedEdge, minPenalizeAmt) + p.sessionSource.MissionControl.ReportEdgeFailure( + failedEdge, minPenalizeAmt, + ) } // ReportEdgePolicyFailure handles a failure message that relates to a @@ -169,21 +171,24 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment, // Taking into account this prune view, we'll attempt to locate a path // to our destination, respecting the recommendations from // MissionControl. + ss := p.sessionSource + + restrictions := &RestrictParams{ + ProbabilitySource: ss.MissionControl.GetEdgeProbability, + FeeLimit: payment.FeeLimit, + OutgoingChannelID: payment.OutgoingChannelID, + CltvLimit: cltvLimit, + PaymentAttemptPenalty: ss.PaymentAttemptPenalty, + MinProbability: ss.MinRouteProbability, + } + path, err := p.pathFinder( &graphParams{ - graph: p.mc.graph, + graph: ss.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, + restrictions, ss.SelfNode.PubKeyBytes, payment.Target, payment.Amount, ) if err != nil { @@ -192,7 +197,7 @@ 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 := route.Vertex(p.mc.selfNode.PubKeyBytes) + sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes) route, err := newRoute( payment.Amount, sourceVertex, path, height, finalCltvDelta, ) diff --git a/routing/payment_session_source.go b/routing/payment_session_source.go new file mode 100644 index 00000000..69738026 --- /dev/null +++ b/routing/payment_session_source.go @@ -0,0 +1,150 @@ +package routing + +import ( + "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/zpay32" +) + +// A compile time assertion to ensure MissionControl meets the +// PaymentSessionSource interface. +var _ PaymentSessionSource = (*SessionSource)(nil) + +// SessionSource defines a source for the router to retrieve new payment +// sessions. +type SessionSource struct { + // Graph is the channel graph that will be used to gather metrics from + // and also to carry out path finding queries. + Graph *channeldb.ChannelGraph + + // QueryBandwidth is a method that allows querying the lower link layer + // to determine the up to date available bandwidth at a prospective link + // to be traversed. If the link isn't available, then a value of zero + // should be returned. Otherwise, the current up to date knowledge of + // the available bandwidth of the link should be returned. + QueryBandwidth func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi + + // SelfNode is our own node. + SelfNode *channeldb.LightningNode + + // MissionControl is a shared memory of sorts that executions of payment + // path finding use in order to remember which vertexes/edges were + // pruned from prior attempts. During payment execution, errors sent by + // nodes are mapped into a vertex or edge to be pruned. Each run will + // then take into account this set of pruned vertexes/edges to reduce + // route failure and pass on graph information gained to the next + // execution. + MissionControl MissionController + + // PaymentAttemptPenalty is the virtual cost in path finding weight + // units of executing a payment attempt that fails. It is used to trade + // off potentially better routes against their probability of + // succeeding. + PaymentAttemptPenalty lnwire.MilliSatoshi + + // MinProbability defines the minimum success probability of the + // returned route. + MinRouteProbability float64 +} + +// NewPaymentSession creates a new payment session backed by the latest prune +// view from Mission Control. An optional set of routing hints can be provided +// in order to populate additional edges to explore when finding a path to the +// payment's destination. +func (m *SessionSource) NewPaymentSession(routeHints [][]zpay32.HopHint, + target route.Vertex) (PaymentSession, error) { + + edges := make(map[route.Vertex][]*channeldb.ChannelEdgePolicy) + + // Traverse through all of the available hop hints and include them in + // our edges map, indexed by the public key of the channel's starting + // node. + for _, routeHint := range routeHints { + // If multiple hop hints are provided within a single route + // hint, we'll assume they must be chained together and sorted + // in forward order in order to reach the target successfully. + for i, hopHint := range routeHint { + // In order to determine the end node of this hint, + // we'll need to look at the next hint's start node. If + // we've reached the end of the hints list, we can + // assume we've reached the destination. + endNode := &channeldb.LightningNode{} + if i != len(routeHint)-1 { + endNode.AddPubKey(routeHint[i+1].NodeID) + } else { + targetPubKey, err := btcec.ParsePubKey( + target[:], btcec.S256(), + ) + if err != nil { + return nil, err + } + endNode.AddPubKey(targetPubKey) + } + + // Finally, create the channel edge from the hop hint + // and add it to list of edges corresponding to the node + // at the start of the channel. + edge := &channeldb.ChannelEdgePolicy{ + Node: endNode, + ChannelID: hopHint.ChannelID, + FeeBaseMSat: lnwire.MilliSatoshi( + hopHint.FeeBaseMSat, + ), + FeeProportionalMillionths: lnwire.MilliSatoshi( + hopHint.FeeProportionalMillionths, + ), + TimeLockDelta: hopHint.CLTVExpiryDelta, + } + + v := route.NewVertex(hopHint.NodeID) + edges[v] = append(edges[v], edge) + } + } + + // We'll also obtain a set of bandwidthHints from the lower layer for + // each of our outbound channels. This will allow the path finding to + // skip any links that aren't active or just don't have enough + // bandwidth to carry the payment. + sourceNode, err := m.Graph.SourceNode() + if err != nil { + return nil, err + } + bandwidthHints, err := generateBandwidthHints( + sourceNode, m.QueryBandwidth, + ) + if err != nil { + return nil, err + } + + return &paymentSession{ + additionalEdges: edges, + bandwidthHints: bandwidthHints, + errFailedPolicyChans: make(map[nodeChannel]struct{}), + sessionSource: m, + pathFinder: findPath, + }, nil +} + +// NewPaymentSessionForRoute creates a new paymentSession instance that is just +// used for failure reporting to missioncontrol. +func (m *SessionSource) NewPaymentSessionForRoute(preBuiltRoute *route.Route) PaymentSession { + return &paymentSession{ + errFailedPolicyChans: make(map[nodeChannel]struct{}), + sessionSource: m, + preBuiltRoute: preBuiltRoute, + } +} + +// NewPaymentSessionEmpty creates a new paymentSession instance that is empty, +// and will be exhausted immediately. Used for failure reporting to +// missioncontrol for resumed payment we don't want to make more attempts for. +func (m *SessionSource) NewPaymentSessionEmpty() PaymentSession { + return &paymentSession{ + errFailedPolicyChans: make(map[nodeChannel]struct{}), + sessionSource: m, + preBuiltRoute: &route.Route{}, + preBuiltRouteTried: true, + } +} diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index a5ee7889..33070fbd 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -32,12 +32,16 @@ func TestRequestRoute(t *testing.T) { return path, nil } - session := &paymentSession{ - mc: &MissionControl{ - selfNode: &channeldb.LightningNode{}, - cfg: &MissionControlConfig{}, + sessionSource := &SessionSource{ + SelfNode: &channeldb.LightningNode{}, + MissionControl: &MissionControl{ + cfg: &MissionControlConfig{}, }, - pathFinder: findPath, + } + + session := &paymentSession{ + sessionSource: sessionSource, + pathFinder: findPath, } cltvLimit := uint32(30) diff --git a/routing/router.go b/routing/router.go index 87bb5c75..80cf4753 100644 --- a/routing/router.go +++ b/routing/router.go @@ -171,6 +171,22 @@ type PaymentSessionSource interface { NewPaymentSessionEmpty() PaymentSession } +// MissionController is an interface that exposes failure reporting and +// probability estimation. +type MissionController interface { + // ReportEdgeFailure reports a channel level failure. + ReportEdgeFailure(failedEdge edge, + minPenalizeAmt lnwire.MilliSatoshi) + + // ReportVertexFailure reports a node level failure. + ReportVertexFailure(v route.Vertex) + + // GetEdgeProbability is expected to return the success probability of a + // payment from fromNode along edge. + GetEdgeProbability(fromNode route.Vertex, edge EdgeLocator, + amt lnwire.MilliSatoshi) float64 +} + // FeeSchema is the set fee configuration for a Lightning Node on the network. // Using the coefficients described within the schema, the required fee to // forward outgoing payments can be derived. @@ -234,7 +250,11 @@ type Config struct { // Each run will then take into account this set of pruned // vertexes/edges to reduce route failure and pass on graph information // gained to the next execution. - MissionControl PaymentSessionSource + MissionControl MissionController + + // SessionSource defines a source for the router to retrieve new payment + // sessions. + SessionSource PaymentSessionSource // ChannelPruneExpiry is the duration used to determine if a channel // should be pruned or not. If the delta between now and when the @@ -544,7 +564,7 @@ func (r *ChannelRouter) Start() error { // // PayAttemptTime doesn't need to be set, as there is // only a single attempt. - paySession := r.cfg.MissionControl.NewPaymentSessionEmpty() + paySession := r.cfg.SessionSource.NewPaymentSessionEmpty() lPayment := &LightningPayment{ PaymentHash: payment.Info.PaymentHash, @@ -1651,7 +1671,7 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) ( // Before starting the HTLC routing attempt, we'll create a fresh // payment session which will report our errors back to mission // control. - paySession, err := r.cfg.MissionControl.NewPaymentSession( + paySession, err := r.cfg.SessionSource.NewPaymentSession( payment.RouteHints, payment.Target, ) if err != nil { @@ -1682,7 +1702,7 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, route *route.Route) ( lntypes.Preimage, error) { // Create a payment session for just this route. - paySession := r.cfg.MissionControl.NewPaymentSessionForRoute(route) + paySession := r.cfg.SessionSource.NewPaymentSessionForRoute(route) // Calculate amount paid to receiver. amt := route.TotalAmount - route.TotalFees() diff --git a/routing/router_test.go b/routing/router_test.go index c50dba1c..bc71cfc9 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -91,17 +91,23 @@ func createTestCtxFromGraphInstance(startingHeight uint32, graphInstance *testGr } mc := NewMissionControl( - graphInstance.graph, selfNode, - func(e *channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi { - return lnwire.NewMSatFromSatoshis(e.Capacity) - }, &MissionControlConfig{ - MinRouteProbability: 0.01, - PaymentAttemptPenalty: 100, PenaltyHalfLife: time.Hour, AprioriHopProbability: 0.9, }, ) + + sessionSource := &SessionSource{ + Graph: graphInstance.graph, + SelfNode: selfNode, + QueryBandwidth: func(e *channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi { + return lnwire.NewMSatFromSatoshis(e.Capacity) + }, + MinRouteProbability: 0.01, + PaymentAttemptPenalty: 100, + MissionControl: mc, + } + router, err := New(Config{ Graph: graphInstance.graph, Chain: chain, @@ -109,6 +115,7 @@ func createTestCtxFromGraphInstance(startingHeight uint32, graphInstance *testGr Payer: &mockPaymentAttemptDispatcher{}, Control: makeMockControlTower(), MissionControl: mc, + SessionSource: sessionSource, ChannelPruneExpiry: time.Hour * 24, GraphPruneInterval: time.Hour * 2, QueryBandwidth: func(e *channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi { @@ -2940,7 +2947,7 @@ func TestRouterPaymentStateMachine(t *testing.T) { Chain: chain, ChainView: chainView, Control: control, - MissionControl: &mockPaymentSessionSource{}, + SessionSource: &mockPaymentSessionSource{}, Payer: payer, ChannelPruneExpiry: time.Hour * 24, GraphPruneInterval: time.Hour * 2, @@ -3004,10 +3011,12 @@ func TestRouterPaymentStateMachine(t *testing.T) { copy(preImage[:], bytes.Repeat([]byte{9}, 32)) - router.cfg.MissionControl = &mockPaymentSessionSource{ + router.cfg.SessionSource = &mockPaymentSessionSource{ routes: test.routes, } + router.cfg.MissionControl = &mockMissionControl{} + // Send the payment. Since this is new payment hash, the // information should be registered with the ControlTower. paymentResult := make(chan error) diff --git a/server.go b/server.go index 88204d81..64b37666 100644 --- a/server.go +++ b/server.go @@ -652,11 +652,29 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, // // TODO(joostjager): When we are further in the process of moving to sub // servers, the mission control instance itself can be moved there too. + routingConfig := routerrpc.GetRoutingConfig(cfg.SubRPCServers.RouterRPC) + s.missionControl = routing.NewMissionControl( - chanGraph, selfNode, queryBandwidth, - routerrpc.GetMissionControlConfig(cfg.SubRPCServers.RouterRPC), + &routing.MissionControlConfig{ + AprioriHopProbability: routingConfig.AprioriHopProbability, + PenaltyHalfLife: routingConfig.PenaltyHalfLife, + }, ) + srvrLog.Debugf("Instantiating payment session source with config: "+ + "PaymentAttemptPenalty=%v, MinRouteProbability=%v", + int64(routingConfig.PaymentAttemptPenalty.ToSatoshis()), + routingConfig.MinRouteProbability) + + paymentSessionSource := &routing.SessionSource{ + Graph: chanGraph, + MissionControl: s.missionControl, + QueryBandwidth: queryBandwidth, + SelfNode: selfNode, + PaymentAttemptPenalty: routingConfig.PaymentAttemptPenalty, + MinRouteProbability: routingConfig.MinRouteProbability, + } + paymentControl := channeldb.NewPaymentControl(chanDB) s.controlTower = routing.NewControlTower(paymentControl) @@ -668,6 +686,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, Payer: s.htlcSwitch, Control: s.controlTower, MissionControl: s.missionControl, + SessionSource: paymentSessionSource, ChannelPruneExpiry: routing.DefaultChannelPruneExpiry, GraphPruneInterval: time.Duration(time.Hour), QueryBandwidth: queryBandwidth,