mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-12-09 20:33:45 +01:00
lnd+routing+rpc: switch mc to (external) estimator
We use a more general `Estimator` interface for probability estimation in missioncontrol. The estimator is created outside of `NewMissionControl`, passed in as a `MissionControlConfig` field, to facilitate usage of externally supplied estimators.
This commit is contained in:
@@ -465,6 +465,9 @@ type Config struct {
|
|||||||
|
|
||||||
// ActiveNetParams contains parameters of the target chain.
|
// ActiveNetParams contains parameters of the target chain.
|
||||||
ActiveNetParams chainreg.BitcoinNetParams
|
ActiveNetParams chainreg.BitcoinNetParams
|
||||||
|
|
||||||
|
// Estimator is used to estimate routing probabilities.
|
||||||
|
Estimator routing.Estimator
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig returns all default values for the Config struct.
|
// DefaultConfig returns all default values for the Config struct.
|
||||||
|
|||||||
@@ -455,13 +455,23 @@ func (s *Server) GetMissionControlConfig(ctx context.Context,
|
|||||||
error) {
|
error) {
|
||||||
|
|
||||||
cfg := s.cfg.RouterBackend.MissionControl.GetConfig()
|
cfg := s.cfg.RouterBackend.MissionControl.GetConfig()
|
||||||
|
eCfg, ok := cfg.Estimator.Config().(*routing.AprioriConfig)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown estimator config type")
|
||||||
|
}
|
||||||
|
|
||||||
return &GetMissionControlConfigResponse{
|
return &GetMissionControlConfigResponse{
|
||||||
Config: &MissionControlConfig{
|
Config: &MissionControlConfig{
|
||||||
HalfLifeSeconds: uint64(cfg.PenaltyHalfLife.Seconds()),
|
HalfLifeSeconds: uint64(
|
||||||
HopProbability: float32(cfg.AprioriHopProbability),
|
eCfg.PenaltyHalfLife.Seconds()),
|
||||||
Weight: float32(cfg.AprioriWeight),
|
HopProbability: float32(
|
||||||
MaximumPaymentResults: uint32(cfg.MaxMcHistory),
|
eCfg.AprioriHopProbability,
|
||||||
MinimumFailureRelaxInterval: uint64(cfg.MinFailureRelaxInterval.Seconds()),
|
),
|
||||||
|
Weight: float32(eCfg.AprioriWeight),
|
||||||
|
MaximumPaymentResults: uint32(cfg.MaxMcHistory),
|
||||||
|
MinimumFailureRelaxInterval: uint64(
|
||||||
|
cfg.MinFailureRelaxInterval.Seconds(),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -471,14 +481,20 @@ func (s *Server) SetMissionControlConfig(ctx context.Context,
|
|||||||
req *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse,
|
req *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse,
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
|
aCfg := routing.AprioriConfig{
|
||||||
|
PenaltyHalfLife: time.Duration(
|
||||||
|
req.Config.HalfLifeSeconds,
|
||||||
|
) * time.Second,
|
||||||
|
AprioriHopProbability: float64(req.Config.HopProbability),
|
||||||
|
AprioriWeight: float64(req.Config.Weight),
|
||||||
|
}
|
||||||
|
estimator, err := routing.NewAprioriEstimator(aCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
cfg := &routing.MissionControlConfig{
|
cfg := &routing.MissionControlConfig{
|
||||||
AprioriConfig: routing.AprioriConfig{
|
Estimator: estimator,
|
||||||
PenaltyHalfLife: time.Duration(
|
|
||||||
req.Config.HalfLifeSeconds,
|
|
||||||
) * time.Second,
|
|
||||||
AprioriHopProbability: float64(req.Config.HopProbability),
|
|
||||||
AprioriWeight: float64(req.Config.Weight),
|
|
||||||
},
|
|
||||||
MaxMcHistory: int(req.Config.MaximumPaymentResults),
|
MaxMcHistory: int(req.Config.MaximumPaymentResults),
|
||||||
MinFailureRelaxInterval: time.Duration(
|
MinFailureRelaxInterval: time.Duration(
|
||||||
req.Config.MinimumFailureRelaxInterval,
|
req.Config.MinimumFailureRelaxInterval,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -68,6 +69,14 @@ func newIntegratedRoutingContext(t *testing.T) *integratedRoutingContext {
|
|||||||
// defaults would break the unit tests. The actual values picked aren't
|
// defaults would break the unit tests. The actual values picked aren't
|
||||||
// critical to excite certain behavior, but do need to be aligned with
|
// critical to excite certain behavior, but do need to be aligned with
|
||||||
// the test case assertions.
|
// the test case assertions.
|
||||||
|
aCfg := AprioriConfig{
|
||||||
|
PenaltyHalfLife: 30 * time.Minute,
|
||||||
|
AprioriHopProbability: 0.6,
|
||||||
|
AprioriWeight: 0.5,
|
||||||
|
}
|
||||||
|
estimator, err := NewAprioriEstimator(aCfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
ctx := integratedRoutingContext{
|
ctx := integratedRoutingContext{
|
||||||
t: t,
|
t: t,
|
||||||
graph: graph,
|
graph: graph,
|
||||||
@@ -75,11 +84,7 @@ func newIntegratedRoutingContext(t *testing.T) *integratedRoutingContext {
|
|||||||
finalExpiry: 40,
|
finalExpiry: 40,
|
||||||
|
|
||||||
mcCfg: MissionControlConfig{
|
mcCfg: MissionControlConfig{
|
||||||
AprioriConfig: AprioriConfig{
|
Estimator: estimator,
|
||||||
PenaltyHalfLife: 30 * time.Minute,
|
|
||||||
AprioriHopProbability: 0.6,
|
|
||||||
AprioriWeight: 0.5,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
pathFindingCfg: PathFindingConfig{
|
pathFindingCfg: PathFindingConfig{
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ func TestProbabilityExtrapolation(t *testing.T) {
|
|||||||
// If we use a static value for the node probability (no extrapolation
|
// If we use a static value for the node probability (no extrapolation
|
||||||
// of data from other channels), all ten bad channels will be tried
|
// of data from other channels), all ten bad channels will be tried
|
||||||
// first before switching to the paid channel.
|
// first before switching to the paid channel.
|
||||||
ctx.mcCfg.AprioriWeight = 1
|
estimator, ok := ctx.mcCfg.Estimator.(*AprioriEstimator)
|
||||||
|
if ok {
|
||||||
|
estimator.AprioriWeight = 1
|
||||||
|
}
|
||||||
attempts, err = ctx.testPayment(1)
|
attempts, err = ctx.testPayment(1)
|
||||||
require.NoError(t, err, "payment failed")
|
require.NoError(t, err, "payment failed")
|
||||||
if len(attempts) != 11 {
|
if len(attempts) != 11 {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ type MissionControl struct {
|
|||||||
|
|
||||||
// estimator is the probability estimator that is used with the payment
|
// estimator is the probability estimator that is used with the payment
|
||||||
// results that mission control collects.
|
// results that mission control collects.
|
||||||
estimator *AprioriEstimator
|
estimator Estimator
|
||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
@@ -116,8 +116,8 @@ type MissionControl struct {
|
|||||||
// MissionControlConfig defines parameters that control mission control
|
// MissionControlConfig defines parameters that control mission control
|
||||||
// behaviour.
|
// behaviour.
|
||||||
type MissionControlConfig struct {
|
type MissionControlConfig struct {
|
||||||
// AprioriConfig is the config we will use for probability calculations.
|
// Estimator gives probability estimates for node pairs.
|
||||||
AprioriConfig
|
Estimator Estimator
|
||||||
|
|
||||||
// MaxMcHistory defines the maximum number of payment results that are
|
// MaxMcHistory defines the maximum number of payment results that are
|
||||||
// held on disk.
|
// held on disk.
|
||||||
@@ -134,10 +134,6 @@ type MissionControlConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *MissionControlConfig) validate() error {
|
func (c *MissionControlConfig) validate() error {
|
||||||
if err := c.AprioriConfig.validate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.MaxMcHistory < 0 {
|
if c.MaxMcHistory < 0 {
|
||||||
return ErrInvalidMcHistory
|
return ErrInvalidMcHistory
|
||||||
}
|
}
|
||||||
@@ -151,11 +147,8 @@ func (c *MissionControlConfig) validate() error {
|
|||||||
|
|
||||||
// String returns a string representation of a mission control config.
|
// String returns a string representation of a mission control config.
|
||||||
func (c *MissionControlConfig) String() string {
|
func (c *MissionControlConfig) String() string {
|
||||||
return fmt.Sprintf("Penalty Half Life: %v, Apriori Hop "+
|
return fmt.Sprintf("maximum history: %v, minimum failure relax "+
|
||||||
"Probablity: %v, Maximum History: %v, Apriori Weight: %v, "+
|
"interval: %v", c.MaxMcHistory, c.MinFailureRelaxInterval)
|
||||||
"Minimum Failure Relax Interval: %v", c.PenaltyHalfLife,
|
|
||||||
c.AprioriHopProbability, c.MaxMcHistory, c.AprioriWeight,
|
|
||||||
c.MinFailureRelaxInterval)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimedPairResult describes a timestamped pair result.
|
// TimedPairResult describes a timestamped pair result.
|
||||||
@@ -211,7 +204,8 @@ type paymentResult struct {
|
|||||||
func NewMissionControl(db kvdb.Backend, self route.Vertex,
|
func NewMissionControl(db kvdb.Backend, self route.Vertex,
|
||||||
cfg *MissionControlConfig) (*MissionControl, error) {
|
cfg *MissionControlConfig) (*MissionControl, error) {
|
||||||
|
|
||||||
log.Debugf("Instantiating mission control with config: %v", cfg)
|
log.Debugf("Instantiating mission control with config: %v, %v", cfg,
|
||||||
|
cfg.Estimator)
|
||||||
|
|
||||||
if err := cfg.validate(); err != nil {
|
if err := cfg.validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -224,17 +218,12 @@ func NewMissionControl(db kvdb.Backend, self route.Vertex,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
estimator := &AprioriEstimator{
|
|
||||||
AprioriConfig: cfg.AprioriConfig,
|
|
||||||
prevSuccessProbability: prevSuccessProbability,
|
|
||||||
}
|
|
||||||
|
|
||||||
mc := &MissionControl{
|
mc := &MissionControl{
|
||||||
state: newMissionControlState(cfg.MinFailureRelaxInterval),
|
state: newMissionControlState(cfg.MinFailureRelaxInterval),
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
selfNode: self,
|
selfNode: self,
|
||||||
store: store,
|
store: store,
|
||||||
estimator: estimator,
|
estimator: cfg.Estimator,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mc.init(); err != nil {
|
if err := mc.init(); err != nil {
|
||||||
@@ -283,7 +272,7 @@ func (m *MissionControl) GetConfig() *MissionControlConfig {
|
|||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
return &MissionControlConfig{
|
return &MissionControlConfig{
|
||||||
AprioriConfig: m.estimator.AprioriConfig,
|
Estimator: m.estimator,
|
||||||
MaxMcHistory: m.store.maxRecords,
|
MaxMcHistory: m.store.maxRecords,
|
||||||
McFlushInterval: m.store.flushInterval,
|
McFlushInterval: m.store.flushInterval,
|
||||||
MinFailureRelaxInterval: m.state.minFailureRelaxInterval,
|
MinFailureRelaxInterval: m.state.minFailureRelaxInterval,
|
||||||
@@ -304,11 +293,12 @@ func (m *MissionControl) SetConfig(cfg *MissionControlConfig) error {
|
|||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
log.Infof("Updating mission control cfg: %v", cfg)
|
log.Infof("Active mission control cfg: %v, estimator: %v", cfg,
|
||||||
|
cfg.Estimator)
|
||||||
|
|
||||||
m.store.maxRecords = cfg.MaxMcHistory
|
m.store.maxRecords = cfg.MaxMcHistory
|
||||||
m.state.minFailureRelaxInterval = cfg.MinFailureRelaxInterval
|
m.state.minFailureRelaxInterval = cfg.MinFailureRelaxInterval
|
||||||
m.estimator.AprioriConfig = cfg.AprioriConfig
|
m.estimator = cfg.Estimator
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,15 +90,17 @@ func (ctx *mcTestContext) restartMc() {
|
|||||||
require.NoError(ctx.t, ctx.mc.store.storeResults())
|
require.NoError(ctx.t, ctx.mc.store.storeResults())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aCfg := AprioriConfig{
|
||||||
|
PenaltyHalfLife: testPenaltyHalfLife,
|
||||||
|
AprioriHopProbability: testAprioriHopProbability,
|
||||||
|
AprioriWeight: testAprioriWeight,
|
||||||
|
}
|
||||||
|
estimator, err := NewAprioriEstimator(aCfg)
|
||||||
|
require.NoError(ctx.t, err)
|
||||||
|
|
||||||
mc, err := NewMissionControl(
|
mc, err := NewMissionControl(
|
||||||
ctx.db, mcTestSelf,
|
ctx.db, mcTestSelf,
|
||||||
&MissionControlConfig{
|
&MissionControlConfig{Estimator: estimator},
|
||||||
AprioriConfig: AprioriConfig{
|
|
||||||
PenaltyHalfLife: testPenaltyHalfLife,
|
|
||||||
AprioriHopProbability: testAprioriHopProbability,
|
|
||||||
AprioriWeight: testAprioriWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.t.Fatal(err)
|
ctx.t.Fatal(err)
|
||||||
|
|||||||
@@ -119,13 +119,15 @@ func createTestCtxFromGraphInstanceAssumeValid(t *testing.T,
|
|||||||
AttemptCost: 100,
|
AttemptCost: 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcConfig := &MissionControlConfig{
|
aCfg := AprioriConfig{
|
||||||
AprioriConfig: AprioriConfig{
|
PenaltyHalfLife: time.Hour,
|
||||||
PenaltyHalfLife: time.Hour,
|
AprioriHopProbability: 0.9,
|
||||||
AprioriHopProbability: 0.9,
|
AprioriWeight: 0.5,
|
||||||
AprioriWeight: 0.5,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
estimator, err := NewAprioriEstimator(aCfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mcConfig := &MissionControlConfig{Estimator: estimator}
|
||||||
|
|
||||||
mc, err := NewMissionControl(
|
mc, err := NewMissionControl(
|
||||||
graphInstance.graphBackend, route.Vertex{}, mcConfig,
|
graphInstance.graphBackend, route.Vertex{}, mcConfig,
|
||||||
|
|||||||
33
server.go
33
server.go
@@ -868,20 +868,31 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
// servers, the mission control instance itself can be moved there too.
|
// servers, the mission control instance itself can be moved there too.
|
||||||
routingConfig := routerrpc.GetRoutingConfig(cfg.SubRPCServers.RouterRPC)
|
routingConfig := routerrpc.GetRoutingConfig(cfg.SubRPCServers.RouterRPC)
|
||||||
|
|
||||||
estimatorCfg := routing.AprioriConfig{
|
// We only initialize a probability estimator if there's no custom one.
|
||||||
AprioriHopProbability: routingConfig.AprioriHopProbability,
|
var estimator routing.Estimator
|
||||||
PenaltyHalfLife: routingConfig.PenaltyHalfLife,
|
if cfg.Estimator != nil {
|
||||||
AprioriWeight: routingConfig.AprioriWeight,
|
estimator = cfg.Estimator
|
||||||
|
} else {
|
||||||
|
aCfg := routing.AprioriConfig{
|
||||||
|
AprioriHopProbability: routingConfig.
|
||||||
|
AprioriHopProbability,
|
||||||
|
PenaltyHalfLife: routingConfig.PenaltyHalfLife,
|
||||||
|
AprioriWeight: routingConfig.AprioriWeight,
|
||||||
|
}
|
||||||
|
estimator, err = routing.NewAprioriEstimator(aCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mcCfg := &routing.MissionControlConfig{
|
||||||
|
Estimator: estimator,
|
||||||
|
MaxMcHistory: routingConfig.MaxMcHistory,
|
||||||
|
McFlushInterval: routingConfig.McFlushInterval,
|
||||||
|
MinFailureRelaxInterval: routing.DefaultMinFailureRelaxInterval,
|
||||||
|
}
|
||||||
s.missionControl, err = routing.NewMissionControl(
|
s.missionControl, err = routing.NewMissionControl(
|
||||||
dbs.ChanStateDB, selfNode.PubKeyBytes,
|
dbs.ChanStateDB, selfNode.PubKeyBytes, mcCfg,
|
||||||
&routing.MissionControlConfig{
|
|
||||||
AprioriConfig: estimatorCfg,
|
|
||||||
MaxMcHistory: routingConfig.MaxMcHistory,
|
|
||||||
McFlushInterval: routingConfig.McFlushInterval,
|
|
||||||
MinFailureRelaxInterval: routing.DefaultMinFailureRelaxInterval,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create mission control: %v", err)
|
return nil, fmt.Errorf("can't create mission control: %v", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user