diff --git a/cmd/lncli/cmd_mc_cfg_get.go b/cmd/lncli/cmd_mc_cfg_get.go new file mode 100644 index 00000000..017449f8 --- /dev/null +++ b/cmd/lncli/cmd_mc_cfg_get.go @@ -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 +} diff --git a/cmd/lncli/cmd_mc_cfg_set.go b/cmd/lncli/cmd_mc_cfg_set.go new file mode 100644 index 00000000..874ff2db --- /dev/null +++ b/cmd/lncli/cmd_mc_cfg_set.go @@ -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 +} diff --git a/cmd/lncli/routerrpc.go b/cmd/lncli/routerrpc.go index 819f66d2..04c9676e 100644 --- a/cmd/lncli/routerrpc.go +++ b/cmd/lncli/routerrpc.go @@ -9,5 +9,7 @@ func routerCommands() []cli.Command { queryProbCommand, resetMissionControlCommand, buildRouteCommand, + getCfgCommand, + setCfgCommand, } } diff --git a/lnrpc/rest-annotations.yaml b/lnrpc/rest-annotations.yaml index fae8d577..927dd63e 100644 --- a/lnrpc/rest-annotations.yaml +++ b/lnrpc/rest-annotations.yaml @@ -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 diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 7d454125..5d552c19 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -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, diff --git a/lnrpc/routerrpc/router.pb.gw.go b/lnrpc/routerrpc/router.pb.gw.go index 23350311..80638c12 100644 --- a/lnrpc/routerrpc/router.pb.gw.go +++ b/lnrpc/routerrpc/router.pb.gw.go @@ -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 diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 8aecab8b..5a5830d0 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -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; diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index aac5761a..a2218c5b 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -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" }, diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 922dec4a..0756770d 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -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 diff --git a/lnrpc/routerrpc/router_backend_test.go b/lnrpc/routerrpc/router_backend_test.go index bd9fee37..f100909a 100644 --- a/lnrpc/routerrpc/router_backend_test.go +++ b/lnrpc/routerrpc/router_backend_test.go @@ -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, diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 2aead9e0..e3c0a2ae 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -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, diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 3fdeed2d..721ea4d7 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -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) { diff --git a/routing/integrated_routing_context_test.go b/routing/integrated_routing_context_test.go index fcd3d05b..ddbc1036 100644 --- a/routing/integrated_routing_context_test.go +++ b/routing/integrated_routing_context_test.go @@ -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) } diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index f48f8f5f..29d62dcc 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -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, diff --git a/routing/missioncontrol_test.go b/routing/missioncontrol_test.go index 001e7b03..48922a3b 100644 --- a/routing/missioncontrol_test.go +++ b/routing/missioncontrol_test.go @@ -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 { diff --git a/routing/mock_test.go b/routing/mock_test.go index a284cf57..9c4f79e5 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -104,6 +104,7 @@ func (m *mockPaymentSessionSource) NewPaymentSessionEmpty() PaymentSession { } type mockMissionControl struct { + MissionControl } var _ MissionController = (*mockMissionControl)(nil) diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index ba55fcea..6a84e1f0 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -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 { diff --git a/routing/probability_estimator.go b/routing/probability_estimator.go index 238a765f..7cd2df16 100644 --- a/routing/probability_estimator.go +++ b/routing/probability_estimator.go @@ -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) } diff --git a/routing/probability_estimator_test.go b/routing/probability_estimator_test.go index 384b39b6..094c0550 100644 --- a/routing/probability_estimator_test.go +++ b/routing/probability_estimator_test.go @@ -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, }, } diff --git a/routing/router_test.go b/routing/router_test.go index 5db80dc9..a04aa7b3 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -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 { diff --git a/server.go b/server.go index 7435e4bf..87754e89 100644 --- a/server.go +++ b/server.go @@ -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, }, )