routing+routerrpc: expose mission control parameters in lnd config
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.
This commit is contained in:
10 files changed
+145
-32
No files matched your search
+44
-20
@@ -14,14 +14,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultPenaltyHalfLife is the default half-life duration. The
|
||||
// DefaultPenaltyHalfLife is the default half-life duration. The
|
||||
// half-life duration defines after how much time a penalized node or
|
||||
// channel is back at 50% probability.
|
||||
defaultPenaltyHalfLife = time.Hour
|
||||
|
||||
// aprioriHopProbability is the assumed success probability of a hop in
|
||||
// a route when no other information is available.
|
||||
aprioriHopProbability = 1
|
||||
DefaultPenaltyHalfLife = time.Hour
|
||||
)
|
||||
|
||||
// MissionControl contains state which summarizes the past attempts of HTLC
|
||||
@@ -46,9 +42,7 @@ type MissionControl struct {
|
||||
// external function to enable deterministic unit tests.
|
||||
now func() time.Time
|
||||
|
||||
// penaltyHalfLife defines after how much time a penalized node or
|
||||
// channel is back at 50% probability.
|
||||
penaltyHalfLife time.Duration
|
||||
cfg *MissionControlConfig
|
||||
|
||||
sync.Mutex
|
||||
|
||||
@@ -62,6 +56,28 @@ type MissionControl struct {
|
||||
// PaymentSessionSource interface.
|
||||
var _ PaymentSessionSource = (*MissionControl)(nil)
|
||||
|
||||
// MissionControlConfig defines parameters that control mission control
|
||||
// behaviour.
|
||||
type MissionControlConfig 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
|
||||
}
|
||||
|
||||
// nodeHistory contains a summary of payment attempt outcomes involving a
|
||||
// particular node.
|
||||
type nodeHistory struct {
|
||||
@@ -130,15 +146,23 @@ type MissionControlChannelSnapshot struct {
|
||||
//
|
||||
// TODO(roasbeef): persist memory
|
||||
func NewMissionControl(g *channeldb.ChannelGraph, selfNode *channeldb.LightningNode,
|
||||
qb func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi) *MissionControl {
|
||||
qb func(*channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi,
|
||||
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)
|
||||
|
||||
return &MissionControl{
|
||||
history: make(map[route.Vertex]*nodeHistory),
|
||||
selfNode: selfNode,
|
||||
queryBandwidth: qb,
|
||||
graph: g,
|
||||
now: time.Now,
|
||||
penaltyHalfLife: defaultPenaltyHalfLife,
|
||||
history: make(map[route.Vertex]*nodeHistory),
|
||||
selfNode: selfNode,
|
||||
queryBandwidth: qb,
|
||||
graph: g,
|
||||
now: time.Now,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +326,7 @@ func (m *MissionControl) getEdgeProbability(fromNode route.Vertex,
|
||||
// adjust this probability.
|
||||
nodeHistory, ok := m.history[fromNode]
|
||||
if !ok {
|
||||
return aprioriHopProbability
|
||||
return m.cfg.AprioriHopProbability
|
||||
}
|
||||
|
||||
return m.getEdgeProbabilityForNode(nodeHistory, edge.ChannelID, amt)
|
||||
@@ -337,7 +361,7 @@ func (m *MissionControl) getEdgeProbabilityForNode(nodeHistory *nodeHistory,
|
||||
}
|
||||
|
||||
if lastFailure == nil {
|
||||
return aprioriHopProbability
|
||||
return m.cfg.AprioriHopProbability
|
||||
}
|
||||
|
||||
timeSinceLastFailure := m.now().Sub(*lastFailure)
|
||||
@@ -346,8 +370,8 @@ func (m *MissionControl) getEdgeProbabilityForNode(nodeHistory *nodeHistory,
|
||||
// the probability down to zero when a failure occurs. From there it
|
||||
// recovers asymptotically back to the a priori probability. The rate at
|
||||
// which this happens is controlled by the penaltyHalfLife parameter.
|
||||
exp := -timeSinceLastFailure.Hours() / m.penaltyHalfLife.Hours()
|
||||
probability := aprioriHopProbability * (1 - math.Pow(2, exp))
|
||||
exp := -timeSinceLastFailure.Hours() / m.cfg.PenaltyHalfLife.Hours()
|
||||
probability := m.cfg.AprioriHopProbability * (1 - math.Pow(2, exp))
|
||||
|
||||
return probability
|
||||
}
|
||||
|
||||
@@ -12,9 +12,13 @@ import (
|
||||
func TestMissionControl(t *testing.T) {
|
||||
now := testTime
|
||||
|
||||
mc := NewMissionControl(nil, nil, nil)
|
||||
mc := NewMissionControl(
|
||||
nil, nil, nil, &MissionControlConfig{
|
||||
PenaltyHalfLife: 30 * time.Minute,
|
||||
AprioriHopProbability: 0.8,
|
||||
},
|
||||
)
|
||||
mc.now = func() time.Time { return now }
|
||||
mc.penaltyHalfLife = 30 * time.Minute
|
||||
|
||||
testTime := time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
||||
|
||||
@@ -36,7 +40,7 @@ func TestMissionControl(t *testing.T) {
|
||||
}
|
||||
|
||||
// Initial probability is expected to be 1.
|
||||
expectP(1000, 1)
|
||||
expectP(1000, 0.8)
|
||||
|
||||
// Expect probability to be zero after reporting the edge as failed.
|
||||
mc.reportEdgeFailure(testEdge, 1000)
|
||||
@@ -44,11 +48,11 @@ func TestMissionControl(t *testing.T) {
|
||||
|
||||
// As we reported with a min penalization amt, a lower amt than reported
|
||||
// should be unaffected.
|
||||
expectP(500, 1)
|
||||
expectP(500, 0.8)
|
||||
|
||||
// Edge decay started.
|
||||
now = testTime.Add(30 * time.Minute)
|
||||
expectP(1000, 0.5)
|
||||
expectP(1000, 0.4)
|
||||
|
||||
// Edge fails again, this time without a min penalization amt. The edge
|
||||
// should be penalized regardless of amount.
|
||||
@@ -58,7 +62,7 @@ func TestMissionControl(t *testing.T) {
|
||||
|
||||
// Edge decay started.
|
||||
now = testTime.Add(60 * time.Minute)
|
||||
expectP(1000, 0.5)
|
||||
expectP(1000, 0.4)
|
||||
|
||||
// A node level failure should bring probability of every channel back
|
||||
// to zero.
|
||||
|
||||
+6
-2
@@ -47,9 +47,13 @@ var (
|
||||
// succeeding.
|
||||
DefaultPaymentAttemptPenalty = lnwire.NewMSatFromSatoshis(100)
|
||||
|
||||
// DefaultMinProbability is the default minimum probability for routes
|
||||
// DefaultMinRouteProbability is the default minimum probability for routes
|
||||
// returned from findPath.
|
||||
DefaultMinProbability = float64(0.01)
|
||||
DefaultMinRouteProbability = float64(0.01)
|
||||
|
||||
// DefaultAprioriHopProbability is the default a priori probability for
|
||||
// a hop.
|
||||
DefaultAprioriHopProbability = float64(0.95)
|
||||
)
|
||||
|
||||
// edgePolicyWithSource is a helper struct to keep track of the source node
|
||||
|
||||
@@ -180,8 +180,8 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
||||
FeeLimit: payment.FeeLimit,
|
||||
OutgoingChannelID: payment.OutgoingChannelID,
|
||||
CltvLimit: cltvLimit,
|
||||
PaymentAttemptPenalty: DefaultPaymentAttemptPenalty,
|
||||
MinProbability: DefaultMinProbability,
|
||||
PaymentAttemptPenalty: p.mc.cfg.PaymentAttemptPenalty,
|
||||
MinProbability: p.mc.cfg.MinRouteProbability,
|
||||
},
|
||||
p.mc.selfNode.PubKeyBytes, payment.Target,
|
||||
payment.Amount,
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestRequestRoute(t *testing.T) {
|
||||
session := &paymentSession{
|
||||
mc: &MissionControl{
|
||||
selfNode: &channeldb.LightningNode{},
|
||||
cfg: &MissionControlConfig{},
|
||||
},
|
||||
pathFinder: findPath,
|
||||
}
|
||||
|
||||
@@ -95,6 +95,12 @@ func createTestCtxFromGraphInstance(startingHeight uint32, graphInstance *testGr
|
||||
func(e *channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi {
|
||||
return lnwire.NewMSatFromSatoshis(e.Capacity)
|
||||
},
|
||||
&MissionControlConfig{
|
||||
MinRouteProbability: 0.01,
|
||||
PaymentAttemptPenalty: 100,
|
||||
PenaltyHalfLife: time.Hour,
|
||||
AprioriHopProbability: 0.9,
|
||||
},
|
||||
)
|
||||
router, err := New(Config{
|
||||
Graph: graphInstance.graph,
|
||||
|
||||
Reference in new issue
Block a user