Merge pull request #4909 from carlaKC/mc-paramsapi

routing: allow runtime updates to mission control config
This commit is contained in:
Olaoluwa Osuntokun 2021-02-10 18:51:53 -08:00 committed by GitHub
commit 7b0ea3c029
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1155 additions and 254 deletions

View File

@ -0,0 +1,36 @@
package main
import (
"context"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/urfave/cli"
)
var getCfgCommand = cli.Command{
Name: "getmccfg",
Usage: "Display mission control's config.",
Description: `
Returns the config currently being used by mission control.
`,
Action: actionDecorator(getCfg),
}
func getCfg(ctx *cli.Context) error {
conn := getClientConn(ctx, false)
defer conn.Close()
client := routerrpc.NewRouterClient(conn)
ctxb := context.Background()
resp, err := client.GetMissionControlConfig(
ctxb, &routerrpc.GetMissionControlConfigRequest{},
)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}

102
cmd/lncli/cmd_mc_cfg_set.go Normal file
View File

@ -0,0 +1,102 @@
package main
import (
"context"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/urfave/cli"
)
var setCfgCommand = cli.Command{
Name: "setmccfg",
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.
`,
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{
Name: "pmtnr",
Usage: "the number of payments mission control " +
"should store",
},
cli.DurationFlag{
Name: "failrelax",
Usage: "the amount of time to wait after a failure " +
"before raising failure amount",
},
},
Action: actionDecorator(setCfg),
}
func setCfg(ctx *cli.Context) error {
conn := getClientConn(ctx, false)
defer conn.Close()
client := routerrpc.NewRouterClient(conn)
ctxb := context.Background()
resp, err := client.GetMissionControlConfig(
ctxb, &routerrpc.GetMissionControlConfigRequest{},
)
if err != nil {
return err
}
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"))
}
if ctx.IsSet("pmtnr") {
haveValue = true
resp.Config.MaximumPaymentResults = uint32(ctx.Int("pmtnr"))
}
if ctx.IsSet("failrelax") {
haveValue = true
resp.Config.MinimumFailureRelaxInterval = uint64(ctx.Duration(
"failrelax",
).Seconds())
}
if !haveValue {
return cli.ShowCommandHelp(ctx, "setmccfg")
}
_, err = client.SetMissionControlConfig(
ctxb, &routerrpc.SetMissionControlConfigRequest{
Config: resp.Config,
},
)
return err
}

View File

@ -9,5 +9,7 @@ func routerCommands() []cli.Command {
queryProbCommand,
resetMissionControlCommand,
buildRouteCommand,
getCfgCommand,
setCfgCommand,
}
}

View File

@ -208,6 +208,11 @@ http:
body: "*"
- selector: routerrpc.Router.QueryMissionControl
get: "/v2/router/mc"
- selector: routerrpc.Router.GetMissionControlConfig
get: "/v2/router/mccfg"
- selector: routerroc.Router.SetMissionControlConfig
post: "/v2/router/mccfg"
body: "*"
- selector: routerrpc.Router.QueryProbability
get: "/v2/router/mc/probability/{from_node}/{to_node}/{amt_msat}"
- selector: routerrpc.Router.BuildRoute

View File

@ -225,7 +225,7 @@ func (x HtlcEvent_EventType) String() string {
}
func (HtlcEvent_EventType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{17, 0}
return fileDescriptor_7a0613f69d37b0a5, []int{22, 0}
}
type SendPaymentRequest struct {
@ -1029,6 +1029,245 @@ func (m *PairData) GetSuccessAmtMsat() int64 {
return 0
}
type GetMissionControlConfigRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetMissionControlConfigRequest) Reset() { *m = GetMissionControlConfigRequest{} }
func (m *GetMissionControlConfigRequest) String() string { return proto.CompactTextString(m) }
func (*GetMissionControlConfigRequest) ProtoMessage() {}
func (*GetMissionControlConfigRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{12}
}
func (m *GetMissionControlConfigRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetMissionControlConfigRequest.Unmarshal(m, b)
}
func (m *GetMissionControlConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetMissionControlConfigRequest.Marshal(b, m, deterministic)
}
func (m *GetMissionControlConfigRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetMissionControlConfigRequest.Merge(m, src)
}
func (m *GetMissionControlConfigRequest) XXX_Size() int {
return xxx_messageInfo_GetMissionControlConfigRequest.Size(m)
}
func (m *GetMissionControlConfigRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetMissionControlConfigRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetMissionControlConfigRequest proto.InternalMessageInfo
type GetMissionControlConfigResponse struct {
//
//Mission control's currently active config.
Config *MissionControlConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetMissionControlConfigResponse) Reset() { *m = GetMissionControlConfigResponse{} }
func (m *GetMissionControlConfigResponse) String() string { return proto.CompactTextString(m) }
func (*GetMissionControlConfigResponse) ProtoMessage() {}
func (*GetMissionControlConfigResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{13}
}
func (m *GetMissionControlConfigResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetMissionControlConfigResponse.Unmarshal(m, b)
}
func (m *GetMissionControlConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetMissionControlConfigResponse.Marshal(b, m, deterministic)
}
func (m *GetMissionControlConfigResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetMissionControlConfigResponse.Merge(m, src)
}
func (m *GetMissionControlConfigResponse) XXX_Size() int {
return xxx_messageInfo_GetMissionControlConfigResponse.Size(m)
}
func (m *GetMissionControlConfigResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetMissionControlConfigResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetMissionControlConfigResponse proto.InternalMessageInfo
func (m *GetMissionControlConfigResponse) GetConfig() *MissionControlConfig {
if m != nil {
return m.Config
}
return nil
}
type SetMissionControlConfigRequest struct {
//
//The config to set for mission control. Note that all values *must* be set,
//because the full config will be applied.
Config *MissionControlConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SetMissionControlConfigRequest) Reset() { *m = SetMissionControlConfigRequest{} }
func (m *SetMissionControlConfigRequest) String() string { return proto.CompactTextString(m) }
func (*SetMissionControlConfigRequest) ProtoMessage() {}
func (*SetMissionControlConfigRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{14}
}
func (m *SetMissionControlConfigRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetMissionControlConfigRequest.Unmarshal(m, b)
}
func (m *SetMissionControlConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SetMissionControlConfigRequest.Marshal(b, m, deterministic)
}
func (m *SetMissionControlConfigRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SetMissionControlConfigRequest.Merge(m, src)
}
func (m *SetMissionControlConfigRequest) XXX_Size() int {
return xxx_messageInfo_SetMissionControlConfigRequest.Size(m)
}
func (m *SetMissionControlConfigRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SetMissionControlConfigRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SetMissionControlConfigRequest proto.InternalMessageInfo
func (m *SetMissionControlConfigRequest) GetConfig() *MissionControlConfig {
if m != nil {
return m.Config
}
return nil
}
type SetMissionControlConfigResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SetMissionControlConfigResponse) Reset() { *m = SetMissionControlConfigResponse{} }
func (m *SetMissionControlConfigResponse) String() string { return proto.CompactTextString(m) }
func (*SetMissionControlConfigResponse) ProtoMessage() {}
func (*SetMissionControlConfigResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{15}
}
func (m *SetMissionControlConfigResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetMissionControlConfigResponse.Unmarshal(m, b)
}
func (m *SetMissionControlConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SetMissionControlConfigResponse.Marshal(b, m, deterministic)
}
func (m *SetMissionControlConfigResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SetMissionControlConfigResponse.Merge(m, src)
}
func (m *SetMissionControlConfigResponse) XXX_Size() int {
return xxx_messageInfo_SetMissionControlConfigResponse.Size(m)
}
func (m *SetMissionControlConfigResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SetMissionControlConfigResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SetMissionControlConfigResponse proto.InternalMessageInfo
type MissionControlConfig struct {
//
//The amount of time mission control will take to restore a penalized node
//or channel back to 50% success probability, expressed as a unix timestamp
//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.
HalfLifeSeconds uint64 `protobuf:"varint,1,opt,name=half_life_seconds,json=halfLifeSeconds,proto3" json:"half_life_seconds,omitempty"`
//
//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.
HopProbability float32 `protobuf:"fixed32,2,opt,name=hop_probability,json=hopProbability,proto3" json:"hop_probability,omitempty"`
//
//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.
Weight float32 `protobuf:"fixed32,3,opt,name=weight,proto3" json:"weight,omitempty"`
//
//The maximum number of payment results that mission control will store.
MaximumPaymentResults uint32 `protobuf:"varint,4,opt,name=maximum_payment_results,json=maximumPaymentResults,proto3" json:"maximum_payment_results,omitempty"`
//
//The minimum time that must have passed since the previously recorded failure
//before we raise the failure amount.
MinimumFailureRelaxInterval uint64 `protobuf:"varint,5,opt,name=minimum_failure_relax_interval,json=minimumFailureRelaxInterval,proto3" json:"minimum_failure_relax_interval,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MissionControlConfig) Reset() { *m = MissionControlConfig{} }
func (m *MissionControlConfig) String() string { return proto.CompactTextString(m) }
func (*MissionControlConfig) ProtoMessage() {}
func (*MissionControlConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{16}
}
func (m *MissionControlConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MissionControlConfig.Unmarshal(m, b)
}
func (m *MissionControlConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MissionControlConfig.Marshal(b, m, deterministic)
}
func (m *MissionControlConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_MissionControlConfig.Merge(m, src)
}
func (m *MissionControlConfig) XXX_Size() int {
return xxx_messageInfo_MissionControlConfig.Size(m)
}
func (m *MissionControlConfig) XXX_DiscardUnknown() {
xxx_messageInfo_MissionControlConfig.DiscardUnknown(m)
}
var xxx_messageInfo_MissionControlConfig proto.InternalMessageInfo
func (m *MissionControlConfig) GetHalfLifeSeconds() uint64 {
if m != nil {
return m.HalfLifeSeconds
}
return 0
}
func (m *MissionControlConfig) GetHopProbability() float32 {
if m != nil {
return m.HopProbability
}
return 0
}
func (m *MissionControlConfig) GetWeight() float32 {
if m != nil {
return m.Weight
}
return 0
}
func (m *MissionControlConfig) GetMaximumPaymentResults() uint32 {
if m != nil {
return m.MaximumPaymentResults
}
return 0
}
func (m *MissionControlConfig) GetMinimumFailureRelaxInterval() uint64 {
if m != nil {
return m.MinimumFailureRelaxInterval
}
return 0
}
type QueryProbabilityRequest struct {
// The source node pubkey of the pair.
FromNode []byte `protobuf:"bytes,1,opt,name=from_node,json=fromNode,proto3" json:"from_node,omitempty"`
@ -1045,7 +1284,7 @@ func (m *QueryProbabilityRequest) Reset() { *m = QueryProbabilityRequest
func (m *QueryProbabilityRequest) String() string { return proto.CompactTextString(m) }
func (*QueryProbabilityRequest) ProtoMessage() {}
func (*QueryProbabilityRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{12}
return fileDescriptor_7a0613f69d37b0a5, []int{17}
}
func (m *QueryProbabilityRequest) XXX_Unmarshal(b []byte) error {
@ -1101,7 +1340,7 @@ func (m *QueryProbabilityResponse) Reset() { *m = QueryProbabilityRespon
func (m *QueryProbabilityResponse) String() string { return proto.CompactTextString(m) }
func (*QueryProbabilityResponse) ProtoMessage() {}
func (*QueryProbabilityResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{13}
return fileDescriptor_7a0613f69d37b0a5, []int{18}
}
func (m *QueryProbabilityResponse) XXX_Unmarshal(b []byte) error {
@ -1164,7 +1403,7 @@ func (m *BuildRouteRequest) Reset() { *m = BuildRouteRequest{} }
func (m *BuildRouteRequest) String() string { return proto.CompactTextString(m) }
func (*BuildRouteRequest) ProtoMessage() {}
func (*BuildRouteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{14}
return fileDescriptor_7a0613f69d37b0a5, []int{19}
}
func (m *BuildRouteRequest) XXX_Unmarshal(b []byte) error {
@ -1233,7 +1472,7 @@ func (m *BuildRouteResponse) Reset() { *m = BuildRouteResponse{} }
func (m *BuildRouteResponse) String() string { return proto.CompactTextString(m) }
func (*BuildRouteResponse) ProtoMessage() {}
func (*BuildRouteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{15}
return fileDescriptor_7a0613f69d37b0a5, []int{20}
}
func (m *BuildRouteResponse) XXX_Unmarshal(b []byte) error {
@ -1271,7 +1510,7 @@ func (m *SubscribeHtlcEventsRequest) Reset() { *m = SubscribeHtlcEventsR
func (m *SubscribeHtlcEventsRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeHtlcEventsRequest) ProtoMessage() {}
func (*SubscribeHtlcEventsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{16}
return fileDescriptor_7a0613f69d37b0a5, []int{21}
}
func (m *SubscribeHtlcEventsRequest) XXX_Unmarshal(b []byte) error {
@ -1338,7 +1577,7 @@ func (m *HtlcEvent) Reset() { *m = HtlcEvent{} }
func (m *HtlcEvent) String() string { return proto.CompactTextString(m) }
func (*HtlcEvent) ProtoMessage() {}
func (*HtlcEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{17}
return fileDescriptor_7a0613f69d37b0a5, []int{22}
}
func (m *HtlcEvent) XXX_Unmarshal(b []byte) error {
@ -1492,7 +1731,7 @@ func (m *HtlcInfo) Reset() { *m = HtlcInfo{} }
func (m *HtlcInfo) String() string { return proto.CompactTextString(m) }
func (*HtlcInfo) ProtoMessage() {}
func (*HtlcInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{18}
return fileDescriptor_7a0613f69d37b0a5, []int{23}
}
func (m *HtlcInfo) XXX_Unmarshal(b []byte) error {
@ -1553,7 +1792,7 @@ func (m *ForwardEvent) Reset() { *m = ForwardEvent{} }
func (m *ForwardEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardEvent) ProtoMessage() {}
func (*ForwardEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{19}
return fileDescriptor_7a0613f69d37b0a5, []int{24}
}
func (m *ForwardEvent) XXX_Unmarshal(b []byte) error {
@ -1591,7 +1830,7 @@ func (m *ForwardFailEvent) Reset() { *m = ForwardFailEvent{} }
func (m *ForwardFailEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardFailEvent) ProtoMessage() {}
func (*ForwardFailEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{20}
return fileDescriptor_7a0613f69d37b0a5, []int{25}
}
func (m *ForwardFailEvent) XXX_Unmarshal(b []byte) error {
@ -1622,7 +1861,7 @@ func (m *SettleEvent) Reset() { *m = SettleEvent{} }
func (m *SettleEvent) String() string { return proto.CompactTextString(m) }
func (*SettleEvent) ProtoMessage() {}
func (*SettleEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{21}
return fileDescriptor_7a0613f69d37b0a5, []int{26}
}
func (m *SettleEvent) XXX_Unmarshal(b []byte) error {
@ -1664,7 +1903,7 @@ func (m *LinkFailEvent) Reset() { *m = LinkFailEvent{} }
func (m *LinkFailEvent) String() string { return proto.CompactTextString(m) }
func (*LinkFailEvent) ProtoMessage() {}
func (*LinkFailEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{22}
return fileDescriptor_7a0613f69d37b0a5, []int{27}
}
func (m *LinkFailEvent) XXX_Unmarshal(b []byte) error {
@ -1731,7 +1970,7 @@ func (m *PaymentStatus) Reset() { *m = PaymentStatus{} }
func (m *PaymentStatus) String() string { return proto.CompactTextString(m) }
func (*PaymentStatus) ProtoMessage() {}
func (*PaymentStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{23}
return fileDescriptor_7a0613f69d37b0a5, []int{28}
}
func (m *PaymentStatus) XXX_Unmarshal(b []byte) error {
@ -1787,7 +2026,7 @@ func (m *CircuitKey) Reset() { *m = CircuitKey{} }
func (m *CircuitKey) String() string { return proto.CompactTextString(m) }
func (*CircuitKey) ProtoMessage() {}
func (*CircuitKey) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{24}
return fileDescriptor_7a0613f69d37b0a5, []int{29}
}
func (m *CircuitKey) XXX_Unmarshal(b []byte) error {
@ -1857,7 +2096,7 @@ func (m *ForwardHtlcInterceptRequest) Reset() { *m = ForwardHtlcIntercep
func (m *ForwardHtlcInterceptRequest) String() string { return proto.CompactTextString(m) }
func (*ForwardHtlcInterceptRequest) ProtoMessage() {}
func (*ForwardHtlcInterceptRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{25}
return fileDescriptor_7a0613f69d37b0a5, []int{30}
}
func (m *ForwardHtlcInterceptRequest) XXX_Unmarshal(b []byte) error {
@ -1965,7 +2204,7 @@ func (m *ForwardHtlcInterceptResponse) Reset() { *m = ForwardHtlcInterce
func (m *ForwardHtlcInterceptResponse) String() string { return proto.CompactTextString(m) }
func (*ForwardHtlcInterceptResponse) ProtoMessage() {}
func (*ForwardHtlcInterceptResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{26}
return fileDescriptor_7a0613f69d37b0a5, []int{31}
}
func (m *ForwardHtlcInterceptResponse) XXX_Unmarshal(b []byte) error {
@ -2025,6 +2264,11 @@ func init() {
proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse")
proto.RegisterType((*PairHistory)(nil), "routerrpc.PairHistory")
proto.RegisterType((*PairData)(nil), "routerrpc.PairData")
proto.RegisterType((*GetMissionControlConfigRequest)(nil), "routerrpc.GetMissionControlConfigRequest")
proto.RegisterType((*GetMissionControlConfigResponse)(nil), "routerrpc.GetMissionControlConfigResponse")
proto.RegisterType((*SetMissionControlConfigRequest)(nil), "routerrpc.SetMissionControlConfigRequest")
proto.RegisterType((*SetMissionControlConfigResponse)(nil), "routerrpc.SetMissionControlConfigResponse")
proto.RegisterType((*MissionControlConfig)(nil), "routerrpc.MissionControlConfig")
proto.RegisterType((*QueryProbabilityRequest)(nil), "routerrpc.QueryProbabilityRequest")
proto.RegisterType((*QueryProbabilityResponse)(nil), "routerrpc.QueryProbabilityResponse")
proto.RegisterType((*BuildRouteRequest)(nil), "routerrpc.BuildRouteRequest")
@ -2046,171 +2290,184 @@ func init() {
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
var fileDescriptor_7a0613f69d37b0a5 = []byte{
// 2622 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xcd, 0x76, 0xdb, 0xc6,
0xf5, 0x0f, 0xf8, 0x25, 0xf2, 0xf2, 0x43, 0xd0, 0x48, 0xb1, 0xf8, 0xa7, 0xec, 0x84, 0x61, 0x12,
0x9b, 0xc7, 0xff, 0x44, 0x56, 0xd4, 0x9e, 0x36, 0x6d, 0x3e, 0x1a, 0x8a, 0x84, 0x2c, 0xd8, 0x14,
0xa9, 0x0c, 0x29, 0x27, 0x69, 0x16, 0x53, 0x88, 0x18, 0x8a, 0xa8, 0x40, 0x80, 0x05, 0x86, 0x76,
0xb4, 0xec, 0xae, 0xa7, 0x0f, 0xd0, 0xc7, 0xe8, 0x13, 0xf4, 0x9c, 0x76, 0xd1, 0x55, 0x5f, 0xa2,
0xdb, 0x3e, 0x41, 0xd7, 0x3d, 0xf3, 0x01, 0x10, 0x90, 0x28, 0xbb, 0x3d, 0xed, 0xc6, 0x26, 0x7e,
0xf7, 0xce, 0x9d, 0x7b, 0xe7, 0x7e, 0xce, 0x08, 0xee, 0x05, 0xfe, 0x92, 0xd1, 0x20, 0x58, 0x4c,
0x9e, 0xc8, 0x5f, 0xfb, 0x8b, 0xc0, 0x67, 0x3e, 0x2a, 0xc5, 0x78, 0xa3, 0x14, 0x2c, 0x26, 0x12,
0x6d, 0xfd, 0x61, 0x03, 0xd0, 0x88, 0x7a, 0xf6, 0x99, 0x75, 0x3d, 0xa7, 0x1e, 0xc3, 0xf4, 0x37,
0x4b, 0x1a, 0x32, 0x84, 0x20, 0x67, 0xd3, 0x90, 0xd5, 0xb5, 0xa6, 0xd6, 0xae, 0x60, 0xf1, 0x1b,
0xe9, 0x90, 0xb5, 0xe6, 0xac, 0x9e, 0x69, 0x6a, 0xed, 0x2c, 0xe6, 0x3f, 0xd1, 0xff, 0x41, 0xd1,
0x9a, 0x33, 0x32, 0x0f, 0x2d, 0x56, 0xaf, 0x08, 0x78, 0xc3, 0x9a, 0xb3, 0xd3, 0xd0, 0x62, 0xe8,
0x3d, 0xa8, 0x2c, 0xa4, 0x48, 0x32, 0xb3, 0xc2, 0x59, 0x3d, 0x2b, 0x04, 0x95, 0x15, 0x76, 0x62,
0x85, 0x33, 0xd4, 0x06, 0x7d, 0xea, 0x78, 0x96, 0x4b, 0x26, 0x2e, 0x7b, 0x49, 0x6c, 0xea, 0x32,
0xab, 0x9e, 0x6b, 0x6a, 0xed, 0x3c, 0xae, 0x09, 0xbc, 0xeb, 0xb2, 0x97, 0x3d, 0x8e, 0x26, 0x85,
0x59, 0xb6, 0x1d, 0xd4, 0x77, 0x52, 0xc2, 0x3a, 0xb6, 0x1d, 0xa0, 0x47, 0xb0, 0x19, 0xb1, 0x04,
0xd2, 0x86, 0x7a, 0xbe, 0xa9, 0xb5, 0x4b, 0xb8, 0xb6, 0x48, 0x5b, 0xf6, 0x08, 0x36, 0x99, 0x33,
0xa7, 0xfe, 0x92, 0x91, 0x90, 0x4e, 0x7c, 0xcf, 0x0e, 0xeb, 0x05, 0xb9, 0xa9, 0x82, 0x47, 0x12,
0x45, 0x2d, 0xa8, 0x4e, 0x29, 0x25, 0xae, 0x33, 0x77, 0x18, 0xe1, 0x16, 0x6e, 0x08, 0x0b, 0xcb,
0x53, 0x4a, 0xfb, 0x1c, 0x1b, 0x59, 0x0c, 0x7d, 0x00, 0xb5, 0x15, 0x8f, 0x38, 0x86, 0xaa, 0x60,
0xaa, 0x44, 0x4c, 0xe2, 0x2c, 0xf6, 0x41, 0xf7, 0x97, 0xec, 0xd2, 0x77, 0xbc, 0x4b, 0x32, 0x99,
0x59, 0x1e, 0x71, 0xec, 0x7a, 0xb1, 0xa9, 0xb5, 0x73, 0x47, 0xb9, 0xba, 0x76, 0xa0, 0xe1, 0x5a,
0x44, 0xed, 0xce, 0x2c, 0xcf, 0xb4, 0xd1, 0x63, 0xd8, 0xba, 0xc9, 0x1f, 0xd6, 0xb7, 0x9b, 0xd9,
0x76, 0x0e, 0x6f, 0xa6, 0x59, 0x43, 0xf4, 0x10, 0x36, 0x5d, 0x2b, 0x64, 0x64, 0xe6, 0x2f, 0xc8,
0x62, 0x79, 0x71, 0x45, 0xaf, 0xeb, 0x35, 0x71, 0x3a, 0x55, 0x0e, 0x9f, 0xf8, 0x8b, 0x33, 0x01,
0xa2, 0x07, 0x00, 0xe2, 0x98, 0x85, 0xaa, 0xf5, 0x92, 0xb0, 0xb8, 0xc4, 0x11, 0xa1, 0x26, 0xfa,
0x04, 0xca, 0x22, 0x3c, 0xc8, 0xcc, 0xf1, 0x58, 0x58, 0x87, 0x66, 0xb6, 0x5d, 0x3e, 0xd4, 0xf7,
0x5d, 0x8f, 0x47, 0x0a, 0xe6, 0x94, 0x13, 0xc7, 0x63, 0x18, 0x82, 0xe8, 0x67, 0x88, 0x6c, 0xd8,
0xe6, 0x61, 0x41, 0x26, 0xcb, 0x90, 0xf9, 0x73, 0x12, 0xd0, 0x89, 0x1f, 0xd8, 0x61, 0xbd, 0x2c,
0x96, 0xfe, 0x78, 0x3f, 0x8e, 0xb6, 0xfd, 0xdb, 0xe1, 0xb5, 0xdf, 0xa3, 0x21, 0xeb, 0x8a, 0x75,
0x58, 0x2e, 0x33, 0x3c, 0x16, 0x5c, 0xe3, 0x2d, 0xfb, 0x26, 0x8e, 0x3e, 0x02, 0x64, 0xb9, 0xae,
0xff, 0x8a, 0x84, 0xd4, 0x9d, 0x12, 0xe5, 0xcb, 0xfa, 0x66, 0x53, 0x6b, 0x17, 0xb1, 0x2e, 0x28,
0x23, 0xea, 0x4e, 0x95, 0x78, 0xf4, 0x13, 0xa8, 0x0a, 0x9d, 0xa6, 0xd4, 0x62, 0xcb, 0x80, 0x86,
0x75, 0xbd, 0x99, 0x6d, 0xd7, 0x0e, 0xb7, 0x94, 0x21, 0xc7, 0x12, 0x3e, 0x72, 0x18, 0xae, 0x70,
0x3e, 0xf5, 0x1d, 0xa2, 0x3d, 0x28, 0xcd, 0xad, 0x1f, 0xc8, 0xc2, 0x0a, 0x58, 0x58, 0xdf, 0x6a,
0x6a, 0xed, 0x2a, 0x2e, 0xce, 0xad, 0x1f, 0xce, 0xf8, 0x37, 0xda, 0x87, 0x6d, 0xcf, 0x27, 0x8e,
0x37, 0x75, 0x9d, 0xcb, 0x19, 0x23, 0xcb, 0x85, 0x6d, 0x31, 0x1a, 0xd6, 0x91, 0xd0, 0x61, 0xcb,
0xf3, 0x4d, 0x45, 0x39, 0x97, 0x84, 0x46, 0x0f, 0xee, 0xad, 0xb7, 0x8f, 0x67, 0x10, 0x77, 0x10,
0x4f, 0xaa, 0x1c, 0xe6, 0x3f, 0xd1, 0x0e, 0xe4, 0x5f, 0x5a, 0xee, 0x92, 0x8a, 0xac, 0xaa, 0x60,
0xf9, 0xf1, 0xf3, 0xcc, 0xa7, 0x5a, 0x6b, 0x06, 0xdb, 0xe3, 0xc0, 0x9a, 0x5c, 0xdd, 0x48, 0xcc,
0x9b, 0x79, 0xa5, 0xdd, 0xce, 0xab, 0x3b, 0xf4, 0xcd, 0xdc, 0xa1, 0x6f, 0xeb, 0x4b, 0xd8, 0x14,
0x1e, 0x3e, 0xa6, 0xf4, 0x75, 0xe9, 0xbf, 0x0b, 0x3c, 0xb9, 0x45, 0x26, 0xc8, 0x12, 0x50, 0xb0,
0xe6, 0x3c, 0x09, 0x5a, 0x36, 0xe8, 0xab, 0xf5, 0xe1, 0xc2, 0xf7, 0x42, 0xca, 0x73, 0x9b, 0x07,
0x00, 0x8f, 0x60, 0x9e, 0x20, 0x22, 0x35, 0x34, 0xb1, 0xaa, 0xa6, 0xf0, 0x63, 0x4a, 0x45, 0x72,
0x3c, 0x94, 0xf9, 0x48, 0x5c, 0x7f, 0x72, 0xc5, 0x8b, 0x80, 0x75, 0xad, 0xc4, 0x57, 0x39, 0xdc,
0xf7, 0x27, 0x57, 0x3d, 0x0e, 0xb6, 0xbe, 0x97, 0x75, 0x6a, 0xec, 0x8b, 0xbd, 0xfe, 0x83, 0xe3,
0x68, 0x41, 0x5e, 0xc4, 0xa2, 0x10, 0x5b, 0x3e, 0xac, 0x24, 0x83, 0x1a, 0x4b, 0x52, 0xeb, 0x7b,
0xd8, 0x4e, 0x09, 0x57, 0x56, 0x34, 0xa0, 0xb8, 0x08, 0xa8, 0x33, 0xb7, 0x2e, 0xa9, 0x92, 0x1c,
0x7f, 0xa3, 0x36, 0x6c, 0x4c, 0x2d, 0xc7, 0x5d, 0x06, 0x91, 0xe0, 0x5a, 0x14, 0x64, 0x12, 0xc5,
0x11, 0xb9, 0x75, 0x1f, 0x1a, 0x98, 0x86, 0x94, 0x9d, 0x3a, 0x61, 0xe8, 0xf8, 0x5e, 0xd7, 0xf7,
0x58, 0xe0, 0xbb, 0xca, 0x82, 0xd6, 0x03, 0xd8, 0x5b, 0x4b, 0x95, 0x2a, 0xf0, 0xc5, 0x5f, 0x2f,
0x69, 0x70, 0xbd, 0x7e, 0xf1, 0xd7, 0xb0, 0xb7, 0x96, 0xaa, 0xf4, 0xff, 0x08, 0xf2, 0x0b, 0xcb,
0x09, 0xb8, 0xef, 0x79, 0x52, 0xde, 0x4b, 0x24, 0xe5, 0x99, 0xe5, 0x04, 0x27, 0x4e, 0xc8, 0xfc,
0xe0, 0x1a, 0x4b, 0xa6, 0x67, 0xb9, 0xa2, 0xa6, 0x67, 0x5a, 0xbf, 0xd7, 0xa0, 0x9c, 0x20, 0xf2,
0xd4, 0xf0, 0x7c, 0x9b, 0x92, 0x69, 0xe0, 0xcf, 0xa3, 0x43, 0xe0, 0xc0, 0x71, 0xe0, 0xcf, 0x79,
0x4c, 0x08, 0x22, 0xf3, 0x55, 0x00, 0x17, 0xf8, 0xe7, 0xd8, 0x47, 0x1f, 0xc3, 0xc6, 0x4c, 0x0a,
0x10, 0x65, 0xb3, 0x7c, 0xb8, 0x7d, 0x63, 0xef, 0x9e, 0xc5, 0x2c, 0x1c, 0xf1, 0x3c, 0xcb, 0x15,
0xb3, 0x7a, 0xee, 0x59, 0xae, 0x98, 0xd3, 0xf3, 0xcf, 0x72, 0xc5, 0xbc, 0x5e, 0x78, 0x96, 0x2b,
0x16, 0xf4, 0x8d, 0xd6, 0x3f, 0x34, 0x28, 0x46, 0xdc, 0x5c, 0x13, 0x7e, 0xa4, 0x84, 0xc7, 0x85,
0x0a, 0xa6, 0x22, 0x07, 0xc6, 0xce, 0x9c, 0xa2, 0x26, 0x54, 0x04, 0x31, 0x1d, 0xa2, 0xc0, 0xb1,
0x8e, 0x08, 0x53, 0x51, 0xcf, 0x23, 0x0e, 0x11, 0x8f, 0x39, 0x55, 0xcf, 0x25, 0x4b, 0xd4, 0xb5,
0xc2, 0xe5, 0x64, 0x42, 0xc3, 0x50, 0xee, 0x92, 0x97, 0x2c, 0x0a, 0x13, 0x1b, 0x3d, 0x84, 0xcd,
0x88, 0x25, 0xda, 0xab, 0x20, 0xe3, 0x55, 0xc1, 0x6a, 0xbb, 0x36, 0xe8, 0x49, 0xbe, 0xf9, 0xaa,
0x83, 0xd4, 0x56, 0x8c, 0x7c, 0x53, 0x69, 0x7c, 0xeb, 0xd7, 0xb0, 0x2b, 0x5c, 0x79, 0x16, 0xf8,
0x17, 0xd6, 0x85, 0xe3, 0x3a, 0xec, 0x3a, 0x0a, 0x72, 0x6e, 0x78, 0xe0, 0xcf, 0x09, 0x3f, 0xdb,
0xc8, 0x05, 0x1c, 0x18, 0xf8, 0x36, 0xe5, 0x2e, 0x60, 0xbe, 0x24, 0x29, 0x17, 0x30, 0x5f, 0x10,
0x92, 0xcd, 0x39, 0x9b, 0x6a, 0xce, 0xad, 0x2b, 0xa8, 0xdf, 0xde, 0x4b, 0xc5, 0x4c, 0x13, 0xca,
0x8b, 0x15, 0x2c, 0xb6, 0xd3, 0x70, 0x12, 0x4a, 0xfa, 0x36, 0xf3, 0x66, 0xdf, 0xb6, 0xfe, 0xa6,
0xc1, 0xd6, 0xd1, 0xd2, 0x71, 0xed, 0x54, 0xe2, 0x26, 0xb5, 0xd3, 0xd2, 0xa3, 0xc3, 0xba, 0xb9,
0x20, 0xb3, 0x76, 0x2e, 0xf8, 0x68, 0x4d, 0x63, 0xcd, 0x8a, 0xc6, 0x9a, 0x59, 0xd3, 0x56, 0xdf,
0x85, 0xf2, 0xaa, 0x4b, 0x86, 0xf5, 0x5c, 0x33, 0xdb, 0xae, 0x60, 0x98, 0x45, 0x2d, 0x32, 0xbc,
0x35, 0x66, 0xe4, 0x6f, 0x8d, 0x19, 0xad, 0x4f, 0x01, 0x25, 0x6d, 0x51, 0x67, 0x16, 0x97, 0x18,
0xed, 0xee, 0x12, 0x73, 0x1f, 0x1a, 0xa3, 0xe5, 0x45, 0x38, 0x09, 0x9c, 0x0b, 0x7a, 0xc2, 0xdc,
0x89, 0xf1, 0x92, 0x7a, 0x2c, 0x8c, 0x12, 0xf9, 0x9f, 0x39, 0x28, 0xc5, 0x28, 0xaf, 0xe0, 0x8e,
0x37, 0xf1, 0xe7, 0x91, 0x5d, 0x1e, 0x75, 0xb9, 0x69, 0xb2, 0x6f, 0x6c, 0x45, 0xa4, 0xae, 0xa4,
0x98, 0x36, 0xe7, 0x4f, 0x9d, 0x83, 0xe2, 0xcf, 0x48, 0xfe, 0xe4, 0x31, 0x48, 0xfe, 0x36, 0xe8,
0xb1, 0xfc, 0x19, 0x73, 0x27, 0xf1, 0xb9, 0xe1, 0x5a, 0x84, 0x73, 0x65, 0x24, 0x67, 0x2c, 0x39,
0xe2, 0xcc, 0x49, 0xce, 0x08, 0x57, 0x9c, 0xef, 0x41, 0x85, 0xa7, 0x4c, 0xc8, 0xac, 0xf9, 0x82,
0x78, 0xa1, 0x38, 0xbc, 0x1c, 0x2e, 0xc7, 0xd8, 0x20, 0x44, 0x5f, 0x00, 0x50, 0x6e, 0x1f, 0x61,
0xd7, 0x0b, 0x2a, 0xb2, 0xa6, 0x76, 0xf8, 0x4e, 0x22, 0x76, 0xe2, 0x03, 0xd8, 0x17, 0xff, 0x8e,
0xaf, 0x17, 0x14, 0x97, 0x68, 0xf4, 0x13, 0x7d, 0x09, 0xd5, 0xa9, 0x1f, 0xbc, 0xb2, 0x02, 0x9b,
0x08, 0x50, 0x55, 0x96, 0xdd, 0x84, 0x84, 0x63, 0x49, 0x17, 0xcb, 0x4f, 0xde, 0xc2, 0x95, 0x69,
0xe2, 0x1b, 0x3d, 0x07, 0x14, 0xad, 0x17, 0x85, 0x40, 0x0a, 0x29, 0x0a, 0x21, 0x7b, 0xb7, 0x85,
0xf0, 0x3a, 0x1e, 0x09, 0xd2, 0xa7, 0x37, 0x30, 0xf4, 0x19, 0x54, 0x42, 0xca, 0x98, 0x4b, 0x95,
0x98, 0x92, 0x10, 0x73, 0x2f, 0x35, 0xf6, 0x70, 0x72, 0x24, 0xa1, 0x1c, 0xae, 0x3e, 0xd1, 0x11,
0x6c, 0xba, 0x8e, 0x77, 0x95, 0x54, 0x03, 0xc4, 0xfa, 0x7a, 0x62, 0x7d, 0xdf, 0xf1, 0xae, 0x92,
0x3a, 0x54, 0xdd, 0x24, 0xd0, 0xfa, 0x1c, 0x4a, 0xf1, 0x29, 0xa1, 0x32, 0x6c, 0x9c, 0x0f, 0x9e,
0x0f, 0x86, 0xdf, 0x0c, 0xf4, 0xb7, 0x50, 0x11, 0x72, 0x23, 0x63, 0xd0, 0xd3, 0x35, 0x0e, 0x63,
0xa3, 0x6b, 0x98, 0x2f, 0x0c, 0x3d, 0xc3, 0x3f, 0x8e, 0x87, 0xf8, 0x9b, 0x0e, 0xee, 0xe9, 0xd9,
0xa3, 0x0d, 0xc8, 0x8b, 0x7d, 0x5b, 0x7f, 0xd2, 0xa0, 0x28, 0x3c, 0xe8, 0x4d, 0x7d, 0xf4, 0xff,
0x10, 0x07, 0x97, 0xa8, 0x7f, 0xbc, 0x27, 0x8b, 0xa8, 0xab, 0xe2, 0x38, 0x60, 0xc6, 0x0a, 0xe7,
0xcc, 0x71, 0x68, 0xc4, 0xcc, 0x19, 0xc9, 0x1c, 0x11, 0x62, 0xe6, 0xc7, 0x09, 0xc9, 0xa9, 0xaa,
0x94, 0xc3, 0x9b, 0x11, 0x21, 0x2a, 0xc2, 0xc9, 0xf1, 0x37, 0x55, 0xac, 0x13, 0xe3, 0xaf, 0xe2,
0x6d, 0xfd, 0x14, 0x2a, 0x49, 0x9f, 0xa3, 0x47, 0x90, 0x73, 0xbc, 0xa9, 0xaf, 0x12, 0x71, 0xfb,
0x46, 0x70, 0x71, 0x23, 0xb1, 0x60, 0x68, 0x21, 0xd0, 0x6f, 0xfa, 0xb9, 0x55, 0x85, 0x72, 0xc2,
0x69, 0xad, 0xbf, 0x6b, 0x50, 0x4d, 0x39, 0xe1, 0xdf, 0x96, 0x8e, 0xbe, 0x80, 0xca, 0x2b, 0x27,
0xa0, 0x24, 0x39, 0x21, 0xd4, 0x0e, 0x1b, 0xe9, 0x09, 0x21, 0xfa, 0xbf, 0xeb, 0xdb, 0x14, 0x97,
0x39, 0xbf, 0x02, 0xd0, 0x2f, 0xa0, 0xa6, 0x56, 0x12, 0x9b, 0x32, 0xcb, 0x71, 0xc5, 0x51, 0xd5,
0x52, 0xe1, 0xa1, 0x78, 0x7b, 0x82, 0x8e, 0xab, 0xd3, 0xe4, 0x27, 0xfa, 0x70, 0x25, 0x20, 0x64,
0x81, 0xe3, 0x5d, 0x8a, 0xf3, 0x2b, 0xc5, 0x6c, 0x23, 0x01, 0xf2, 0x5e, 0x5f, 0x55, 0xf3, 0xe5,
0x88, 0x59, 0x6c, 0x19, 0xa2, 0x8f, 0x21, 0x1f, 0x32, 0x4b, 0x55, 0xb2, 0x5a, 0x2a, 0xb7, 0x12,
0x8c, 0x14, 0x4b, 0xae, 0xd4, 0x80, 0x94, 0xb9, 0x35, 0x20, 0xe5, 0x79, 0xc5, 0x90, 0x85, 0xb6,
0x7c, 0x88, 0x94, 0xf1, 0x27, 0xe3, 0x7e, 0xb7, 0xc3, 0x18, 0x9d, 0x2f, 0x18, 0x96, 0x0c, 0xaa,
0x01, 0x7e, 0x09, 0xd0, 0x75, 0x82, 0xc9, 0xd2, 0x61, 0xcf, 0xe9, 0x35, 0x6f, 0x6b, 0x51, 0x45,
0x97, 0x65, 0xaf, 0x30, 0x91, 0x55, 0x7c, 0x17, 0x36, 0xa2, 0x42, 0x24, 0xeb, 0x5b, 0x61, 0x26,
0x0a, 0x50, 0xeb, 0xcf, 0x39, 0xd8, 0x53, 0x2e, 0x95, 0xde, 0x60, 0x34, 0x98, 0xd0, 0x45, 0x3c,
0x39, 0x3f, 0x85, 0x9d, 0x55, 0x51, 0x95, 0x1b, 0x91, 0x68, 0x1a, 0x2f, 0x1f, 0xbe, 0x9d, 0xb0,
0x74, 0xa5, 0x06, 0x46, 0x71, 0xb1, 0x5d, 0xa9, 0x76, 0x90, 0x10, 0x64, 0xcd, 0xfd, 0xa5, 0xa7,
0x42, 0x54, 0x56, 0x3c, 0xb4, 0x0a, 0x67, 0x4e, 0x12, 0x11, 0xfd, 0x08, 0xe2, 0x20, 0x27, 0xf4,
0x87, 0x85, 0x13, 0x5c, 0x8b, 0xea, 0x57, 0x5d, 0x95, 0x5b, 0x43, 0xa0, 0xb7, 0xc6, 0xd9, 0xcc,
0xed, 0x71, 0xf6, 0x33, 0x68, 0xc4, 0xd9, 0xa1, 0x6e, 0xba, 0xd4, 0x8e, 0xbb, 0xdf, 0x86, 0xd0,
0x61, 0x37, 0xe2, 0xc0, 0x11, 0x83, 0x6a, 0x81, 0x07, 0xb0, 0x93, 0x48, 0xad, 0x95, 0xea, 0x32,
0x13, 0xd1, 0x2a, 0xbb, 0x92, 0xaa, 0xc7, 0x2b, 0x94, 0xea, 0x39, 0xa9, 0x7a, 0x04, 0x2b, 0xd5,
0x7f, 0x05, 0xb5, 0x1b, 0x37, 0xc1, 0xa2, 0xf0, 0xfb, 0xcf, 0x6e, 0x57, 0xd6, 0x75, 0xee, 0xd9,
0x5f, 0x73, 0x1d, 0xac, 0x4e, 0x52, 0x57, 0xc1, 0x07, 0x00, 0xbe, 0xe7, 0xf8, 0x1e, 0xb9, 0x70,
0xfd, 0x0b, 0x51, 0x70, 0x2b, 0xb8, 0x24, 0x90, 0x23, 0xd7, 0xbf, 0x68, 0x7c, 0x05, 0xe8, 0xbf,
0xbc, 0x72, 0xfd, 0x45, 0x83, 0xfb, 0xeb, 0x55, 0x54, 0x7d, 0xfe, 0x7f, 0x16, 0x42, 0x9f, 0x41,
0xc1, 0x9a, 0x30, 0xc7, 0xf7, 0x54, 0x65, 0x78, 0x3f, 0xb1, 0x14, 0xd3, 0xd0, 0x77, 0x5f, 0xd2,
0x13, 0xdf, 0xb5, 0x95, 0x32, 0x1d, 0xc1, 0x8a, 0xd5, 0x92, 0x54, 0xd2, 0x65, 0xd3, 0x49, 0xf7,
0xf8, 0xb7, 0x39, 0xa8, 0xa6, 0x2a, 0x43, 0xba, 0x35, 0x54, 0xa1, 0x34, 0x18, 0x92, 0x9e, 0x31,
0xee, 0x98, 0x7d, 0x5d, 0x43, 0x3a, 0x54, 0x86, 0x03, 0x73, 0x38, 0x20, 0x3d, 0xa3, 0x3b, 0xec,
0xf1, 0x26, 0xf1, 0x36, 0x6c, 0xf5, 0xcd, 0xc1, 0x73, 0x32, 0x18, 0x8e, 0x89, 0xd1, 0x37, 0x9f,
0x9a, 0x47, 0x7d, 0x43, 0xcf, 0xa2, 0x1d, 0xd0, 0x87, 0x03, 0xd2, 0x3d, 0xe9, 0x98, 0x03, 0x32,
0x36, 0x4f, 0x8d, 0xe1, 0xf9, 0x58, 0xcf, 0x71, 0x94, 0x67, 0x33, 0x31, 0xbe, 0xed, 0x1a, 0x46,
0x6f, 0x44, 0x4e, 0x3b, 0xdf, 0xea, 0x79, 0x54, 0x87, 0x1d, 0x73, 0x30, 0x3a, 0x3f, 0x3e, 0x36,
0xbb, 0xa6, 0x31, 0x18, 0x93, 0xa3, 0x4e, 0xbf, 0x33, 0xe8, 0x1a, 0x7a, 0x01, 0xdd, 0x03, 0x64,
0x0e, 0xba, 0xc3, 0xd3, 0xb3, 0xbe, 0x31, 0x36, 0x48, 0xd4, 0x8c, 0x36, 0xd0, 0x36, 0x6c, 0x0a,
0x39, 0x9d, 0x5e, 0x8f, 0x1c, 0x77, 0xcc, 0xbe, 0xd1, 0xd3, 0x8b, 0x5c, 0x13, 0xc5, 0x31, 0x22,
0x3d, 0x73, 0xd4, 0x39, 0xe2, 0x70, 0x89, 0xef, 0x69, 0x0e, 0x5e, 0x0c, 0xcd, 0xae, 0x41, 0xba,
0x5c, 0x2c, 0x47, 0x81, 0x33, 0x47, 0xe8, 0xf9, 0xa0, 0x67, 0xe0, 0xb3, 0x8e, 0xd9, 0xd3, 0xcb,
0x68, 0x0f, 0x76, 0x23, 0xd8, 0xf8, 0xf6, 0xcc, 0xc4, 0xdf, 0x91, 0xf1, 0x70, 0x48, 0x46, 0xc3,
0xe1, 0x40, 0xaf, 0x24, 0x25, 0x71, 0x6b, 0x87, 0x67, 0xc6, 0x40, 0xaf, 0xa2, 0x5d, 0xd8, 0x3e,
0x3d, 0x3b, 0x23, 0x11, 0x25, 0x32, 0xb6, 0xc6, 0xd9, 0x3b, 0xbd, 0x1e, 0x36, 0x46, 0x23, 0x72,
0x6a, 0x8e, 0x4e, 0x3b, 0xe3, 0xee, 0x89, 0xbe, 0xc9, 0x4d, 0x1a, 0x19, 0x63, 0x32, 0x1e, 0x8e,
0x3b, 0xfd, 0x15, 0xae, 0x73, 0x85, 0x56, 0x38, 0xdf, 0xb4, 0x3f, 0xfc, 0x46, 0xdf, 0xe2, 0x07,
0xce, 0xe1, 0xe1, 0x0b, 0xa5, 0x22, 0xe2, 0xb6, 0x2b, 0xf7, 0x44, 0x7b, 0xea, 0xdb, 0x1c, 0x34,
0x07, 0x2f, 0x3a, 0x7d, 0xb3, 0x47, 0x9e, 0x1b, 0xdf, 0x89, 0x66, 0xbe, 0xc3, 0x41, 0xa9, 0x19,
0x39, 0xc3, 0xc3, 0xa7, 0x5c, 0x11, 0xfd, 0x6d, 0x84, 0xa0, 0xd6, 0x35, 0x71, 0xf7, 0xbc, 0xdf,
0xc1, 0x04, 0x0f, 0xcf, 0xc7, 0x86, 0x7e, 0xef, 0xf1, 0x1f, 0x35, 0xa8, 0x24, 0x8b, 0x35, 0xf7,
0xba, 0x39, 0x20, 0xc7, 0x7d, 0xf3, 0xe9, 0xc9, 0x58, 0x06, 0xc1, 0xe8, 0xbc, 0xcb, 0x5d, 0x66,
0xf0, 0x21, 0x01, 0x41, 0x4d, 0x1e, 0x7a, 0x6c, 0x6c, 0x86, 0xef, 0xa5, 0xb0, 0xc1, 0x50, 0xc9,
0xcd, 0x72, 0xe5, 0x15, 0x68, 0x60, 0x3c, 0xc4, 0x7a, 0x0e, 0x7d, 0x00, 0x4d, 0x85, 0x70, 0xbf,
0x62, 0x6c, 0x74, 0xc7, 0xe4, 0xac, 0xf3, 0xdd, 0x29, 0x77, 0xbb, 0x0c, 0xb2, 0x91, 0x9e, 0x47,
0xef, 0xc2, 0x5e, 0xcc, 0xb5, 0x2e, 0x2e, 0x1e, 0x7f, 0x0e, 0xf5, 0xbb, 0x82, 0x1e, 0x01, 0x14,
0x46, 0xc6, 0x78, 0xdc, 0x37, 0xe4, 0x60, 0x73, 0x2c, 0x03, 0x17, 0xa0, 0x80, 0x8d, 0xd1, 0xf9,
0xa9, 0xa1, 0x67, 0x0e, 0xff, 0x5a, 0x84, 0x82, 0x98, 0xb4, 0x03, 0xf4, 0x15, 0x54, 0x13, 0x8f,
0x4d, 0x2f, 0x0e, 0xd1, 0x83, 0xd7, 0x3e, 0x43, 0x35, 0xa2, 0x2b, 0xbb, 0x82, 0x0f, 0x34, 0x74,
0x04, 0xb5, 0xe4, 0xab, 0xcb, 0x8b, 0x43, 0x94, 0x1c, 0x50, 0xd7, 0x3c, 0xc8, 0xac, 0x91, 0xf1,
0x1c, 0x74, 0x23, 0x64, 0xce, 0x9c, 0xf7, 0x49, 0xf5, 0x2e, 0x82, 0x1a, 0xc9, 0x04, 0x4f, 0x3f,
0xb6, 0x34, 0xf6, 0xd6, 0xd2, 0x54, 0xc9, 0xf9, 0x9a, 0xcf, 0x24, 0xf1, 0xcb, 0xc4, 0x2d, 0x83,
0xd2, 0xcf, 0x21, 0x8d, 0x77, 0xee, 0x22, 0xab, 0xd7, 0x84, 0xec, 0xef, 0x32, 0xdc, 0xc6, 0x6a,
0x82, 0xb6, 0xe6, 0x94, 0x6e, 0x08, 0x5d, 0xd3, 0xb9, 0x91, 0x0d, 0xdb, 0x6b, 0x5e, 0x2d, 0xd0,
0x87, 0xe9, 0x3a, 0x76, 0xc7, 0x9b, 0x47, 0xe3, 0xe1, 0x9b, 0xd8, 0x94, 0xf1, 0x36, 0x6c, 0xaf,
0x79, 0xde, 0x48, 0xed, 0x72, 0xf7, 0xe3, 0x48, 0x6a, 0x97, 0xd7, 0xbd, 0x92, 0x7c, 0x0f, 0xfa,
0xcd, 0xdb, 0x30, 0x6a, 0xdd, 0x5c, 0x7b, 0xfb, 0x5a, 0xde, 0x78, 0xff, 0xb5, 0x3c, 0x4a, 0xb8,
0x09, 0xb0, 0xba, 0x30, 0xa2, 0xfb, 0x89, 0x25, 0xb7, 0xee, 0xc4, 0x8d, 0x07, 0x77, 0x50, 0x95,
0xa8, 0x31, 0x6c, 0xaf, 0xb9, 0x41, 0xa6, 0x4e, 0xe3, 0xee, 0x1b, 0x66, 0x63, 0x67, 0xdd, 0x45,
0xeb, 0x40, 0x43, 0xa7, 0x32, 0xc0, 0xa2, 0x17, 0xd4, 0x37, 0x64, 0x4c, 0x7d, 0xfd, 0x40, 0xb8,
0x0c, 0x45, 0x68, 0x1d, 0x68, 0x68, 0x08, 0x95, 0x64, 0x96, 0xbc, 0x31, 0x7d, 0xde, 0x28, 0x70,
0x0a, 0x9b, 0xa9, 0x66, 0xec, 0x07, 0xe8, 0xd1, 0x1b, 0x47, 0x0a, 0x79, 0x62, 0xa9, 0x08, 0x78,
0xcd, 0xec, 0xd1, 0xd6, 0x0e, 0xb4, 0xa3, 0x4f, 0x7e, 0xf9, 0xe4, 0xd2, 0x61, 0xb3, 0xe5, 0xc5,
0xfe, 0xc4, 0x9f, 0x3f, 0x11, 0x0f, 0xa4, 0x9e, 0xe3, 0x5d, 0x7a, 0x94, 0xbd, 0xf2, 0x83, 0xab,
0x27, 0xae, 0x67, 0x3f, 0x11, 0x69, 0xf0, 0x24, 0x16, 0x79, 0x51, 0x10, 0x7f, 0x42, 0xf9, 0xd1,
0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xbf, 0xfc, 0xb7, 0xb8, 0x72, 0x19, 0x00, 0x00,
// 2823 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xcb, 0x7a, 0xdb, 0xc6,
0x15, 0x0e, 0x78, 0x13, 0x79, 0x78, 0x83, 0x46, 0xb2, 0xc5, 0x52, 0xbe, 0x30, 0x4c, 0x62, 0xb3,
0x6e, 0x22, 0x3b, 0x6a, 0xbf, 0x24, 0x6d, 0x2e, 0x0d, 0x45, 0x42, 0x16, 0x6c, 0x8a, 0x54, 0x86,
0x94, 0x13, 0x27, 0x8b, 0x29, 0x44, 0x0e, 0x45, 0x54, 0xb8, 0xb0, 0xc0, 0xd0, 0xb6, 0x76, 0xed,
0xae, 0x5f, 0x1f, 0xa0, 0xcf, 0xd0, 0x55, 0x9f, 0xa0, 0xdf, 0xd7, 0xae, 0xfb, 0x12, 0xdd, 0xf6,
0x09, 0xba, 0xee, 0x37, 0x83, 0x01, 0x08, 0x48, 0x94, 0xe4, 0x5e, 0x36, 0x36, 0xf1, 0x9f, 0x7f,
0xce, 0x9c, 0x99, 0x39, 0xb7, 0x19, 0xc1, 0x6d, 0xcf, 0x5d, 0x30, 0xea, 0x79, 0xf3, 0xf1, 0xe3,
0xe0, 0xd7, 0xce, 0xdc, 0x73, 0x99, 0x8b, 0x0a, 0x11, 0x5e, 0x2f, 0x78, 0xf3, 0x71, 0x80, 0x36,
0xff, 0xb8, 0x06, 0x68, 0x48, 0x9d, 0xc9, 0x91, 0x71, 0x6e, 0x53, 0x87, 0x61, 0xfa, 0x9b, 0x05,
0xf5, 0x19, 0x42, 0x90, 0x99, 0x50, 0x9f, 0xd5, 0x94, 0x86, 0xd2, 0x2a, 0x61, 0xf1, 0x1b, 0xa9,
0x90, 0x36, 0x6c, 0x56, 0x4b, 0x35, 0x94, 0x56, 0x1a, 0xf3, 0x9f, 0xe8, 0x47, 0x90, 0x37, 0x6c,
0x46, 0x6c, 0xdf, 0x60, 0xb5, 0x92, 0x80, 0xd7, 0x0c, 0x9b, 0x1d, 0xfa, 0x06, 0x43, 0xef, 0x42,
0x69, 0x1e, 0xa8, 0x24, 0x33, 0xc3, 0x9f, 0xd5, 0xd2, 0x42, 0x51, 0x51, 0x62, 0x07, 0x86, 0x3f,
0x43, 0x2d, 0x50, 0xa7, 0xa6, 0x63, 0x58, 0x64, 0x6c, 0xb1, 0x57, 0x64, 0x42, 0x2d, 0x66, 0xd4,
0x32, 0x0d, 0xa5, 0x95, 0xc5, 0x15, 0x81, 0x77, 0x2c, 0xf6, 0xaa, 0xcb, 0xd1, 0xb8, 0x32, 0x63,
0x32, 0xf1, 0x6a, 0x9b, 0x09, 0x65, 0xed, 0xc9, 0xc4, 0x43, 0x0f, 0xa1, 0x1a, 0x52, 0xbc, 0x60,
0x0d, 0xb5, 0x6c, 0x43, 0x69, 0x15, 0x70, 0x65, 0x9e, 0x5c, 0xd9, 0x43, 0xa8, 0x32, 0xd3, 0xa6,
0xee, 0x82, 0x11, 0x9f, 0x8e, 0x5d, 0x67, 0xe2, 0xd7, 0x72, 0xc1, 0xa4, 0x12, 0x1e, 0x06, 0x28,
0x6a, 0x42, 0x79, 0x4a, 0x29, 0xb1, 0x4c, 0xdb, 0x64, 0x84, 0xaf, 0x70, 0x4d, 0xac, 0xb0, 0x38,
0xa5, 0xb4, 0xc7, 0xb1, 0xa1, 0xc1, 0xd0, 0xfb, 0x50, 0x59, 0x72, 0xc4, 0x36, 0x94, 0x05, 0xa9,
0x14, 0x92, 0xc4, 0x5e, 0xec, 0x80, 0xea, 0x2e, 0xd8, 0xa9, 0x6b, 0x3a, 0xa7, 0x64, 0x3c, 0x33,
0x1c, 0x62, 0x4e, 0x6a, 0xf9, 0x86, 0xd2, 0xca, 0xec, 0x65, 0x6a, 0xca, 0x13, 0x05, 0x57, 0x42,
0x69, 0x67, 0x66, 0x38, 0xfa, 0x04, 0x3d, 0x82, 0xf5, 0x8b, 0x7c, 0xbf, 0xb6, 0xd1, 0x48, 0xb7,
0x32, 0xb8, 0x9a, 0xa4, 0xfa, 0xe8, 0x01, 0x54, 0x2d, 0xc3, 0x67, 0x64, 0xe6, 0xce, 0xc9, 0x7c,
0x71, 0x72, 0x46, 0xcf, 0x6b, 0x15, 0xb1, 0x3b, 0x65, 0x0e, 0x1f, 0xb8, 0xf3, 0x23, 0x01, 0xa2,
0xbb, 0x00, 0x62, 0x9b, 0x85, 0xa9, 0xb5, 0x82, 0x58, 0x71, 0x81, 0x23, 0xc2, 0x4c, 0xf4, 0x31,
0x14, 0x85, 0x7b, 0x90, 0x99, 0xe9, 0x30, 0xbf, 0x06, 0x8d, 0x74, 0xab, 0xb8, 0xab, 0xee, 0x58,
0x0e, 0xf7, 0x14, 0xcc, 0x25, 0x07, 0xa6, 0xc3, 0x30, 0x78, 0xe1, 0x4f, 0x1f, 0x4d, 0x60, 0x83,
0xbb, 0x05, 0x19, 0x2f, 0x7c, 0xe6, 0xda, 0xc4, 0xa3, 0x63, 0xd7, 0x9b, 0xf8, 0xb5, 0xa2, 0x18,
0xfa, 0xb3, 0x9d, 0xc8, 0xdb, 0x76, 0x2e, 0xbb, 0xd7, 0x4e, 0x97, 0xfa, 0xac, 0x23, 0xc6, 0xe1,
0x60, 0x98, 0xe6, 0x30, 0xef, 0x1c, 0xaf, 0x4f, 0x2e, 0xe2, 0xe8, 0x43, 0x40, 0x86, 0x65, 0xb9,
0xaf, 0x89, 0x4f, 0xad, 0x29, 0x91, 0x67, 0x59, 0xab, 0x36, 0x94, 0x56, 0x1e, 0xab, 0x42, 0x32,
0xa4, 0xd6, 0x54, 0xaa, 0x47, 0x9f, 0x40, 0x59, 0xd8, 0x34, 0xa5, 0x06, 0x5b, 0x78, 0xd4, 0xaf,
0xa9, 0x8d, 0x74, 0xab, 0xb2, 0xbb, 0x2e, 0x17, 0xb2, 0x1f, 0xc0, 0x7b, 0x26, 0xc3, 0x25, 0xce,
0x93, 0xdf, 0x3e, 0xda, 0x86, 0x82, 0x6d, 0xbc, 0x21, 0x73, 0xc3, 0x63, 0x7e, 0x6d, 0xbd, 0xa1,
0xb4, 0xca, 0x38, 0x6f, 0x1b, 0x6f, 0x8e, 0xf8, 0x37, 0xda, 0x81, 0x0d, 0xc7, 0x25, 0xa6, 0x33,
0xb5, 0xcc, 0xd3, 0x19, 0x23, 0x8b, 0xf9, 0xc4, 0x60, 0xd4, 0xaf, 0x21, 0x61, 0xc3, 0xba, 0xe3,
0xea, 0x52, 0x72, 0x1c, 0x08, 0xea, 0x5d, 0xb8, 0xbd, 0x7a, 0x7d, 0x3c, 0x82, 0xf8, 0x01, 0xf1,
0xa0, 0xca, 0x60, 0xfe, 0x13, 0x6d, 0x42, 0xf6, 0x95, 0x61, 0x2d, 0xa8, 0x88, 0xaa, 0x12, 0x0e,
0x3e, 0x7e, 0x91, 0xfa, 0x4c, 0x69, 0xce, 0x60, 0x63, 0xe4, 0x19, 0xe3, 0xb3, 0x0b, 0x81, 0x79,
0x31, 0xae, 0x94, 0xcb, 0x71, 0x75, 0x85, 0xbd, 0xa9, 0x2b, 0xec, 0x6d, 0x7e, 0x05, 0x55, 0x71,
0xc2, 0xfb, 0x94, 0x5e, 0x17, 0xfe, 0x5b, 0xc0, 0x83, 0x5b, 0x44, 0x42, 0x90, 0x02, 0x72, 0x86,
0xcd, 0x83, 0xa0, 0x39, 0x01, 0x75, 0x39, 0xde, 0x9f, 0xbb, 0x8e, 0x4f, 0x79, 0x6c, 0x73, 0x07,
0xe0, 0x1e, 0xcc, 0x03, 0x44, 0x84, 0x86, 0x22, 0x46, 0x55, 0x24, 0xbe, 0x4f, 0xa9, 0x08, 0x8e,
0x07, 0x41, 0x3c, 0x12, 0xcb, 0x1d, 0x9f, 0xf1, 0x24, 0x60, 0x9c, 0x4b, 0xf5, 0x65, 0x0e, 0xf7,
0xdc, 0xf1, 0x59, 0x97, 0x83, 0xcd, 0x1f, 0x82, 0x3c, 0x35, 0x72, 0xc5, 0x5c, 0xff, 0xc1, 0x76,
0x34, 0x21, 0x2b, 0x7c, 0x51, 0xa8, 0x2d, 0xee, 0x96, 0xe2, 0x4e, 0x8d, 0x03, 0x51, 0xf3, 0x07,
0xd8, 0x48, 0x28, 0x97, 0xab, 0xa8, 0x43, 0x7e, 0xee, 0x51, 0xd3, 0x36, 0x4e, 0xa9, 0xd4, 0x1c,
0x7d, 0xa3, 0x16, 0xac, 0x4d, 0x0d, 0xd3, 0x5a, 0x78, 0xa1, 0xe2, 0x4a, 0xe8, 0x64, 0x01, 0x8a,
0x43, 0x71, 0xf3, 0x0e, 0xd4, 0x31, 0xf5, 0x29, 0x3b, 0x34, 0x7d, 0xdf, 0x74, 0x9d, 0x8e, 0xeb,
0x30, 0xcf, 0xb5, 0xe4, 0x0a, 0x9a, 0x77, 0x61, 0x7b, 0xa5, 0x34, 0x30, 0x81, 0x0f, 0xfe, 0x66,
0x41, 0xbd, 0xf3, 0xd5, 0x83, 0xbf, 0x81, 0xed, 0x95, 0x52, 0x69, 0xff, 0x87, 0x90, 0x9d, 0x1b,
0xa6, 0xc7, 0xcf, 0x9e, 0x07, 0xe5, 0xed, 0x58, 0x50, 0x1e, 0x19, 0xa6, 0x77, 0x60, 0xfa, 0xcc,
0xf5, 0xce, 0x71, 0x40, 0x7a, 0x96, 0xc9, 0x2b, 0x6a, 0xaa, 0xf9, 0x07, 0x05, 0x8a, 0x31, 0x21,
0x0f, 0x0d, 0xc7, 0x9d, 0x50, 0x32, 0xf5, 0x5c, 0x3b, 0xdc, 0x04, 0x0e, 0xec, 0x7b, 0xae, 0xcd,
0x7d, 0x42, 0x08, 0x99, 0x2b, 0x1d, 0x38, 0xc7, 0x3f, 0x47, 0x2e, 0xfa, 0x08, 0xd6, 0x66, 0x81,
0x02, 0x91, 0x36, 0x8b, 0xbb, 0x1b, 0x17, 0xe6, 0xee, 0x1a, 0xcc, 0xc0, 0x21, 0xe7, 0x59, 0x26,
0x9f, 0x56, 0x33, 0xcf, 0x32, 0xf9, 0x8c, 0x9a, 0x7d, 0x96, 0xc9, 0x67, 0xd5, 0xdc, 0xb3, 0x4c,
0x3e, 0xa7, 0xae, 0x35, 0xff, 0xa9, 0x40, 0x3e, 0x64, 0x73, 0x4b, 0xf8, 0x96, 0x12, 0xee, 0x17,
0xd2, 0x99, 0xf2, 0x1c, 0x18, 0x99, 0x36, 0x45, 0x0d, 0x28, 0x09, 0x61, 0xd2, 0x45, 0x81, 0x63,
0x6d, 0xe1, 0xa6, 0x22, 0x9f, 0x87, 0x0c, 0xe1, 0x8f, 0x19, 0x99, 0xcf, 0x03, 0x4a, 0x58, 0xb5,
0xfc, 0xc5, 0x78, 0x4c, 0x7d, 0x3f, 0x98, 0x25, 0x1b, 0x50, 0x24, 0x26, 0x26, 0x7a, 0x00, 0xd5,
0x90, 0x12, 0xce, 0x95, 0x0b, 0xfc, 0x55, 0xc2, 0x72, 0xba, 0x16, 0xa8, 0x71, 0x9e, 0xbd, 0xac,
0x20, 0x95, 0x25, 0x91, 0x4f, 0x1a, 0x2c, 0xbe, 0xd9, 0x80, 0x7b, 0x4f, 0x2f, 0x7a, 0x41, 0xc7,
0x75, 0xa6, 0xe6, 0x69, 0x78, 0xd8, 0xdf, 0xc3, 0xfd, 0x2b, 0x19, 0xf2, 0xc0, 0x3f, 0x85, 0xdc,
0x58, 0x20, 0x62, 0x7f, 0x8a, 0xbb, 0xf7, 0x63, 0xbb, 0xbe, 0x72, 0xa0, 0xa4, 0x37, 0x5f, 0xc2,
0xbd, 0xe1, 0xb5, 0xb3, 0xff, 0xf7, 0xaa, 0xdf, 0x85, 0xfb, 0xc3, 0xeb, 0xcd, 0x6e, 0xfe, 0x36,
0x05, 0x9b, 0xab, 0x08, 0xbc, 0x12, 0xce, 0x0c, 0x6b, 0x4a, 0x2c, 0x73, 0x4a, 0xa3, 0x72, 0x1d,
0xa4, 0xcf, 0x2a, 0x17, 0xf4, 0xcc, 0x29, 0x0d, 0xeb, 0xf5, 0x43, 0xa8, 0x8a, 0x22, 0xe8, 0xb9,
0x27, 0xc6, 0x89, 0x69, 0x99, 0x2c, 0x48, 0x24, 0x29, 0x5c, 0x99, 0xb9, 0xf3, 0xa3, 0x25, 0x8a,
0x6e, 0x43, 0xee, 0x35, 0xe5, 0x09, 0x50, 0x34, 0x25, 0x29, 0x2c, 0xbf, 0xd0, 0x27, 0xb0, 0x65,
0x1b, 0x6f, 0x4c, 0x7b, 0x61, 0x93, 0x65, 0x2b, 0xe1, 0x2f, 0x2c, 0xe6, 0x0b, 0x57, 0x29, 0xe3,
0x5b, 0x52, 0x1c, 0xa5, 0x64, 0x21, 0x44, 0x1d, 0xb8, 0x67, 0x9b, 0x8e, 0x18, 0x27, 0x43, 0x9e,
0x78, 0xd4, 0x32, 0xde, 0x10, 0xd3, 0x61, 0xd4, 0x7b, 0x65, 0x58, 0xc2, 0x8d, 0x32, 0x78, 0x5b,
0xb2, 0xc2, 0x04, 0xc1, 0x39, 0xba, 0xa4, 0x34, 0x7f, 0x0d, 0x5b, 0x22, 0x92, 0x63, 0x86, 0x86,
0x3b, 0xcf, 0xfd, 0xde, 0x73, 0x6d, 0xc2, 0x43, 0x2b, 0x8c, 0x40, 0x0e, 0xf4, 0xdd, 0x09, 0xe5,
0x11, 0xc8, 0xdc, 0x40, 0x24, 0x23, 0x90, 0xb9, 0x42, 0x10, 0xef, 0xcd, 0xd2, 0x89, 0xde, 0xac,
0x79, 0x06, 0xb5, 0xcb, 0x73, 0x49, 0x0f, 0x6a, 0x40, 0x31, 0xbe, 0x83, 0x7c, 0x3a, 0x05, 0xc7,
0xa1, 0x78, 0x68, 0xa7, 0x6e, 0x0e, 0xed, 0xe6, 0xdf, 0x15, 0x58, 0xdf, 0x5b, 0x98, 0xd6, 0x24,
0x91, 0xb7, 0xe3, 0xd6, 0x29, 0xc9, 0xce, 0x71, 0x55, 0x5b, 0x98, 0x5a, 0xd9, 0x16, 0x7e, 0xb8,
0xa2, 0xaf, 0x4a, 0x8b, 0xbe, 0x2a, 0xb5, 0xa2, 0xab, 0xba, 0x0f, 0xc5, 0x65, 0x93, 0xc4, 0x8f,
0x34, 0xdd, 0x2a, 0x61, 0x98, 0x85, 0x1d, 0x92, 0x7f, 0xa9, 0xcb, 0xcc, 0x5e, 0xea, 0x32, 0x9b,
0x9f, 0x01, 0x8a, 0xaf, 0x45, 0xee, 0x59, 0x54, 0x61, 0x94, 0xab, 0x2b, 0xcc, 0x1d, 0xa8, 0x0f,
0x17, 0x27, 0xfe, 0xd8, 0x33, 0x4f, 0xe8, 0x01, 0xb3, 0xc6, 0xda, 0x2b, 0xea, 0x30, 0x3f, 0x0c,
0xed, 0x7f, 0x65, 0xa0, 0x10, 0xa1, 0xbc, 0x80, 0x9b, 0xce, 0xd8, 0xb5, 0xc3, 0x75, 0x39, 0xd4,
0xe2, 0x4b, 0x0b, 0xfc, 0x7e, 0x3d, 0x14, 0x75, 0x02, 0x89, 0x3e, 0xe1, 0xfc, 0xc4, 0x3e, 0x48,
0x7e, 0x2a, 0xe0, 0xc7, 0xb7, 0x21, 0xe0, 0xb7, 0x40, 0x8d, 0xf4, 0xcf, 0x98, 0x35, 0x8e, 0xf6,
0x0d, 0x57, 0x42, 0x9c, 0x1b, 0x13, 0x30, 0x23, 0xcd, 0x21, 0x33, 0x13, 0x30, 0x43, 0x5c, 0x32,
0xdf, 0x85, 0x12, 0xcf, 0x98, 0x3e, 0x33, 0xec, 0x39, 0x71, 0x7c, 0xe9, 0xf2, 0xc5, 0x08, 0xeb,
0xfb, 0xe8, 0x4b, 0x00, 0xca, 0xd7, 0x47, 0xd8, 0xf9, 0x9c, 0x8a, 0xa4, 0x59, 0xd9, 0xbd, 0x17,
0xf3, 0x9d, 0x68, 0x03, 0x76, 0xc4, 0xbf, 0xa3, 0xf3, 0x39, 0xc5, 0x05, 0x1a, 0xfe, 0x44, 0x5f,
0x41, 0x79, 0xea, 0x7a, 0xaf, 0x0d, 0x6f, 0x42, 0x04, 0x28, 0x0b, 0xcb, 0x56, 0x4c, 0xc3, 0x7e,
0x20, 0x17, 0xc3, 0x0f, 0xde, 0xc1, 0xa5, 0x69, 0xec, 0x1b, 0x3d, 0x07, 0x14, 0x8e, 0x17, 0x75,
0x20, 0x50, 0x92, 0x17, 0x4a, 0xb6, 0x2f, 0x2b, 0xe1, 0x51, 0x1a, 0x2a, 0x52, 0xa7, 0x17, 0x30,
0xf4, 0x39, 0x94, 0x7c, 0xca, 0x98, 0x45, 0xa5, 0x9a, 0x82, 0x50, 0x73, 0x3b, 0xd1, 0xf5, 0x72,
0x71, 0xa8, 0xa1, 0xe8, 0x2f, 0x3f, 0xd1, 0x1e, 0x54, 0x2d, 0xd3, 0x39, 0x8b, 0x9b, 0x01, 0x62,
0x7c, 0x2d, 0x36, 0xbe, 0x67, 0x3a, 0x67, 0x71, 0x1b, 0xca, 0x56, 0x1c, 0x68, 0x7e, 0x01, 0x85,
0x68, 0x97, 0x50, 0x11, 0xd6, 0x8e, 0xfb, 0xcf, 0xfb, 0x83, 0x6f, 0xfb, 0xea, 0x3b, 0x28, 0x0f,
0x99, 0xa1, 0xd6, 0xef, 0xaa, 0x0a, 0x87, 0xb1, 0xd6, 0xd1, 0xf4, 0x17, 0x9a, 0x9a, 0xe2, 0x1f,
0xfb, 0x03, 0xfc, 0x6d, 0x1b, 0x77, 0xd5, 0xf4, 0xde, 0x1a, 0x64, 0xc5, 0xbc, 0xcd, 0xbf, 0x28,
0x90, 0x17, 0x27, 0xe8, 0x4c, 0x5d, 0xf4, 0x13, 0x88, 0x9c, 0x4b, 0x94, 0x3f, 0xde, 0x92, 0x09,
0xaf, 0x2b, 0xe3, 0xc8, 0x61, 0x46, 0x12, 0xe7, 0xe4, 0xc8, 0x35, 0x22, 0x72, 0x2a, 0x20, 0x87,
0x82, 0x88, 0xfc, 0x28, 0xa6, 0x39, 0x91, 0x95, 0x32, 0xb8, 0x1a, 0x0a, 0xc2, 0x1a, 0x1c, 0xbf,
0xfd, 0x24, 0x6a, 0x75, 0xec, 0xf6, 0x23, 0xb9, 0xcd, 0x4f, 0xa1, 0x14, 0x3f, 0x73, 0xf4, 0x10,
0x32, 0xa6, 0x33, 0x75, 0x65, 0x20, 0x6e, 0x5c, 0x70, 0x2e, 0xbe, 0x48, 0x2c, 0x08, 0x4d, 0x04,
0xea, 0xc5, 0x73, 0x6e, 0x96, 0xa1, 0x18, 0x3b, 0xb4, 0xe6, 0x3f, 0x14, 0x28, 0x27, 0x0e, 0xe1,
0xad, 0xb5, 0xa3, 0x2f, 0xa1, 0xf4, 0xda, 0xf4, 0x28, 0x89, 0x37, 0x88, 0x95, 0xdd, 0x7a, 0xb2,
0x41, 0x0c, 0xff, 0xef, 0xb8, 0x13, 0x8a, 0x8b, 0x9c, 0x2f, 0x01, 0xf4, 0x4b, 0xa8, 0x84, 0x85,
0x64, 0x42, 0x99, 0x61, 0x5a, 0x62, 0xab, 0x2a, 0x09, 0xf7, 0x90, 0xdc, 0xae, 0x90, 0xe3, 0xf2,
0x34, 0xfe, 0x89, 0x3e, 0x58, 0x2a, 0xf0, 0x99, 0x67, 0x3a, 0xa7, 0x62, 0xff, 0x0a, 0x11, 0x6d,
0x28, 0x40, 0xde, 0xea, 0x95, 0x65, 0x2d, 0x1b, 0x32, 0x83, 0x2d, 0x7c, 0xf4, 0x11, 0x64, 0x7d,
0x66, 0xc8, 0x4c, 0x56, 0x49, 0xc4, 0x56, 0x8c, 0x48, 0x71, 0xc0, 0x4a, 0xf4, 0xc7, 0xa9, 0x4b,
0xfd, 0x71, 0x96, 0x67, 0x8c, 0x20, 0xd1, 0x16, 0x77, 0x91, 0x5c, 0xfc, 0xc1, 0xa8, 0xd7, 0x69,
0x33, 0x46, 0xed, 0x39, 0xc3, 0x01, 0x41, 0xf6, 0x3f, 0x5f, 0x01, 0x74, 0x4c, 0x6f, 0xbc, 0x30,
0xd9, 0x73, 0x7a, 0xce, 0xcb, 0x5a, 0x98, 0xd1, 0x83, 0xb4, 0x97, 0x1b, 0x07, 0x59, 0x7c, 0x0b,
0xd6, 0xc2, 0x44, 0x14, 0xe4, 0xb7, 0xdc, 0x4c, 0x24, 0xa0, 0xe6, 0x5f, 0x33, 0xb0, 0x2d, 0x8f,
0x34, 0x38, 0x0d, 0x46, 0xbd, 0x31, 0x9d, 0x47, 0x17, 0xa7, 0xa7, 0xb0, 0xb9, 0x4c, 0xaa, 0xc1,
0x44, 0x24, 0xbc, 0x8c, 0x15, 0x77, 0x6f, 0xc5, 0x56, 0xba, 0x34, 0x03, 0xa3, 0x28, 0xd9, 0x2e,
0x4d, 0x7b, 0x12, 0x53, 0x64, 0xd8, 0xee, 0xc2, 0x91, 0x2e, 0x1a, 0x64, 0x3c, 0xb4, 0x74, 0x67,
0x2e, 0x12, 0x1e, 0xfd, 0x10, 0x22, 0x27, 0x27, 0xf4, 0xcd, 0xdc, 0xf4, 0xce, 0x45, 0xf6, 0x2b,
0x2f, 0xd3, 0xad, 0x26, 0xd0, 0x4b, 0xb7, 0x99, 0xd4, 0xe5, 0xdb, 0xcc, 0xe7, 0x50, 0x8f, 0xa2,
0x43, 0x3e, 0x74, 0xd0, 0x49, 0x54, 0xfd, 0xd6, 0x84, 0x0d, 0x5b, 0x21, 0x03, 0x87, 0x04, 0x59,
0x02, 0x9f, 0xc0, 0x66, 0x2c, 0xb4, 0x96, 0xa6, 0x07, 0x91, 0x88, 0x96, 0xd1, 0x15, 0x37, 0x3d,
0x1a, 0x21, 0x4d, 0x0f, 0x7a, 0xa1, 0x28, 0xff, 0x4b, 0xd3, 0x7f, 0x05, 0x95, 0x0b, 0x0f, 0x01,
0x79, 0x71, 0xee, 0x3f, 0xbf, 0x9c, 0x59, 0x57, 0x1d, 0xcf, 0xce, 0x8a, 0xd7, 0x80, 0xf2, 0x38,
0xf1, 0x12, 0x70, 0x17, 0xc0, 0x75, 0x4c, 0xd7, 0x21, 0x27, 0x96, 0x7b, 0x22, 0x12, 0x6e, 0x09,
0x17, 0x04, 0xb2, 0x67, 0xb9, 0x27, 0xf5, 0xaf, 0x01, 0xfd, 0x8f, 0x37, 0xee, 0xbf, 0x29, 0x70,
0x67, 0xb5, 0x89, 0xb2, 0xce, 0xff, 0xdf, 0x5c, 0xe8, 0x73, 0xc8, 0x19, 0x63, 0x66, 0xba, 0x8e,
0xcc, 0x0c, 0xef, 0xc5, 0x86, 0x62, 0xea, 0xbb, 0xd6, 0x2b, 0x7a, 0xe0, 0x5a, 0x13, 0x69, 0x4c,
0x5b, 0x50, 0xb1, 0x1c, 0x92, 0x08, 0xba, 0x74, 0x32, 0xe8, 0x1e, 0xfd, 0x2e, 0x03, 0xe5, 0x44,
0x66, 0x48, 0x96, 0x86, 0x32, 0x14, 0xfa, 0x03, 0xd2, 0xd5, 0x46, 0x6d, 0xbd, 0xa7, 0x2a, 0x48,
0x85, 0xd2, 0xa0, 0xaf, 0x0f, 0xfa, 0xa4, 0xab, 0x75, 0x06, 0x5d, 0x5e, 0x24, 0x6e, 0xc1, 0x7a,
0x4f, 0xef, 0x3f, 0x27, 0xfd, 0xc1, 0x88, 0x68, 0x3d, 0xfd, 0xa9, 0xbe, 0xd7, 0xd3, 0xd4, 0x34,
0xda, 0x04, 0x75, 0xd0, 0x27, 0x9d, 0x83, 0xb6, 0xde, 0x27, 0x23, 0xfd, 0x50, 0x1b, 0x1c, 0x8f,
0xd4, 0x0c, 0x47, 0x79, 0x34, 0x13, 0xed, 0xbb, 0x8e, 0xa6, 0x75, 0x87, 0xe4, 0xb0, 0xfd, 0x9d,
0x9a, 0x45, 0x35, 0xd8, 0xd4, 0xfb, 0xc3, 0xe3, 0xfd, 0x7d, 0xbd, 0xa3, 0x6b, 0xfd, 0x11, 0xd9,
0x6b, 0xf7, 0xda, 0xfd, 0x8e, 0xa6, 0xe6, 0xd0, 0x6d, 0x40, 0x7a, 0xbf, 0x33, 0x38, 0x3c, 0xea,
0x69, 0x23, 0x8d, 0x84, 0xc5, 0x68, 0x0d, 0x6d, 0x40, 0x55, 0xe8, 0x69, 0x77, 0xbb, 0x64, 0xbf,
0xad, 0xf7, 0xb4, 0xae, 0x9a, 0xe7, 0x96, 0x48, 0xc6, 0x90, 0x74, 0xf5, 0x61, 0x7b, 0x8f, 0xc3,
0x05, 0x3e, 0xa7, 0xde, 0x7f, 0x31, 0xd0, 0x3b, 0x1a, 0xe9, 0x70, 0xb5, 0x1c, 0x05, 0x4e, 0x0e,
0xd1, 0xe3, 0x7e, 0x57, 0xc3, 0x47, 0x6d, 0xbd, 0xab, 0x16, 0xd1, 0x36, 0x6c, 0x85, 0xb0, 0xf6,
0xdd, 0x91, 0x8e, 0x5f, 0x92, 0xd1, 0x60, 0x40, 0x86, 0x83, 0x41, 0x5f, 0x2d, 0xc5, 0x35, 0xf1,
0xd5, 0x0e, 0x8e, 0xb4, 0xbe, 0x5a, 0x46, 0x5b, 0xb0, 0x71, 0x78, 0x74, 0x44, 0x42, 0x49, 0xb8,
0xd8, 0x0a, 0xa7, 0xb7, 0xbb, 0x5d, 0xac, 0x0d, 0x87, 0xe4, 0x50, 0x1f, 0x1e, 0xb6, 0x47, 0x9d,
0x03, 0xb5, 0xca, 0x97, 0x34, 0xd4, 0x46, 0x64, 0x34, 0x18, 0xb5, 0x7b, 0x4b, 0x5c, 0xe5, 0x06,
0x2d, 0x71, 0x3e, 0x69, 0x6f, 0xf0, 0xad, 0xba, 0xce, 0x37, 0x9c, 0xc3, 0x83, 0x17, 0xd2, 0x44,
0xc4, 0xd7, 0x2e, 0x8f, 0x27, 0x9c, 0x53, 0xdd, 0xe0, 0xa0, 0xde, 0x7f, 0xd1, 0xee, 0xe9, 0x5d,
0xf2, 0x5c, 0x7b, 0x29, 0x8a, 0xf9, 0x26, 0x07, 0x03, 0xcb, 0xc8, 0x11, 0x1e, 0x3c, 0xe5, 0x86,
0xa8, 0xb7, 0x10, 0x82, 0x4a, 0x47, 0xc7, 0x9d, 0xe3, 0x5e, 0x1b, 0x13, 0x3c, 0x38, 0x1e, 0x69,
0xea, 0xed, 0x47, 0x7f, 0x56, 0xa0, 0x14, 0x4f, 0xd6, 0xfc, 0xd4, 0xf5, 0x3e, 0xd9, 0xef, 0xe9,
0x4f, 0x0f, 0x46, 0x81, 0x13, 0x0c, 0x8f, 0x3b, 0xfc, 0xc8, 0x34, 0xde, 0x24, 0x20, 0xa8, 0x04,
0x9b, 0x1e, 0x2d, 0x36, 0xc5, 0xe7, 0x92, 0x58, 0x7f, 0x20, 0xf5, 0xa6, 0xb9, 0xf1, 0x12, 0xd4,
0x30, 0x1e, 0x60, 0x35, 0x83, 0xde, 0x87, 0x86, 0x44, 0xf8, 0xb9, 0x62, 0xac, 0x75, 0x46, 0xe4,
0xa8, 0xfd, 0xf2, 0x90, 0x1f, 0x7b, 0xe0, 0x64, 0x43, 0x35, 0x8b, 0xee, 0xc3, 0x76, 0xc4, 0x5a,
0xe5, 0x17, 0x8f, 0xbe, 0x80, 0xda, 0x55, 0x4e, 0x8f, 0x00, 0x72, 0x43, 0x6d, 0x34, 0xea, 0x69,
0x41, 0x63, 0xb3, 0x1f, 0x38, 0x2e, 0x40, 0x0e, 0x6b, 0xc3, 0xe3, 0x43, 0x4d, 0x4d, 0xed, 0xfe,
0x89, 0x7f, 0x88, 0xe8, 0x41, 0x5f, 0x43, 0x39, 0xf6, 0xd6, 0xf8, 0x62, 0x17, 0xdd, 0xbd, 0xf6,
0x15, 0xb2, 0x1e, 0xbe, 0xd8, 0x48, 0xf8, 0x89, 0x82, 0xf6, 0xa0, 0x12, 0x7f, 0x74, 0x7b, 0xb1,
0x8b, 0xe2, 0x0d, 0xea, 0x8a, 0xf7, 0xb8, 0x15, 0x3a, 0x9e, 0x83, 0xaa, 0xf9, 0xcc, 0xb4, 0x79,
0x9d, 0x94, 0xcf, 0x62, 0xa8, 0x1e, 0x0f, 0xf0, 0xe4, 0x5b, 0x5b, 0x7d, 0x7b, 0xa5, 0x4c, 0xa6,
0x9c, 0x6f, 0x78, 0x4f, 0x12, 0x3d, 0x4c, 0x5d, 0x5a, 0x50, 0xf2, 0x35, 0xac, 0x7e, 0xef, 0x2a,
0xb1, 0xbc, 0x67, 0xa7, 0x7f, 0x9f, 0xe2, 0x6b, 0x2c, 0xc7, 0x64, 0x2b, 0x76, 0xe9, 0x82, 0xd2,
0x15, 0x95, 0x1b, 0x4d, 0x60, 0x63, 0xc5, 0xa3, 0x15, 0xfa, 0x20, 0x99, 0xc7, 0xae, 0x78, 0xf2,
0xaa, 0x3f, 0xb8, 0x89, 0x26, 0x17, 0x3f, 0x81, 0x8d, 0x15, 0xaf, 0x5b, 0x89, 0x59, 0xae, 0x7e,
0x1b, 0x4b, 0xcc, 0x72, 0xdd, 0x23, 0xd9, 0x1c, 0xb6, 0xae, 0x78, 0x56, 0x41, 0x3f, 0x8e, 0xa9,
0xb8, 0xfe, 0x71, 0xa6, 0xfe, 0xe8, 0x6d, 0xa8, 0xcb, 0x19, 0x87, 0x6f, 0x31, 0xe3, 0xf0, 0xed,
0x67, 0xbc, 0xe1, 0x81, 0x05, 0xfd, 0x00, 0xea, 0xc5, 0x1b, 0x3f, 0x6a, 0x5e, 0xdc, 0x9f, 0xcb,
0x4f, 0x0f, 0xf5, 0xf7, 0xae, 0xe5, 0x48, 0xe5, 0x3a, 0xc0, 0xf2, 0x52, 0x8c, 0xee, 0xc4, 0x86,
0x5c, 0xba, 0xf7, 0xd7, 0xef, 0x5e, 0x21, 0x95, 0xaa, 0x46, 0xb0, 0xb1, 0xe2, 0x96, 0x9c, 0x38,
0xf1, 0xab, 0x6f, 0xd1, 0xf5, 0xcd, 0x55, 0x97, 0xc9, 0x27, 0x0a, 0x3a, 0x0c, 0x82, 0x28, 0xfc,
0x23, 0xc1, 0x0d, 0x59, 0xa1, 0xb6, 0xba, 0xe9, 0x5d, 0xf8, 0x22, 0x7c, 0x9e, 0x28, 0x68, 0x00,
0xa5, 0x78, 0x26, 0xb8, 0x31, 0x45, 0xdc, 0xa8, 0x70, 0x0a, 0xd5, 0x44, 0xc3, 0xe1, 0x7a, 0xe8,
0xe1, 0x8d, 0x6d, 0x53, 0xb0, 0x63, 0x09, 0x2f, 0xbf, 0xa6, 0xbf, 0x6a, 0x29, 0x4f, 0x94, 0xbd,
0x8f, 0xbf, 0x7f, 0x7c, 0x6a, 0xb2, 0xd9, 0xe2, 0x64, 0x67, 0xec, 0xda, 0x8f, 0xc5, 0xdf, 0x00,
0x1c, 0xd3, 0x39, 0x75, 0x28, 0x7b, 0xed, 0x7a, 0x67, 0x8f, 0x2d, 0x67, 0xf2, 0x58, 0x84, 0xfa,
0xe3, 0x48, 0xe5, 0x49, 0x4e, 0xfc, 0x95, 0xf0, 0xa7, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xff,
0x7e, 0x76, 0x2e, 0x55, 0x1c, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -2260,6 +2517,13 @@ type RouterClient interface {
//It is a development feature.
QueryMissionControl(ctx context.Context, in *QueryMissionControlRequest, opts ...grpc.CallOption) (*QueryMissionControlResponse, error)
//
//GetMissionControlConfig returns mission control's current config.
GetMissionControlConfig(ctx context.Context, in *GetMissionControlConfigRequest, opts ...grpc.CallOption) (*GetMissionControlConfigResponse, error)
//
//SetMissionControlConfig will set mission control's config, if the config
//provided is valid.
SetMissionControlConfig(ctx context.Context, in *SetMissionControlConfigRequest, opts ...grpc.CallOption) (*SetMissionControlConfigResponse, error)
//
//QueryProbability returns the current success probability estimate for a
//given node pair and amount.
QueryProbability(ctx context.Context, in *QueryProbabilityRequest, opts ...grpc.CallOption) (*QueryProbabilityResponse, error)
@ -2408,6 +2672,24 @@ func (c *routerClient) QueryMissionControl(ctx context.Context, in *QueryMission
return out, nil
}
func (c *routerClient) GetMissionControlConfig(ctx context.Context, in *GetMissionControlConfigRequest, opts ...grpc.CallOption) (*GetMissionControlConfigResponse, error) {
out := new(GetMissionControlConfigResponse)
err := c.cc.Invoke(ctx, "/routerrpc.Router/GetMissionControlConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *routerClient) SetMissionControlConfig(ctx context.Context, in *SetMissionControlConfigRequest, opts ...grpc.CallOption) (*SetMissionControlConfigResponse, error) {
out := new(SetMissionControlConfigResponse)
err := c.cc.Invoke(ctx, "/routerrpc.Router/SetMissionControlConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *routerClient) QueryProbability(ctx context.Context, in *QueryProbabilityRequest, opts ...grpc.CallOption) (*QueryProbabilityResponse, error) {
out := new(QueryProbabilityResponse)
err := c.cc.Invoke(ctx, "/routerrpc.Router/QueryProbability", in, out, opts...)
@ -2592,6 +2874,13 @@ type RouterServer interface {
//It is a development feature.
QueryMissionControl(context.Context, *QueryMissionControlRequest) (*QueryMissionControlResponse, error)
//
//GetMissionControlConfig returns mission control's current config.
GetMissionControlConfig(context.Context, *GetMissionControlConfigRequest) (*GetMissionControlConfigResponse, error)
//
//SetMissionControlConfig will set mission control's config, if the config
//provided is valid.
SetMissionControlConfig(context.Context, *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse, error)
//
//QueryProbability returns the current success probability estimate for a
//given node pair and amount.
QueryProbability(context.Context, *QueryProbabilityRequest) (*QueryProbabilityResponse, error)
@ -2647,6 +2936,12 @@ func (*UnimplementedRouterServer) ResetMissionControl(ctx context.Context, req *
func (*UnimplementedRouterServer) QueryMissionControl(ctx context.Context, req *QueryMissionControlRequest) (*QueryMissionControlResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryMissionControl not implemented")
}
func (*UnimplementedRouterServer) GetMissionControlConfig(ctx context.Context, req *GetMissionControlConfigRequest) (*GetMissionControlConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMissionControlConfig not implemented")
}
func (*UnimplementedRouterServer) SetMissionControlConfig(ctx context.Context, req *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetMissionControlConfig not implemented")
}
func (*UnimplementedRouterServer) QueryProbability(ctx context.Context, req *QueryProbabilityRequest) (*QueryProbabilityResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryProbability not implemented")
}
@ -2802,6 +3097,42 @@ func _Router_QueryMissionControl_Handler(srv interface{}, ctx context.Context, d
return interceptor(ctx, in, info, handler)
}
func _Router_GetMissionControlConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetMissionControlConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RouterServer).GetMissionControlConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/routerrpc.Router/GetMissionControlConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RouterServer).GetMissionControlConfig(ctx, req.(*GetMissionControlConfigRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Router_SetMissionControlConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetMissionControlConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RouterServer).SetMissionControlConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/routerrpc.Router/SetMissionControlConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RouterServer).SetMissionControlConfig(ctx, req.(*SetMissionControlConfigRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Router_QueryProbability_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryProbabilityRequest)
if err := dec(in); err != nil {
@ -2951,6 +3282,14 @@ var _Router_serviceDesc = grpc.ServiceDesc{
MethodName: "QueryMissionControl",
Handler: _Router_QueryMissionControl_Handler,
},
{
MethodName: "GetMissionControlConfig",
Handler: _Router_GetMissionControlConfig_Handler,
},
{
MethodName: "SetMissionControlConfig",
Handler: _Router_SetMissionControlConfig_Handler,
},
{
MethodName: "QueryProbability",
Handler: _Router_QueryProbability_Handler,

View File

@ -222,6 +222,24 @@ func local_request_Router_QueryMissionControl_0(ctx context.Context, marshaler r
}
func request_Router_GetMissionControlConfig_0(ctx context.Context, marshaler runtime.Marshaler, client RouterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetMissionControlConfigRequest
var metadata runtime.ServerMetadata
msg, err := client.GetMissionControlConfig(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Router_GetMissionControlConfig_0(ctx context.Context, marshaler runtime.Marshaler, server RouterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetMissionControlConfigRequest
var metadata runtime.ServerMetadata
msg, err := server.GetMissionControlConfig(ctx, &protoReq)
return msg, metadata, err
}
func request_Router_QueryProbability_0(ctx context.Context, marshaler runtime.Marshaler, client RouterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq QueryProbabilityRequest
var metadata runtime.ServerMetadata
@ -470,6 +488,26 @@ func RegisterRouterHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser
})
mux.Handle("GET", pattern_Router_GetMissionControlConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Router_GetMissionControlConfig_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Router_GetMissionControlConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Router_QueryProbability_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@ -678,6 +716,26 @@ func RegisterRouterHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli
})
mux.Handle("GET", pattern_Router_GetMissionControlConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Router_GetMissionControlConfig_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Router_GetMissionControlConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Router_QueryProbability_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@ -754,6 +812,8 @@ var (
pattern_Router_QueryMissionControl_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "router", "mc"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_Router_GetMissionControlConfig_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "router", "mccfg"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_Router_QueryProbability_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"v2", "router", "mc", "probability", "from_node", "to_node", "amt_msat"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_Router_BuildRoute_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "router", "route"}, "", runtime.AssumeColonVerbOpt(true)))
@ -774,6 +834,8 @@ var (
forward_Router_QueryMissionControl_0 = runtime.ForwardResponseMessage
forward_Router_GetMissionControlConfig_0 = runtime.ForwardResponseMessage
forward_Router_QueryProbability_0 = runtime.ForwardResponseMessage
forward_Router_BuildRoute_0 = runtime.ForwardResponseMessage

View File

@ -61,6 +61,19 @@ service Router {
rpc QueryMissionControl (QueryMissionControlRequest)
returns (QueryMissionControlResponse);
/*
GetMissionControlConfig returns mission control's current config.
*/
rpc GetMissionControlConfig (GetMissionControlConfigRequest)
returns (GetMissionControlConfigResponse);
/*
SetMissionControlConfig will set mission control's config, if the config
provided is valid.
*/
rpc SetMissionControlConfig (SetMissionControlConfigRequest)
returns (SetMissionControlConfigResponse);
/*
QueryProbability returns the current success probability estimate for a
given node pair and amount.
@ -354,6 +367,67 @@ message PairData {
int64 success_amt_msat = 7;
}
message GetMissionControlConfigRequest {
}
message GetMissionControlConfigResponse {
/*
Mission control's currently active config.
*/
MissionControlConfig config = 1;
}
message SetMissionControlConfigRequest {
/*
The config to set for mission control. Note that all values *must* be set,
because the full config will be applied.
*/
MissionControlConfig config = 1;
}
message SetMissionControlConfigResponse {
}
message MissionControlConfig {
/*
The amount of time mission control will take to restore a penalized node
or channel back to 50% success probability, expressed as a unix timestamp
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;
/*
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;
/*
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;
/*
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 {
// The source node pubkey of the pair.
bytes from_node = 1;

View File

@ -148,6 +148,29 @@
]
}
},
"/v2/router/mccfg": {
"get": {
"summary": "GetMissionControlConfig returns mission control's current config.",
"operationId": "GetMissionControlConfig",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/routerrpcGetMissionControlConfigResponse"
}
},
"default": {
"description": "An unexpected error response",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
},
"tags": [
"Router"
]
}
},
"/v2/router/route": {
"post": {
"summary": "BuildRoute builds a fully specified route based on a list of hop public\nkeys. It retrieves the relevant channel policies from the graph in order to\ncalculate the correct fees and time locks.",
@ -968,6 +991,15 @@
}
}
},
"routerrpcGetMissionControlConfigResponse": {
"type": "object",
"properties": {
"config": {
"$ref": "#/definitions/routerrpcMissionControlConfig",
"description": "Mission control's currently active config."
}
}
},
"routerrpcHtlcEvent": {
"type": "object",
"properties": {
@ -1071,6 +1103,36 @@
}
}
},
"routerrpcMissionControlConfig": {
"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 as a unix timestamp\nin seconds. Setting this value to a higher value will penalize failures for\nlonger, making mission control less likely to route through nodes and\nchannels 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."
},
"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."
},
"maximum_payment_results": {
"type": "integer",
"format": "int64",
"description": "The maximum number of payment results that mission control will store."
},
"minimum_failure_relax_interval": {
"type": "string",
"format": "uint64",
"description": "The minimum time that must have passed since the previously recorded failure\nbefore we raise the failure amount."
}
}
},
"routerrpcPairData": {
"type": "object",
"properties": {
@ -1374,6 +1436,9 @@
}
}
},
"routerrpcSetMissionControlConfigResponse": {
"type": "object"
},
"routerrpcSettleEvent": {
"type": "object"
},

View File

@ -95,6 +95,13 @@ type MissionControl interface {
// pair.
GetPairHistorySnapshot(fromNode,
toNode route.Vertex) routing.TimedPairResult
// GetConfig gets mission control's current config.
GetConfig() *routing.MissionControlConfig
// SetConfig sets mission control's config to the values provided, if
// they are valid.
SetConfig(cfg *routing.MissionControlConfig) error
}
// QueryRoutes attempts to query the daemons' Channel Router for a possible

View File

@ -216,6 +216,7 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool) {
}
type mockMissionControl struct {
MissionControl
}
func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,

View File

@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"sync/atomic"
"time"
"github.com/btcsuite/btcutil"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
@ -79,6 +80,13 @@ var (
Entity: "offchain",
Action: "read",
}},
"/routerrpc.Router/GetMissionControlConfig": {{
Entity: "offchain",
Action: "read",
}}, "/routerrpc.Router/SetMissionControlConfig": {{
Entity: "offchain",
Action: "write",
}},
"/routerrpc.Router/QueryProbability": {{
Entity: "offchain",
Action: "read",
@ -391,6 +399,46 @@ func (s *Server) ResetMissionControl(ctx context.Context,
return &ResetMissionControlResponse{}, nil
}
// GetMissionControlConfig returns our current mission control config.
func (s *Server) GetMissionControlConfig(ctx context.Context,
req *GetMissionControlConfigRequest) (*GetMissionControlConfigResponse,
error) {
cfg := s.cfg.RouterBackend.MissionControl.GetConfig()
return &GetMissionControlConfigResponse{
Config: &MissionControlConfig{
HalfLifeSeconds: uint64(cfg.PenaltyHalfLife.Seconds()),
HopProbability: float32(cfg.AprioriHopProbability),
Weight: float32(cfg.AprioriWeight),
MaximumPaymentResults: uint32(cfg.MaxMcHistory),
MinimumFailureRelaxInterval: uint64(cfg.MinFailureRelaxInterval.Seconds()),
},
}, nil
}
// SetMissionControlConfig returns our current mission control config.
func (s *Server) SetMissionControlConfig(ctx context.Context,
req *SetMissionControlConfigRequest) (*SetMissionControlConfigResponse,
error) {
cfg := &routing.MissionControlConfig{
ProbabilityEstimatorCfg: routing.ProbabilityEstimatorCfg{
PenaltyHalfLife: time.Duration(
req.Config.HalfLifeSeconds,
) * time.Second,
AprioriHopProbability: float64(req.Config.HopProbability),
AprioriWeight: float64(req.Config.Weight),
},
MaxMcHistory: int(req.Config.MaximumPaymentResults),
MinFailureRelaxInterval: time.Duration(
req.Config.MinimumFailureRelaxInterval,
) * time.Second,
}
return &SetMissionControlConfigResponse{},
s.cfg.RouterBackend.MissionControl.SetConfig(cfg)
}
// QueryMissionControl exposes the internal mission control state to callers. It
// is a development feature.
func (s *Server) QueryMissionControl(ctx context.Context,

View File

@ -12750,6 +12750,10 @@ func testQueryRoutes(net *lntest.NetworkHarness, t *harnessTest) {
}
}
// While we're here, we test updating mission control's config values
// and assert that they are correctly updated.
testMissionControlCfg(t.t, net.Alice)
// We clean up the test case by closing channels that were created for
// the duration of the tests.
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
@ -12760,6 +12764,51 @@ func testQueryRoutes(net *lntest.NetworkHarness, t *harnessTest) {
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarol, false)
}
// testMissionControlCfg tests getting and setting of a node's mission control
// config, resetting to the original values after testing so that no other
// tests are affected.
func testMissionControlCfg(t *testing.T, node *lntest.HarnessNode) {
ctxb := context.Background()
startCfg, err := node.RouterClient.GetMissionControlConfig(
ctxb, &routerrpc.GetMissionControlConfigRequest{},
)
require.NoError(t, err)
cfg := &routerrpc.MissionControlConfig{
HalfLifeSeconds: 8000,
HopProbability: 0.8,
Weight: 0.3,
MaximumPaymentResults: 30,
MinimumFailureRelaxInterval: 60,
}
_, err = node.RouterClient.SetMissionControlConfig(
ctxb, &routerrpc.SetMissionControlConfigRequest{
Config: cfg,
},
)
require.NoError(t, err)
resp, err := node.RouterClient.GetMissionControlConfig(
ctxb, &routerrpc.GetMissionControlConfigRequest{},
)
require.NoError(t, err)
// Set the hidden fields on the cfg we set so that we can use require
// equal rather than comparing field by field.
cfg.XXX_sizecache = resp.XXX_sizecache
cfg.XXX_NoUnkeyedLiteral = resp.XXX_NoUnkeyedLiteral
cfg.XXX_unrecognized = resp.XXX_unrecognized
require.Equal(t, cfg, resp.Config)
_, err = node.RouterClient.SetMissionControlConfig(
ctxb, &routerrpc.SetMissionControlConfigRequest{
Config: startCfg.Config,
},
)
require.NoError(t, err)
}
// testRouteFeeCutoff tests that we are able to prevent querying routes and
// sending payments that incur a fee higher than the fee limit.
func testRouteFeeCutoff(net *lntest.NetworkHarness, t *harnessTest) {

View File

@ -58,10 +58,11 @@ func newIntegratedRoutingContext(t *testing.T) *integratedRoutingContext {
finalExpiry: 40,
mcCfg: MissionControlConfig{
PenaltyHalfLife: 30 * time.Minute,
AprioriHopProbability: 0.6,
AprioriWeight: 0.5,
SelfNode: source.pubkey,
ProbabilityEstimatorCfg: ProbabilityEstimatorCfg{
PenaltyHalfLife: 30 * time.Minute,
AprioriHopProbability: 0.6,
AprioriWeight: 0.5,
},
},
pathFindingCfg: PathFindingConfig{
@ -123,7 +124,7 @@ func (c *integratedRoutingContext) testPayment(maxParts uint32,
// Instantiate a new mission control with the current configuration
// values.
mc, err := NewMissionControl(db, &c.mcCfg)
mc, err := NewMissionControl(db, c.source.pubkey, &c.mcCfg)
if err != nil {
c.t.Fatal(err)
}

View File

@ -1,6 +1,8 @@
package routing
import (
"errors"
"fmt"
"sync"
"time"
@ -57,6 +59,17 @@ const (
DefaultMinFailureRelaxInterval = time.Minute
)
var (
// ErrInvalidMcHistory is returned if we get a negative mission control
// history count.
ErrInvalidMcHistory = errors.New("mission control history must be " +
">= 0")
// ErrInvalidFailureInterval is returned if we get an invalid failure
// interval.
ErrInvalidFailureInterval = errors.New("failure interval must be >= 0")
)
// NodeResults contains previous results from a node to its peers.
type NodeResults map[route.Vertex]TimedPairResult
@ -78,7 +91,8 @@ type MissionControl struct {
// external function to enable deterministic unit tests.
now func() time.Time
cfg *MissionControlConfig
// selfNode is our pubkey.
selfNode route.Vertex
store *missionControlStore
@ -97,34 +111,43 @@ type MissionControl struct {
// 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
// AprioriHopProbability is the assumed success probability of a hop in
// a route when no other information is available.
AprioriHopProbability float64
// ProbabilityEstimatorConfig is the config we will use for probability
// calculations.
ProbabilityEstimatorCfg
// MaxMcHistory defines the maximum number of payment results that are
// held on disk.
MaxMcHistory int
// AprioriWeight is a value in the range [0, 1] that defines to what
// extent historical results should be extrapolated to untried
// connections. Setting it to one will completely ignore historical
// results and always assume the configured a priori probability for
// untried connections. A value of zero will ignore the a priori
// probability completely and only base the probability on historical
// results, unless there are none available.
AprioriWeight float64
// MinFailureRelaxInterval is the minimum time that must have passed
// since the previously recorded failure before the failure amount may
// be raised.
MinFailureRelaxInterval time.Duration
}
// SelfNode is our own pubkey.
SelfNode route.Vertex
func (c *MissionControlConfig) validate() error {
if err := c.ProbabilityEstimatorCfg.validate(); err != nil {
return err
}
if c.MaxMcHistory < 0 {
return ErrInvalidMcHistory
}
if c.MinFailureRelaxInterval < 0 {
return ErrInvalidFailureInterval
}
return nil
}
// String returns a string representation of a mission control config.
func (c *MissionControlConfig) String() string {
return fmt.Sprintf("Penalty Half Life: %v, Apriori Hop "+
"Probablity: %v, Maximum History: %v, Apriori Weight: %v, "+
"Minimum Failure Relax Interval: %v", c.PenaltyHalfLife,
c.AprioriHopProbability, c.MaxMcHistory, c.AprioriWeight,
c.MinFailureRelaxInterval)
}
// TimedPairResult describes a timestamped pair result.
@ -177,13 +200,14 @@ type paymentResult struct {
}
// NewMissionControl returns a new instance of missionControl.
func NewMissionControl(db kvdb.Backend, cfg *MissionControlConfig) (
*MissionControl, error) {
func NewMissionControl(db kvdb.Backend, self route.Vertex,
cfg *MissionControlConfig) (*MissionControl, error) {
log.Debugf("Instantiating mission control with config: "+
"PenaltyHalfLife=%v, AprioriHopProbability=%v, "+
"AprioriWeight=%v", cfg.PenaltyHalfLife,
cfg.AprioriHopProbability, cfg.AprioriWeight)
log.Debugf("Instantiating mission control with config: %v", cfg)
if err := cfg.validate(); err != nil {
return nil, err
}
store, err := newMissionControlStore(db, cfg.MaxMcHistory)
if err != nil {
@ -191,16 +215,14 @@ func NewMissionControl(db kvdb.Backend, cfg *MissionControlConfig) (
}
estimator := &probabilityEstimator{
aprioriHopProbability: cfg.AprioriHopProbability,
aprioriWeight: cfg.AprioriWeight,
penaltyHalfLife: cfg.PenaltyHalfLife,
prevSuccessProbability: prevSuccessProbability,
ProbabilityEstimatorCfg: cfg.ProbabilityEstimatorCfg,
prevSuccessProbability: prevSuccessProbability,
}
mc := &MissionControl{
state: newMissionControlState(cfg.MinFailureRelaxInterval),
now: time.Now,
cfg: cfg,
selfNode: self,
store: store,
estimator: estimator,
}
@ -216,6 +238,9 @@ func NewMissionControl(db kvdb.Backend, cfg *MissionControlConfig) (
func (m *MissionControl) init() error {
log.Debugf("Mission control state reconstruction started")
m.Lock()
defer m.Unlock()
start := time.Now()
results, err := m.store.fetchAll()
@ -233,6 +258,43 @@ func (m *MissionControl) init() error {
return nil
}
// GetConfig returns the config that mission control is currently configured
// with. All fields are copied by value, so we do not need to worry about
// mutation.
func (m *MissionControl) GetConfig() *MissionControlConfig {
m.Lock()
defer m.Unlock()
return &MissionControlConfig{
ProbabilityEstimatorCfg: m.estimator.ProbabilityEstimatorCfg,
MaxMcHistory: m.store.maxRecords,
MinFailureRelaxInterval: m.state.minFailureRelaxInterval,
}
}
// SetConfig validates the config provided and updates mission control's config
// if it is valid.
func (m *MissionControl) SetConfig(cfg *MissionControlConfig) error {
if cfg == nil {
return errors.New("nil mission control config")
}
if err := cfg.validate(); err != nil {
return err
}
m.Lock()
defer m.Unlock()
log.Infof("Updating mission control cfg: %v", cfg)
m.store.maxRecords = cfg.MaxMcHistory
m.state.minFailureRelaxInterval = cfg.MinFailureRelaxInterval
m.estimator.ProbabilityEstimatorCfg = cfg.ProbabilityEstimatorCfg
return nil
}
// ResetHistory resets the history of MissionControl returning it to a state as
// if no payment attempts have been made.
func (m *MissionControl) ResetHistory() error {
@ -262,7 +324,7 @@ func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex,
results, _ := m.state.getLastPairResult(fromNode)
// Use a distinct probability estimation function for local channels.
if fromNode == m.cfg.SelfNode {
if fromNode == m.selfNode {
return m.estimator.getLocalPairProbability(now, results, toNode)
}
@ -309,6 +371,9 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
failureSourceIdx *int, failure lnwire.FailureMessage) (
*channeldb.FailureReason, error) {
m.Lock()
defer m.Unlock()
timestamp := m.now()
result := &paymentResult{
@ -329,6 +394,9 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
func (m *MissionControl) ReportPaymentSuccess(paymentID uint64,
rt *route.Route) error {
m.Lock()
defer m.Unlock()
timestamp := m.now()
result := &paymentResult{
@ -371,10 +439,6 @@ func (m *MissionControl) applyPaymentResult(
result.failure,
)
// Update mission control state using the interpretation.
m.Lock()
defer m.Unlock()
if i.policyFailure != nil {
if m.state.requestSecondChance(
result.timeReply,

View File

@ -78,12 +78,13 @@ func createMcTestContext(t *testing.T) *mcTestContext {
// restartMc creates a new instances of mission control on the same database.
func (ctx *mcTestContext) restartMc() {
mc, err := NewMissionControl(
ctx.db,
ctx.db, mcTestSelf,
&MissionControlConfig{
PenaltyHalfLife: testPenaltyHalfLife,
AprioriHopProbability: testAprioriHopProbability,
AprioriWeight: testAprioriWeight,
SelfNode: mcTestSelf,
ProbabilityEstimatorCfg: ProbabilityEstimatorCfg{
PenaltyHalfLife: testPenaltyHalfLife,
AprioriHopProbability: testAprioriHopProbability,
AprioriWeight: testAprioriWeight,
},
},
)
if err != nil {

View File

@ -104,6 +104,7 @@ func (m *mockPaymentSessionSource) NewPaymentSessionEmpty() PaymentSession {
}
type mockMissionControl struct {
MissionControl
}
var _ MissionController = (*mockMissionControl)(nil)

View File

@ -33,7 +33,7 @@ func TestRequestRoute(t *testing.T) {
func() (routingGraph, func(), error) {
return &sessionGraph{}, func() {}, nil
},
&MissionControl{cfg: &MissionControlConfig{}},
&MissionControl{},
PathFindingConfig{},
)
if err != nil {

View File

@ -1,6 +1,7 @@
package routing
import (
"errors"
"math"
"time"
@ -8,25 +9,61 @@ import (
"github.com/lightningnetwork/lnd/routing/route"
)
// probabilityEstimator returns node and pair probabilities based on historical
// payment results.
type probabilityEstimator struct {
// penaltyHalfLife defines after how much time a penalized node or
var (
// ErrInvalidHalflife is returned when we get an invalid half life.
ErrInvalidHalflife = errors.New("penalty half life must be >= 0")
// ErrInvalidHopProbability is returned when we get an invalid hop
// probability.
ErrInvalidHopProbability = errors.New("hop probability must be in [0;1]")
// ErrInvalidAprioriWeight is returned when we get an apriori weight
// that is out of range.
ErrInvalidAprioriWeight = errors.New("apriori weight must be in [0;1]")
)
// ProbabilityEstimatorCfg contains configuration for our probability estimator.
type ProbabilityEstimatorCfg struct {
// PenaltyHalfLife defines after how much time a penalized node or
// channel is back at 50% probability.
penaltyHalfLife time.Duration
PenaltyHalfLife time.Duration
// aprioriHopProbability is the assumed success probability of a hop in
// AprioriHopProbability is the assumed success probability of a hop in
// a route when no other information is available.
aprioriHopProbability float64
AprioriHopProbability float64
// aprioriWeight is a value in the range [0, 1] that defines to what
// AprioriWeight is a value in the range [0, 1] that defines to what
// extent historical results should be extrapolated to untried
// connections. Setting it to one will completely ignore historical
// results and always assume the configured a priori probability for
// untried connections. A value of zero will ignore the a priori
// probability completely and only base the probability on historical
// results, unless there are none available.
aprioriWeight float64
AprioriWeight float64
}
func (p ProbabilityEstimatorCfg) validate() error {
if p.PenaltyHalfLife < 0 {
return ErrInvalidHalflife
}
if p.AprioriHopProbability < 0 || p.AprioriHopProbability > 1 {
return ErrInvalidHopProbability
}
if p.AprioriWeight < 0 || p.AprioriWeight > 1 {
return ErrInvalidAprioriWeight
}
return nil
}
// probabilityEstimator returns node and pair probabilities based on historical
// payment results.
type probabilityEstimator struct {
// ProbabilityEstimatorCfg contains configuration options for our
// estimator.
ProbabilityEstimatorCfg
// prevSuccessProbability is the assumed probability for node pairs that
// successfully relayed the previous attempt.
@ -41,14 +78,14 @@ func (p *probabilityEstimator) getNodeProbability(now time.Time,
// If the channel history is not to be taken into account, we can return
// early here with the configured a priori probability.
if p.aprioriWeight == 1 {
return p.aprioriHopProbability
if p.AprioriWeight == 1 {
return p.AprioriHopProbability
}
// If there is no channel history, our best estimate is still the a
// priori probability.
if len(results) == 0 {
return p.aprioriHopProbability
return p.AprioriHopProbability
}
// The value of the apriori weight is in the range [0, 1]. Convert it to
@ -58,7 +95,7 @@ func (p *probabilityEstimator) getNodeProbability(now time.Time,
// the weighted average calculation below. When the apriori weight
// approaches 1, the apriori factor goes to infinity. It will heavily
// outweigh any observations that have been collected.
aprioriFactor := 1/(1-p.aprioriWeight) - 1
aprioriFactor := 1/(1-p.AprioriWeight) - 1
// Calculate a weighted average consisting of the apriori probability
// and historical observations. This is the part that incentivizes nodes
@ -76,7 +113,7 @@ func (p *probabilityEstimator) getNodeProbability(now time.Time,
// effectively prunes all channels of the node forever. This is the most
// aggressive way in which we can penalize nodes and unlikely to yield
// good results in a real network.
probabilitiesTotal := p.aprioriHopProbability * aprioriFactor
probabilitiesTotal := p.AprioriHopProbability * aprioriFactor
totalWeight := aprioriFactor
for _, result := range results {
@ -106,7 +143,7 @@ func (p *probabilityEstimator) getNodeProbability(now time.Time,
// the result is fresh and asymptotically approaches zero over time. The rate at
// which this happens is controlled by the penaltyHalfLife parameter.
func (p *probabilityEstimator) getWeight(age time.Duration) float64 {
exp := -age.Hours() / p.penaltyHalfLife.Hours()
exp := -age.Hours() / p.PenaltyHalfLife.Hours()
return math.Pow(2, exp)
}

View File

@ -40,9 +40,11 @@ func newEstimatorTestContext(t *testing.T) *estimatorTestContext {
return &estimatorTestContext{
t: t,
estimator: &probabilityEstimator{
aprioriHopProbability: aprioriHopProb,
aprioriWeight: aprioriWeight,
penaltyHalfLife: time.Hour,
ProbabilityEstimatorCfg: ProbabilityEstimatorCfg{
AprioriHopProbability: aprioriHopProb,
AprioriWeight: aprioriWeight,
PenaltyHalfLife: time.Hour,
},
prevSuccessProbability: aprioriPrevSucProb,
},
}

View File

@ -85,13 +85,15 @@ func createTestCtxFromGraphInstance(startingHeight uint32, graphInstance *testGr
}
mcConfig := &MissionControlConfig{
PenaltyHalfLife: time.Hour,
AprioriHopProbability: 0.9,
AprioriWeight: 0.5,
ProbabilityEstimatorCfg: ProbabilityEstimatorCfg{
PenaltyHalfLife: time.Hour,
AprioriHopProbability: 0.9,
AprioriWeight: 0.5,
},
}
mc, err := NewMissionControl(
graphInstance.graph.Database(),
graphInstance.graph.Database(), route.Vertex{},
mcConfig,
)
if err != nil {

View File

@ -725,14 +725,17 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
// servers, the mission control instance itself can be moved there too.
routingConfig := routerrpc.GetRoutingConfig(cfg.SubRPCServers.RouterRPC)
estimatorCfg := routing.ProbabilityEstimatorCfg{
AprioriHopProbability: routingConfig.AprioriHopProbability,
PenaltyHalfLife: routingConfig.PenaltyHalfLife,
AprioriWeight: routingConfig.AprioriWeight,
}
s.missionControl, err = routing.NewMissionControl(
remoteChanDB,
remoteChanDB, selfNode.PubKeyBytes,
&routing.MissionControlConfig{
AprioriHopProbability: routingConfig.AprioriHopProbability,
PenaltyHalfLife: routingConfig.PenaltyHalfLife,
ProbabilityEstimatorCfg: estimatorCfg,
MaxMcHistory: routingConfig.MaxMcHistory,
AprioriWeight: routingConfig.AprioriWeight,
SelfNode: selfNode.PubKeyBytes,
MinFailureRelaxInterval: routing.DefaultMinFailureRelaxInterval,
},
)