mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-15 11:21:03 +02:00
lncli+routerrpc: adapt mc api to dynamic estimator
The active probability estimator can be switched dynamically using the `Set/GetMissionControl` API, maintaining backward compatibility. The lncli commands `setmccfg` and `getmccfg` are updated around this functionality. Note that deprecated configuration parameters were removed from the commands.
This commit is contained in:
parent
58d5131e31
commit
2ccdfb1151
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@ -45,26 +46,12 @@ var setCfgCommand = cli.Command{
|
|||||||
Category: "Mission Control",
|
Category: "Mission Control",
|
||||||
Usage: "Set mission control's config.",
|
Usage: "Set mission control's config.",
|
||||||
Description: `
|
Description: `
|
||||||
Update the config values being used by mission control to calculate
|
Update the config values being used by mission control to calculate the
|
||||||
the probability that payment routes will succeed.
|
probability that payment routes will succeed. The estimator type must be
|
||||||
`,
|
provided to set estimator-related parameters.`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.DurationFlag{
|
// General settings.
|
||||||
Name: "halflife",
|
cli.UintFlag{
|
||||||
Usage: "the amount of time taken to restore a node " +
|
|
||||||
"or channel to 50% probability of success.",
|
|
||||||
},
|
|
||||||
cli.Float64Flag{
|
|
||||||
Name: "hopprob",
|
|
||||||
Usage: "the probability of success assigned " +
|
|
||||||
"to hops that we have no information about",
|
|
||||||
},
|
|
||||||
cli.Float64Flag{
|
|
||||||
Name: "weight",
|
|
||||||
Usage: "the degree to which mission control should " +
|
|
||||||
"rely on historical results, expressed as " +
|
|
||||||
"value in [0;1]",
|
|
||||||
}, cli.UintFlag{
|
|
||||||
Name: "pmtnr",
|
Name: "pmtnr",
|
||||||
Usage: "the number of payments mission control " +
|
Usage: "the number of payments mission control " +
|
||||||
"should store",
|
"should store",
|
||||||
@ -74,6 +61,48 @@ var setCfgCommand = cli.Command{
|
|||||||
Usage: "the amount of time to wait after a failure " +
|
Usage: "the amount of time to wait after a failure " +
|
||||||
"before raising failure amount",
|
"before raising failure amount",
|
||||||
},
|
},
|
||||||
|
// Probability estimator.
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "estimator",
|
||||||
|
Usage: "the probability estimator to use, choose " +
|
||||||
|
"between 'apriori' or 'bimodal'",
|
||||||
|
},
|
||||||
|
// Apriori config.
|
||||||
|
cli.DurationFlag{
|
||||||
|
Name: "apriorihalflife",
|
||||||
|
Usage: "the amount of time taken to restore a node " +
|
||||||
|
"or channel to 50% probability of success.",
|
||||||
|
},
|
||||||
|
cli.Float64Flag{
|
||||||
|
Name: "apriorihopprob",
|
||||||
|
Usage: "the probability of success assigned " +
|
||||||
|
"to hops that we have no information about",
|
||||||
|
},
|
||||||
|
cli.Float64Flag{
|
||||||
|
Name: "aprioriweight",
|
||||||
|
Usage: "the degree to which mission control should " +
|
||||||
|
"rely on historical results, expressed as " +
|
||||||
|
"value in [0, 1]",
|
||||||
|
},
|
||||||
|
// Bimodal config.
|
||||||
|
cli.DurationFlag{
|
||||||
|
Name: "bimodaldecaytime",
|
||||||
|
Usage: "the time span after which we phase out " +
|
||||||
|
"learnings from previous payment attempts",
|
||||||
|
},
|
||||||
|
cli.Uint64Flag{
|
||||||
|
Name: "bimodalscale",
|
||||||
|
Usage: "controls the assumed channel liquidity " +
|
||||||
|
"imbalance in the network, measured in msat. " +
|
||||||
|
"a low value (compared to typical channel " +
|
||||||
|
"capacity) anticipates unbalanced channels.",
|
||||||
|
},
|
||||||
|
cli.Float64Flag{
|
||||||
|
Name: "bimodalweight",
|
||||||
|
Usage: "controls the degree to which the probability " +
|
||||||
|
"estimator takes into account other channels " +
|
||||||
|
"of a router",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: actionDecorator(setCfg),
|
Action: actionDecorator(setCfg),
|
||||||
}
|
}
|
||||||
@ -85,51 +114,140 @@ func setCfg(ctx *cli.Context) error {
|
|||||||
|
|
||||||
client := routerrpc.NewRouterClient(conn)
|
client := routerrpc.NewRouterClient(conn)
|
||||||
|
|
||||||
resp, err := client.GetMissionControlConfig(
|
// Fetch current mission control config which we update to create our
|
||||||
|
// response.
|
||||||
|
mcCfg, err := client.GetMissionControlConfig(
|
||||||
ctxc, &routerrpc.GetMissionControlConfigRequest{},
|
ctxc, &routerrpc.GetMissionControlConfigRequest{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// haveValue is a helper variable to determine if a flag has been set or
|
||||||
|
// the help should be displayed.
|
||||||
var haveValue bool
|
var haveValue bool
|
||||||
|
|
||||||
if ctx.IsSet("halflife") {
|
// Handle general mission control settings.
|
||||||
haveValue = true
|
|
||||||
resp.Config.HalfLifeSeconds = uint64(ctx.Duration(
|
|
||||||
"halflife",
|
|
||||||
).Seconds())
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet("hopprob") {
|
|
||||||
haveValue = true
|
|
||||||
resp.Config.HopProbability = float32(ctx.Float64("hopprob"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet("weight") {
|
|
||||||
haveValue = true
|
|
||||||
resp.Config.Weight = float32(ctx.Float64("weight"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet("pmtnr") {
|
if ctx.IsSet("pmtnr") {
|
||||||
haveValue = true
|
haveValue = true
|
||||||
resp.Config.MaximumPaymentResults = uint32(ctx.Int("pmtnr"))
|
mcCfg.Config.MaximumPaymentResults = uint32(ctx.Int("pmtnr"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.IsSet("failrelax") {
|
if ctx.IsSet("failrelax") {
|
||||||
haveValue = true
|
haveValue = true
|
||||||
resp.Config.MinimumFailureRelaxInterval = uint64(ctx.Duration(
|
mcCfg.Config.MinimumFailureRelaxInterval = uint64(ctx.Duration(
|
||||||
"failrelax",
|
"failrelax",
|
||||||
).Seconds())
|
).Seconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We switch between estimators and set corresponding configs. If
|
||||||
|
// estimator is not set, we ignore the values.
|
||||||
|
if ctx.IsSet("estimator") {
|
||||||
|
switch ctx.String("estimator") {
|
||||||
|
case routing.AprioriEstimatorName:
|
||||||
|
haveValue = true
|
||||||
|
|
||||||
|
// If we switch from another estimator, initialize with
|
||||||
|
// default values.
|
||||||
|
if mcCfg.Config.Model !=
|
||||||
|
routerrpc.MissionControlConfig_APRIORI {
|
||||||
|
|
||||||
|
dCfg := routing.DefaultAprioriConfig()
|
||||||
|
aParams := &routerrpc.AprioriParameters{
|
||||||
|
HalfLifeSeconds: uint64(
|
||||||
|
dCfg.PenaltyHalfLife.Seconds(),
|
||||||
|
),
|
||||||
|
HopProbability: dCfg.AprioriHopProbability, //nolint:lll
|
||||||
|
Weight: dCfg.AprioriWeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We make sure the correct config is set.
|
||||||
|
mcCfg.Config.EstimatorConfig =
|
||||||
|
&routerrpc.MissionControlConfig_Apriori{
|
||||||
|
Apriori: aParams,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We update all values for the apriori estimator.
|
||||||
|
mcCfg.Config.Model = routerrpc.
|
||||||
|
MissionControlConfig_APRIORI
|
||||||
|
|
||||||
|
aCfg := mcCfg.Config.GetApriori()
|
||||||
|
if ctx.IsSet("apriorihalflife") {
|
||||||
|
aCfg.HalfLifeSeconds = uint64(ctx.Duration(
|
||||||
|
"apriorihalflife",
|
||||||
|
).Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet("apriorihopprob") {
|
||||||
|
aCfg.HopProbability = ctx.Float64(
|
||||||
|
"apriorihopprob",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet("aprioriweight") {
|
||||||
|
aCfg.Weight = ctx.Float64("aprioriweight")
|
||||||
|
}
|
||||||
|
|
||||||
|
case routing.BimodalEstimatorName:
|
||||||
|
haveValue = true
|
||||||
|
|
||||||
|
// If we switch from another estimator, initialize with
|
||||||
|
// default values.
|
||||||
|
if mcCfg.Config.Model !=
|
||||||
|
routerrpc.MissionControlConfig_BIMODAL {
|
||||||
|
|
||||||
|
dCfg := routing.DefaultBimodalConfig()
|
||||||
|
bParams := &routerrpc.BimodalParameters{
|
||||||
|
DecayTime: uint64(
|
||||||
|
dCfg.BimodalDecayTime.Seconds(),
|
||||||
|
),
|
||||||
|
ScaleMsat: uint64(
|
||||||
|
dCfg.BimodalScaleMsat,
|
||||||
|
),
|
||||||
|
NodeWeight: dCfg.BimodalNodeWeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We make sure the correct config is set.
|
||||||
|
mcCfg.Config.EstimatorConfig =
|
||||||
|
&routerrpc.MissionControlConfig_Bimodal{
|
||||||
|
Bimodal: bParams,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We update all values for the bimodal estimator.
|
||||||
|
mcCfg.Config.Model = routerrpc.
|
||||||
|
MissionControlConfig_BIMODAL
|
||||||
|
|
||||||
|
bCfg := mcCfg.Config.GetBimodal()
|
||||||
|
if ctx.IsSet("bimodaldecaytime") {
|
||||||
|
bCfg.DecayTime = uint64(ctx.Duration(
|
||||||
|
"bimodaldecaytime",
|
||||||
|
).Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet("bimodalscale") {
|
||||||
|
bCfg.ScaleMsat = ctx.Uint64("bimodalscale")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet("bimodalweight") {
|
||||||
|
bCfg.NodeWeight = ctx.Float64(
|
||||||
|
"bimodalweight",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown estimator %v",
|
||||||
|
ctx.String("estimator"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !haveValue {
|
if !haveValue {
|
||||||
return cli.ShowCommandHelp(ctx, "setmccfg")
|
return cli.ShowCommandHelp(ctx, "setmccfg")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.SetMissionControlConfig(
|
_, err = client.SetMissionControlConfig(
|
||||||
ctxc, &routerrpc.SetMissionControlConfigRequest{
|
ctxc, &routerrpc.SetMissionControlConfigRequest{
|
||||||
Config: resp.Config,
|
Config: mcCfg.Config,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -467,6 +467,93 @@ message SetMissionControlConfigResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message MissionControlConfig {
|
message MissionControlConfig {
|
||||||
|
/*
|
||||||
|
Deprecated, use AprioriParameters. The amount of time mission control will
|
||||||
|
take to restore a penalized node or channel back to 50% success probability,
|
||||||
|
expressed in seconds. Setting this value to a higher value will penalize
|
||||||
|
failures for longer, making mission control less likely to route through
|
||||||
|
nodes and channels that we have previously recorded failures for.
|
||||||
|
*/
|
||||||
|
uint64 half_life_seconds = 1 [deprecated = true];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Deprecated, use AprioriParameters. The probability of success mission
|
||||||
|
control should assign to hop in a route where it has no other information
|
||||||
|
available. Higher values will make mission control more willing to try hops
|
||||||
|
that we have no information about, lower values will discourage trying these
|
||||||
|
hops.
|
||||||
|
*/
|
||||||
|
float hop_probability = 2 [deprecated = true];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Deprecated, use AprioriParameters. The importance that mission control
|
||||||
|
should place on historical results, expressed as a value in [0;1]. Setting
|
||||||
|
this value to 1 will ignore all historical payments and just use the hop
|
||||||
|
probability to assess the probability of success for each hop. A zero value
|
||||||
|
ignores hop probability completely and relies entirely on historical
|
||||||
|
results, unless none are available.
|
||||||
|
*/
|
||||||
|
float weight = 3 [deprecated = true];
|
||||||
|
|
||||||
|
/*
|
||||||
|
The maximum number of payment results that mission control will store.
|
||||||
|
*/
|
||||||
|
uint32 maximum_payment_results = 4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The minimum time that must have passed since the previously recorded failure
|
||||||
|
before we raise the failure amount.
|
||||||
|
*/
|
||||||
|
uint64 minimum_failure_relax_interval = 5;
|
||||||
|
|
||||||
|
enum ProbabilityModel {
|
||||||
|
APRIORI = 0;
|
||||||
|
BIMODAL = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ProbabilityModel defines which probability estimator should be used in
|
||||||
|
pathfinding.
|
||||||
|
*/
|
||||||
|
ProbabilityModel Model = 6;
|
||||||
|
|
||||||
|
/*
|
||||||
|
EstimatorConfig is populated dependent on the estimator type.
|
||||||
|
*/
|
||||||
|
oneof EstimatorConfig {
|
||||||
|
AprioriParameters apriori = 7;
|
||||||
|
BimodalParameters bimodal = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message BimodalParameters {
|
||||||
|
/*
|
||||||
|
NodeWeight defines how strongly other previous forwardings on channels of a
|
||||||
|
router should be taken into account when computing a channel's probability
|
||||||
|
to route. The allowed values are in the range [0, 1], where a value of 0
|
||||||
|
means that only direct information about a channel is taken into account.
|
||||||
|
*/
|
||||||
|
double node_weight = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
ScaleMsat describes the scale over which channels statistically have some
|
||||||
|
liquidity left. The value determines how quickly the bimodal distribution
|
||||||
|
drops off from the edges of a channel. A larger value (compared to typical
|
||||||
|
channel capacities) means that the drop off is slow and that channel
|
||||||
|
balances are distributed more uniformly. A small value leads to the
|
||||||
|
assumption of very unbalanced channels.
|
||||||
|
*/
|
||||||
|
uint64 scale_msat = 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
DecayTime describes the information decay of knowledge about previous
|
||||||
|
successes and failures in channels. The smaller the decay time, the quicker
|
||||||
|
we forget about past forwardings.
|
||||||
|
*/
|
||||||
|
uint64 decay_time = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AprioriParameters {
|
||||||
/*
|
/*
|
||||||
The amount of time mission control will take to restore a penalized node
|
The amount of time mission control will take to restore a penalized node
|
||||||
or channel back to 50% success probability, expressed in seconds. Setting
|
or channel back to 50% success probability, expressed in seconds. Setting
|
||||||
@ -482,7 +569,7 @@ message MissionControlConfig {
|
|||||||
control more willing to try hops that we have no information about, lower
|
control more willing to try hops that we have no information about, lower
|
||||||
values will discourage trying these hops.
|
values will discourage trying these hops.
|
||||||
*/
|
*/
|
||||||
float hop_probability = 2;
|
double hop_probability = 2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The importance that mission control should place on historical results,
|
The importance that mission control should place on historical results,
|
||||||
@ -492,18 +579,7 @@ message MissionControlConfig {
|
|||||||
completely and relies entirely on historical results, unless none are
|
completely and relies entirely on historical results, unless none are
|
||||||
available.
|
available.
|
||||||
*/
|
*/
|
||||||
float weight = 3;
|
double weight = 3;
|
||||||
|
|
||||||
/*
|
|
||||||
The maximum number of payment results that mission control will store.
|
|
||||||
*/
|
|
||||||
uint32 maximum_payment_results = 4;
|
|
||||||
|
|
||||||
/*
|
|
||||||
The minimum time that must have passed since the previously recorded failure
|
|
||||||
before we raise the failure amount.
|
|
||||||
*/
|
|
||||||
uint64 minimum_failure_relax_interval = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message QueryProbabilityRequest {
|
message QueryProbabilityRequest {
|
||||||
|
@ -593,6 +593,14 @@
|
|||||||
],
|
],
|
||||||
"default": "IN_FLIGHT"
|
"default": "IN_FLIGHT"
|
||||||
},
|
},
|
||||||
|
"MissionControlConfigProbabilityModel": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"APRIORI",
|
||||||
|
"BIMODAL"
|
||||||
|
],
|
||||||
|
"default": "APRIORI"
|
||||||
|
},
|
||||||
"lnrpcAMPRecord": {
|
"lnrpcAMPRecord": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -1073,6 +1081,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"routerrpcAprioriParameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"half_life_seconds": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uint64",
|
||||||
|
"description": "The amount of time mission control will take to restore a penalized node\nor channel back to 50% success probability, expressed in seconds. Setting\nthis value to a higher value will penalize failures for longer, making\nmission control less likely to route through nodes and channels that we\nhave previously recorded failures for."
|
||||||
|
},
|
||||||
|
"hop_probability": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"description": "The probability of success mission control should assign to hop in a route\nwhere it has no other information available. Higher values will make mission\ncontrol more willing to try hops that we have no information about, lower\nvalues will discourage trying these hops."
|
||||||
|
},
|
||||||
|
"weight": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"description": "The importance that mission control should place on historical results,\nexpressed as a value in [0;1]. Setting this value to 1 will ignore all\nhistorical payments and just use the hop probability to assess the\nprobability of success for each hop. A zero value ignores hop probability\ncompletely and relies entirely on historical results, unless none are\navailable."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"routerrpcBimodalParameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"node_weight": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"description": "NodeWeight defines how strongly other previous forwardings on channels of a\nrouter should be taken into account when computing a channel's probability\nto route. The allowed values are in the range [0, 1], where a value of 0\nmeans that only direct information about a channel is taken into account."
|
||||||
|
},
|
||||||
|
"scale_msat": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uint64",
|
||||||
|
"description": "ScaleMsat describes the scale over which channels statistically have some\nliquidity left. The value determines how quickly the bimodal distribution\ndrops off from the edges of a channel. A larger value (compared to typical\nchannel capacities) means that the drop off is slow and that channel\nbalances are distributed more uniformly. A small value leads to the\nassumption of very unbalanced channels."
|
||||||
|
},
|
||||||
|
"decay_time": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uint64",
|
||||||
|
"description": "DecayTime describes the information decay of knowledge about previous\nsuccesses and failures in channels. The smaller the decay time, the quicker\nwe forget about past forwardings."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"routerrpcBuildRouteRequest": {
|
"routerrpcBuildRouteRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -1400,17 +1448,17 @@
|
|||||||
"half_life_seconds": {
|
"half_life_seconds": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uint64",
|
"format": "uint64",
|
||||||
"description": "The amount of time mission control will take to restore a penalized node\nor channel back to 50% success probability, expressed in seconds. Setting\nthis value to a higher value will penalize failures for longer, making\nmission control less likely to route through nodes and channels that we\nhave previously recorded failures for."
|
"description": "Deprecated, use AprioriParameters. The amount of time mission control will\ntake to restore a penalized node or channel back to 50% success probability,\nexpressed in seconds. Setting this value to a higher value will penalize\nfailures for longer, making mission control less likely to route through\nnodes and channels that we have previously recorded failures for."
|
||||||
},
|
},
|
||||||
"hop_probability": {
|
"hop_probability": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"format": "float",
|
"format": "float",
|
||||||
"description": "The probability of success mission control should assign to hop in a route\nwhere it has no other information available. Higher values will make mission\ncontrol more willing to try hops that we have no information about, lower\nvalues will discourage trying these hops."
|
"description": "Deprecated, use AprioriParameters. The probability of success mission\ncontrol should assign to hop in a route where it has no other information\navailable. Higher values will make mission control more willing to try hops\nthat we have no information about, lower values will discourage trying these\nhops."
|
||||||
},
|
},
|
||||||
"weight": {
|
"weight": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"format": "float",
|
"format": "float",
|
||||||
"description": "The importance that mission control should place on historical results,\nexpressed as a value in [0;1]. Setting this value to 1 will ignore all\nhistorical payments and just use the hop probability to assess the\nprobability of success for each hop. A zero value ignores hop probability\ncompletely and relies entirely on historical results, unless none are\navailable."
|
"description": "Deprecated, use AprioriParameters. The importance that mission control\nshould place on historical results, expressed as a value in [0;1]. Setting\nthis value to 1 will ignore all historical payments and just use the hop\nprobability to assess the probability of success for each hop. A zero value\nignores hop probability completely and relies entirely on historical\nresults, unless none are available."
|
||||||
},
|
},
|
||||||
"maximum_payment_results": {
|
"maximum_payment_results": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@ -1421,6 +1469,16 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uint64",
|
"format": "uint64",
|
||||||
"description": "The minimum time that must have passed since the previously recorded failure\nbefore we raise the failure amount."
|
"description": "The minimum time that must have passed since the previously recorded failure\nbefore we raise the failure amount."
|
||||||
|
},
|
||||||
|
"Model": {
|
||||||
|
"$ref": "#/definitions/MissionControlConfigProbabilityModel",
|
||||||
|
"description": "ProbabilityModel defines which probability estimator should be used in\npathfinding."
|
||||||
|
},
|
||||||
|
"apriori": {
|
||||||
|
"$ref": "#/definitions/routerrpcAprioriParameters"
|
||||||
|
},
|
||||||
|
"bimodal": {
|
||||||
|
"$ref": "#/definitions/routerrpcBimodalParameters"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -454,55 +454,137 @@ func (s *Server) GetMissionControlConfig(ctx context.Context,
|
|||||||
req *GetMissionControlConfigRequest) (*GetMissionControlConfigResponse,
|
req *GetMissionControlConfigRequest) (*GetMissionControlConfigResponse,
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
|
// Query the current mission control config.
|
||||||
cfg := s.cfg.RouterBackend.MissionControl.GetConfig()
|
cfg := s.cfg.RouterBackend.MissionControl.GetConfig()
|
||||||
eCfg, ok := cfg.Estimator.Config().(*routing.AprioriConfig)
|
resp := &GetMissionControlConfigResponse{
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown estimator config type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &GetMissionControlConfigResponse{
|
|
||||||
Config: &MissionControlConfig{
|
Config: &MissionControlConfig{
|
||||||
HalfLifeSeconds: uint64(
|
|
||||||
eCfg.PenaltyHalfLife.Seconds()),
|
|
||||||
HopProbability: float32(
|
|
||||||
eCfg.AprioriHopProbability,
|
|
||||||
),
|
|
||||||
Weight: float32(eCfg.AprioriWeight),
|
|
||||||
MaximumPaymentResults: uint32(cfg.MaxMcHistory),
|
MaximumPaymentResults: uint32(cfg.MaxMcHistory),
|
||||||
MinimumFailureRelaxInterval: uint64(
|
MinimumFailureRelaxInterval: uint64(
|
||||||
cfg.MinFailureRelaxInterval.Seconds(),
|
cfg.MinFailureRelaxInterval.Seconds(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
// We only populate fields based on the current estimator.
|
||||||
|
switch v := cfg.Estimator.Config().(type) {
|
||||||
|
case routing.AprioriConfig:
|
||||||
|
resp.Config.Model = MissionControlConfig_APRIORI
|
||||||
|
aCfg := AprioriParameters{
|
||||||
|
HalfLifeSeconds: uint64(v.PenaltyHalfLife.Seconds()),
|
||||||
|
HopProbability: v.AprioriHopProbability,
|
||||||
|
Weight: v.AprioriWeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate deprecated fields.
|
||||||
|
resp.Config.HalfLifeSeconds = uint64(
|
||||||
|
v.PenaltyHalfLife.Seconds(),
|
||||||
|
)
|
||||||
|
resp.Config.HopProbability = float32(v.AprioriHopProbability)
|
||||||
|
resp.Config.Weight = float32(v.AprioriWeight)
|
||||||
|
|
||||||
|
resp.Config.EstimatorConfig = &MissionControlConfig_Apriori{
|
||||||
|
Apriori: &aCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
case routing.BimodalConfig:
|
||||||
|
resp.Config.Model = MissionControlConfig_BIMODAL
|
||||||
|
bCfg := BimodalParameters{
|
||||||
|
NodeWeight: v.BimodalNodeWeight,
|
||||||
|
ScaleMsat: uint64(v.BimodalScaleMsat),
|
||||||
|
DecayTime: uint64(v.BimodalDecayTime.Seconds()),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Config.EstimatorConfig = &MissionControlConfig_Bimodal{
|
||||||
|
Bimodal: &bCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown estimator config type %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMissionControlConfig returns our current mission control config.
|
// SetMissionControlConfig sets parameters in the mission control config.
|
||||||
func (s *Server) SetMissionControlConfig(ctx context.Context,
|
func (s *Server) SetMissionControlConfig(ctx context.Context,
|
||||||
req *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse,
|
req *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse,
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
aCfg := routing.AprioriConfig{
|
mcCfg := &routing.MissionControlConfig{
|
||||||
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{
|
|
||||||
Estimator: estimator,
|
|
||||||
MaxMcHistory: int(req.Config.MaximumPaymentResults),
|
MaxMcHistory: int(req.Config.MaximumPaymentResults),
|
||||||
MinFailureRelaxInterval: time.Duration(
|
MinFailureRelaxInterval: time.Duration(
|
||||||
req.Config.MinimumFailureRelaxInterval,
|
req.Config.MinimumFailureRelaxInterval,
|
||||||
) * time.Second,
|
) * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch req.Config.Model {
|
||||||
|
case MissionControlConfig_APRIORI:
|
||||||
|
var aprioriConfig routing.AprioriConfig
|
||||||
|
|
||||||
|
// Determine the apriori config with backward compatibility
|
||||||
|
// should the api use deprecated fields.
|
||||||
|
switch v := req.Config.EstimatorConfig.(type) {
|
||||||
|
case *MissionControlConfig_Bimodal:
|
||||||
|
return nil, fmt.Errorf("bimodal config " +
|
||||||
|
"provided, but apriori model requested")
|
||||||
|
|
||||||
|
case *MissionControlConfig_Apriori:
|
||||||
|
aprioriConfig = routing.AprioriConfig{
|
||||||
|
PenaltyHalfLife: time.Duration(
|
||||||
|
v.Apriori.HalfLifeSeconds,
|
||||||
|
) * time.Second,
|
||||||
|
AprioriHopProbability: v.Apriori.HopProbability,
|
||||||
|
AprioriWeight: v.Apriori.Weight,
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
aprioriConfig = routing.AprioriConfig{
|
||||||
|
PenaltyHalfLife: time.Duration(
|
||||||
|
int64(req.Config.HalfLifeSeconds),
|
||||||
|
) * time.Second,
|
||||||
|
AprioriHopProbability: float64(
|
||||||
|
req.Config.HopProbability,
|
||||||
|
),
|
||||||
|
AprioriWeight: float64(req.Config.Weight),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
estimator, err := routing.NewAprioriEstimator(aprioriConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mcCfg.Estimator = estimator
|
||||||
|
|
||||||
|
case MissionControlConfig_BIMODAL:
|
||||||
|
cfg, ok := req.Config.
|
||||||
|
EstimatorConfig.(*MissionControlConfig_Bimodal)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("bimodal estimator requested " +
|
||||||
|
"but corresponding config not set")
|
||||||
|
}
|
||||||
|
bCfg := cfg.Bimodal
|
||||||
|
|
||||||
|
bimodalConfig := routing.BimodalConfig{
|
||||||
|
BimodalDecayTime: time.Duration(
|
||||||
|
bCfg.DecayTime,
|
||||||
|
) * time.Second,
|
||||||
|
BimodalScaleMsat: lnwire.MilliSatoshi(bCfg.ScaleMsat),
|
||||||
|
BimodalNodeWeight: bCfg.NodeWeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
estimator, err := routing.NewBimodalEstimator(bimodalConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mcCfg.Estimator = estimator
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown estimator type %v",
|
||||||
|
req.Config.Model)
|
||||||
|
}
|
||||||
|
|
||||||
return &SetMissionControlConfigResponse{},
|
return &SetMissionControlConfigResponse{},
|
||||||
s.cfg.RouterBackend.MissionControl.SetConfig(cfg)
|
s.cfg.RouterBackend.MissionControl.SetConfig(mcCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryMissionControl exposes the internal mission control state to callers. It
|
// QueryMissionControl exposes the internal mission control state to callers. It
|
||||||
|
@ -86,6 +86,20 @@ func (h *HarnessRPC) SetMissionControlConfig(
|
|||||||
h.NoError(err, "SetMissionControlConfig")
|
h.NoError(err, "SetMissionControlConfig")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMissionControlConfigAssertErr makes a RPC call to the node's
|
||||||
|
// SetMissionControlConfig and asserts that we error.
|
||||||
|
func (h *HarnessRPC) SetMissionControlConfigAssertErr(
|
||||||
|
config *routerrpc.MissionControlConfig) {
|
||||||
|
|
||||||
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
req := &routerrpc.SetMissionControlConfigRequest{Config: config}
|
||||||
|
_, err := h.Router.SetMissionControlConfig(ctxt, req)
|
||||||
|
require.Error(h, err, "expect an error from setting import mission "+
|
||||||
|
"control")
|
||||||
|
}
|
||||||
|
|
||||||
// ResetMissionControl makes a RPC call to the node's ResetMissionControl and
|
// ResetMissionControl makes a RPC call to the node's ResetMissionControl and
|
||||||
// asserts.
|
// asserts.
|
||||||
func (h *HarnessRPC) ResetMissionControl() {
|
func (h *HarnessRPC) ResetMissionControl() {
|
||||||
|
@ -1003,25 +1003,85 @@ func testQueryRoutes(ht *lntemp.HarnessTest) {
|
|||||||
func testMissionControlCfg(t *testing.T, hn *node.HarnessNode) {
|
func testMissionControlCfg(t *testing.T, hn *node.HarnessNode) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
startCfg := hn.RPC.GetMissionControlConfig()
|
// Getting and setting does not alter the configuration.
|
||||||
|
startCfg := hn.RPC.GetMissionControlConfig().Config
|
||||||
|
hn.RPC.SetMissionControlConfig(startCfg)
|
||||||
|
resp := hn.RPC.GetMissionControlConfig()
|
||||||
|
require.True(t, proto.Equal(startCfg, resp.Config))
|
||||||
|
|
||||||
|
// We test that setting and getting leads to the same config if all
|
||||||
|
// fields are set.
|
||||||
cfg := &routerrpc.MissionControlConfig{
|
cfg := &routerrpc.MissionControlConfig{
|
||||||
HalfLifeSeconds: 8000,
|
|
||||||
HopProbability: 0.8,
|
|
||||||
Weight: 0.3,
|
|
||||||
MaximumPaymentResults: 30,
|
MaximumPaymentResults: 30,
|
||||||
MinimumFailureRelaxInterval: 60,
|
MinimumFailureRelaxInterval: 60,
|
||||||
|
Model: routerrpc.
|
||||||
|
MissionControlConfig_APRIORI,
|
||||||
|
EstimatorConfig: &routerrpc.MissionControlConfig_Apriori{
|
||||||
|
Apriori: &routerrpc.AprioriParameters{
|
||||||
|
HalfLifeSeconds: 8000,
|
||||||
|
HopProbability: 0.8,
|
||||||
|
Weight: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
hn.RPC.SetMissionControlConfig(cfg)
|
hn.RPC.SetMissionControlConfig(cfg)
|
||||||
|
|
||||||
resp := hn.RPC.GetMissionControlConfig()
|
// The deprecated fields should be populated.
|
||||||
require.True(t, proto.Equal(cfg, resp.Config))
|
cfg.HalfLifeSeconds = 8000
|
||||||
|
cfg.HopProbability = 0.8
|
||||||
|
cfg.Weight = 0.3
|
||||||
|
respCfg := hn.RPC.GetMissionControlConfig().Config
|
||||||
|
require.True(t, proto.Equal(cfg, respCfg))
|
||||||
|
|
||||||
hn.RPC.SetMissionControlConfig(startCfg.Config)
|
// Switching to another estimator is possible.
|
||||||
|
cfg = &routerrpc.MissionControlConfig{
|
||||||
|
Model: routerrpc.
|
||||||
|
MissionControlConfig_BIMODAL,
|
||||||
|
EstimatorConfig: &routerrpc.MissionControlConfig_Bimodal{
|
||||||
|
Bimodal: &routerrpc.BimodalParameters{
|
||||||
|
ScaleMsat: 1_000,
|
||||||
|
DecayTime: 500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hn.RPC.SetMissionControlConfig(cfg)
|
||||||
|
respCfg = hn.RPC.GetMissionControlConfig().Config
|
||||||
|
require.NotNil(t, respCfg.GetBimodal())
|
||||||
|
|
||||||
|
// If parameters are not set in the request, they will have zero values
|
||||||
|
// after.
|
||||||
|
require.Zero(t, respCfg.MaximumPaymentResults)
|
||||||
|
require.Zero(t, respCfg.MinimumFailureRelaxInterval)
|
||||||
|
require.Zero(t, respCfg.GetBimodal().NodeWeight)
|
||||||
|
|
||||||
|
// Setting deprecated values will initialize the apriori estimator.
|
||||||
|
cfg = &routerrpc.MissionControlConfig{
|
||||||
|
MaximumPaymentResults: 30,
|
||||||
|
MinimumFailureRelaxInterval: 60,
|
||||||
|
HopProbability: 0.8,
|
||||||
|
Weight: 0.3,
|
||||||
|
HalfLifeSeconds: 8000,
|
||||||
|
}
|
||||||
|
hn.RPC.SetMissionControlConfig(cfg)
|
||||||
|
respCfg = hn.RPC.GetMissionControlConfig().Config
|
||||||
|
require.NotNil(t, respCfg.GetApriori())
|
||||||
|
|
||||||
|
// Setting the wrong config results in an error.
|
||||||
|
cfg = &routerrpc.MissionControlConfig{
|
||||||
|
Model: routerrpc.
|
||||||
|
MissionControlConfig_APRIORI,
|
||||||
|
EstimatorConfig: &routerrpc.MissionControlConfig_Bimodal{
|
||||||
|
Bimodal: &routerrpc.BimodalParameters{
|
||||||
|
ScaleMsat: 1_000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hn.RPC.SetMissionControlConfigAssertErr(cfg)
|
||||||
|
|
||||||
|
// Undo any changes.
|
||||||
|
hn.RPC.SetMissionControlConfig(startCfg)
|
||||||
resp = hn.RPC.GetMissionControlConfig()
|
resp = hn.RPC.GetMissionControlConfig()
|
||||||
require.True(t, proto.Equal(startCfg.Config, resp.Config))
|
require.True(t, proto.Equal(startCfg, resp.Config))
|
||||||
}
|
}
|
||||||
|
|
||||||
// testMissionControlImport tests import of mission control results from an
|
// testMissionControlImport tests import of mission control results from an
|
||||||
|
Loading…
x
Reference in New Issue
Block a user