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:
bitromortac 2022-11-22 09:18:41 +01:00
parent 58d5131e31
commit 2ccdfb1151
No known key found for this signature in database
GPG Key ID: 1965063FC13BEBE2
7 changed files with 1074 additions and 336 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli"
)
@ -45,26 +46,12 @@ var setCfgCommand = cli.Command{
Category: "Mission Control",
Usage: "Set mission control's config.",
Description: `
Update the config values being used by mission control to calculate
the probability that payment routes will succeed.
`,
Update the config values being used by mission control to calculate the
probability that payment routes will succeed. The estimator type must be
provided to set estimator-related parameters.`,
Flags: []cli.Flag{
cli.DurationFlag{
Name: "halflife",
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{
// General settings.
cli.UintFlag{
Name: "pmtnr",
Usage: "the number of payments mission control " +
"should store",
@ -74,6 +61,48 @@ var setCfgCommand = cli.Command{
Usage: "the amount of time to wait after a failure " +
"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),
}
@ -85,51 +114,140 @@ func setCfg(ctx *cli.Context) error {
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{},
)
if err != nil {
return err
}
// haveValue is a helper variable to determine if a flag has been set or
// the help should be displayed.
var haveValue bool
if ctx.IsSet("halflife") {
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"))
}
// Handle general mission control settings.
if ctx.IsSet("pmtnr") {
haveValue = true
resp.Config.MaximumPaymentResults = uint32(ctx.Int("pmtnr"))
mcCfg.Config.MaximumPaymentResults = uint32(ctx.Int("pmtnr"))
}
if ctx.IsSet("failrelax") {
haveValue = true
resp.Config.MinimumFailureRelaxInterval = uint64(ctx.Duration(
mcCfg.Config.MinimumFailureRelaxInterval = uint64(ctx.Duration(
"failrelax",
).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 {
return cli.ShowCommandHelp(ctx, "setmccfg")
}
_, err = client.SetMissionControlConfig(
ctxc, &routerrpc.SetMissionControlConfigRequest{
Config: resp.Config,
Config: mcCfg.Config,
},
)
return err

File diff suppressed because it is too large Load Diff

View File

@ -467,6 +467,93 @@ message SetMissionControlConfigResponse {
}
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
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
values will discourage trying these hops.
*/
float hop_probability = 2;
double hop_probability = 2;
/*
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
available.
*/
float 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;
double weight = 3;
}
message QueryProbabilityRequest {

View File

@ -593,6 +593,14 @@
],
"default": "IN_FLIGHT"
},
"MissionControlConfigProbabilityModel": {
"type": "string",
"enum": [
"APRIORI",
"BIMODAL"
],
"default": "APRIORI"
},
"lnrpcAMPRecord": {
"type": "object",
"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": {
"type": "object",
"properties": {
@ -1400,17 +1448,17 @@
"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."
"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": {
"type": "number",
"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": {
"type": "number",
"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": {
"type": "integer",
@ -1421,6 +1469,16 @@
"type": "string",
"format": "uint64",
"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"
}
}
},

View File

@ -454,55 +454,137 @@ func (s *Server) GetMissionControlConfig(ctx context.Context,
req *GetMissionControlConfigRequest) (*GetMissionControlConfigResponse,
error) {
// Query the current mission control config.
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{
resp := &GetMissionControlConfigResponse{
Config: &MissionControlConfig{
HalfLifeSeconds: uint64(
eCfg.PenaltyHalfLife.Seconds()),
HopProbability: float32(
eCfg.AprioriHopProbability,
),
Weight: float32(eCfg.AprioriWeight),
MaximumPaymentResults: uint32(cfg.MaxMcHistory),
MinimumFailureRelaxInterval: uint64(
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,
req *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse,
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{
Estimator: estimator,
mcCfg := &routing.MissionControlConfig{
MaxMcHistory: int(req.Config.MaximumPaymentResults),
MinFailureRelaxInterval: time.Duration(
req.Config.MinimumFailureRelaxInterval,
) * 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{},
s.cfg.RouterBackend.MissionControl.SetConfig(cfg)
s.cfg.RouterBackend.MissionControl.SetConfig(mcCfg)
}
// QueryMissionControl exposes the internal mission control state to callers. It

View File

@ -86,6 +86,20 @@ func (h *HarnessRPC) 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
// asserts.
func (h *HarnessRPC) ResetMissionControl() {

View File

@ -1003,25 +1003,85 @@ func testQueryRoutes(ht *lntemp.HarnessTest) {
func testMissionControlCfg(t *testing.T, hn *node.HarnessNode) {
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{
HalfLifeSeconds: 8000,
HopProbability: 0.8,
Weight: 0.3,
MaximumPaymentResults: 30,
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)
resp := hn.RPC.GetMissionControlConfig()
require.True(t, proto.Equal(cfg, resp.Config))
// The deprecated fields should be populated.
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()
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