Merge pull request #3462 from joostjager/mc-extrapolate
routing+routerrpc: improve prob. estimation for untried connections
This commit is contained in:
commit
8ed7583448
@ -31,36 +31,17 @@ func queryMissionControl(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type displayNodeHistory struct {
|
|
||||||
Pubkey string
|
|
||||||
LastFailTime int64
|
|
||||||
OtherSuccessProb float32
|
|
||||||
}
|
|
||||||
|
|
||||||
type displayPairHistory struct {
|
type displayPairHistory struct {
|
||||||
NodeFrom, NodeTo string
|
NodeFrom, NodeTo string
|
||||||
LastAttemptSuccessful bool
|
LastAttemptSuccessful bool
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
SuccessProb float32
|
|
||||||
MinPenalizeAmtSat int64
|
MinPenalizeAmtSat int64
|
||||||
}
|
}
|
||||||
|
|
||||||
displayResp := struct {
|
displayResp := struct {
|
||||||
Nodes []displayNodeHistory
|
|
||||||
Pairs []displayPairHistory
|
Pairs []displayPairHistory
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
for _, n := range snapshot.Nodes {
|
|
||||||
displayResp.Nodes = append(
|
|
||||||
displayResp.Nodes,
|
|
||||||
displayNodeHistory{
|
|
||||||
Pubkey: hex.EncodeToString(n.Pubkey),
|
|
||||||
LastFailTime: n.LastFailTime,
|
|
||||||
OtherSuccessProb: n.OtherSuccessProb,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, n := range snapshot.Pairs {
|
for _, n := range snapshot.Pairs {
|
||||||
displayResp.Pairs = append(
|
displayResp.Pairs = append(
|
||||||
displayResp.Pairs,
|
displayResp.Pairs,
|
||||||
@ -69,7 +50,6 @@ func queryMissionControl(ctx *cli.Context) error {
|
|||||||
NodeTo: hex.EncodeToString(n.NodeTo),
|
NodeTo: hex.EncodeToString(n.NodeTo),
|
||||||
LastAttemptSuccessful: n.LastAttemptSuccessful,
|
LastAttemptSuccessful: n.LastAttemptSuccessful,
|
||||||
Timestamp: n.Timestamp,
|
Timestamp: n.Timestamp,
|
||||||
SuccessProb: n.SuccessProb,
|
|
||||||
MinPenalizeAmtSat: n.MinPenalizeAmtSat,
|
MinPenalizeAmtSat: n.MinPenalizeAmtSat,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -16,6 +16,15 @@ type RoutingConfig struct {
|
|||||||
// a route when no other information is available.
|
// a route when no other information is available.
|
||||||
AprioriHopProbability float64 `long:"apriorihopprob" description:"Assumed success probability of a hop in a route when no other information is available."`
|
AprioriHopProbability float64 `long:"apriorihopprob" description:"Assumed success probability of a hop in a route when no other information is available."`
|
||||||
|
|
||||||
|
// 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 `long:"aprioriweight" description:"Weight of the a priori probability in success probability estimation. Valid values are in [0, 1]."`
|
||||||
|
|
||||||
// PenaltyHalfLife defines after how much time a penalized node or
|
// PenaltyHalfLife defines after how much time a penalized node or
|
||||||
// channel is back at 50% probability.
|
// channel is back at 50% probability.
|
||||||
PenaltyHalfLife time.Duration `long:"penaltyhalflife" description:"Defines the duration after which a penalized node or channel is back at 50% probability"`
|
PenaltyHalfLife time.Duration `long:"penaltyhalflife" description:"Defines the duration after which a penalized node or channel is back at 50% probability"`
|
||||||
|
@ -45,6 +45,7 @@ type Config struct {
|
|||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
defaultRoutingConfig := RoutingConfig{
|
defaultRoutingConfig := RoutingConfig{
|
||||||
AprioriHopProbability: routing.DefaultAprioriHopProbability,
|
AprioriHopProbability: routing.DefaultAprioriHopProbability,
|
||||||
|
AprioriWeight: routing.DefaultAprioriWeight,
|
||||||
MinRouteProbability: routing.DefaultMinRouteProbability,
|
MinRouteProbability: routing.DefaultMinRouteProbability,
|
||||||
PenaltyHalfLife: routing.DefaultPenaltyHalfLife,
|
PenaltyHalfLife: routing.DefaultPenaltyHalfLife,
|
||||||
AttemptCost: routing.DefaultPaymentAttemptPenalty.
|
AttemptCost: routing.DefaultPaymentAttemptPenalty.
|
||||||
@ -61,6 +62,7 @@ func DefaultConfig() *Config {
|
|||||||
func GetRoutingConfig(cfg *Config) *RoutingConfig {
|
func GetRoutingConfig(cfg *Config) *RoutingConfig {
|
||||||
return &RoutingConfig{
|
return &RoutingConfig{
|
||||||
AprioriHopProbability: cfg.AprioriHopProbability,
|
AprioriHopProbability: cfg.AprioriHopProbability,
|
||||||
|
AprioriWeight: cfg.AprioriWeight,
|
||||||
MinRouteProbability: cfg.MinRouteProbability,
|
MinRouteProbability: cfg.MinRouteProbability,
|
||||||
AttemptCost: cfg.AttemptCost,
|
AttemptCost: cfg.AttemptCost,
|
||||||
PenaltyHalfLife: cfg.PenaltyHalfLife,
|
PenaltyHalfLife: cfg.PenaltyHalfLife,
|
||||||
|
@ -18,6 +18,7 @@ func DefaultConfig() *Config {
|
|||||||
func GetRoutingConfig(cfg *Config) *RoutingConfig {
|
func GetRoutingConfig(cfg *Config) *RoutingConfig {
|
||||||
return &RoutingConfig{
|
return &RoutingConfig{
|
||||||
AprioriHopProbability: routing.DefaultAprioriHopProbability,
|
AprioriHopProbability: routing.DefaultAprioriHopProbability,
|
||||||
|
AprioriWeight: routing.DefaultAprioriWeight,
|
||||||
MinRouteProbability: routing.DefaultMinRouteProbability,
|
MinRouteProbability: routing.DefaultMinRouteProbability,
|
||||||
AttemptCost: routing.DefaultPaymentAttemptPenalty.
|
AttemptCost: routing.DefaultPaymentAttemptPenalty.
|
||||||
ToSatoshis(),
|
ToSatoshis(),
|
||||||
|
@ -1006,8 +1006,6 @@ var xxx_messageInfo_QueryMissionControlRequest proto.InternalMessageInfo
|
|||||||
|
|
||||||
/// QueryMissionControlResponse contains mission control state.
|
/// QueryMissionControlResponse contains mission control state.
|
||||||
type QueryMissionControlResponse struct {
|
type QueryMissionControlResponse struct {
|
||||||
/// Node-level mission control state.
|
|
||||||
Nodes []*NodeHistory `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
|
|
||||||
/// Node pair-level mission control state.
|
/// Node pair-level mission control state.
|
||||||
Pairs []*PairHistory `protobuf:"bytes,2,rep,name=pairs,proto3" json:"pairs,omitempty"`
|
Pairs []*PairHistory `protobuf:"bytes,2,rep,name=pairs,proto3" json:"pairs,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
@ -1040,13 +1038,6 @@ func (m *QueryMissionControlResponse) XXX_DiscardUnknown() {
|
|||||||
|
|
||||||
var xxx_messageInfo_QueryMissionControlResponse proto.InternalMessageInfo
|
var xxx_messageInfo_QueryMissionControlResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *QueryMissionControlResponse) GetNodes() []*NodeHistory {
|
|
||||||
if m != nil {
|
|
||||||
return m.Nodes
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *QueryMissionControlResponse) GetPairs() []*PairHistory {
|
func (m *QueryMissionControlResponse) GetPairs() []*PairHistory {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Pairs
|
return m.Pairs
|
||||||
@ -1054,67 +1045,6 @@ func (m *QueryMissionControlResponse) GetPairs() []*PairHistory {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NodeHistory contains the mission control state for a particular node.
|
|
||||||
type NodeHistory struct {
|
|
||||||
/// Node pubkey
|
|
||||||
Pubkey []byte `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
|
|
||||||
/// Time stamp of last failure. Set to zero if no failure happened yet.
|
|
||||||
LastFailTime int64 `protobuf:"varint,2,opt,name=last_fail_time,proto3" json:"last_fail_time,omitempty"`
|
|
||||||
//*
|
|
||||||
//Estimation of success probability of forwarding towards peers of this node
|
|
||||||
//for which no specific history is available.
|
|
||||||
OtherSuccessProb float32 `protobuf:"fixed32,3,opt,name=other_success_prob,proto3" json:"other_success_prob,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NodeHistory) Reset() { *m = NodeHistory{} }
|
|
||||||
func (m *NodeHistory) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*NodeHistory) ProtoMessage() {}
|
|
||||||
func (*NodeHistory) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_7a0613f69d37b0a5, []int{13}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NodeHistory) XXX_Unmarshal(b []byte) error {
|
|
||||||
return xxx_messageInfo_NodeHistory.Unmarshal(m, b)
|
|
||||||
}
|
|
||||||
func (m *NodeHistory) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_NodeHistory.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *NodeHistory) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_NodeHistory.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *NodeHistory) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_NodeHistory.Size(m)
|
|
||||||
}
|
|
||||||
func (m *NodeHistory) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_NodeHistory.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_NodeHistory proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *NodeHistory) GetPubkey() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Pubkey
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NodeHistory) GetLastFailTime() int64 {
|
|
||||||
if m != nil {
|
|
||||||
return m.LastFailTime
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *NodeHistory) GetOtherSuccessProb() float32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.OtherSuccessProb
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PairHistory contains the mission control state for a particular node pair.
|
/// PairHistory contains the mission control state for a particular node pair.
|
||||||
type PairHistory struct {
|
type PairHistory struct {
|
||||||
/// The source node pubkey of the pair.
|
/// The source node pubkey of the pair.
|
||||||
@ -1125,8 +1055,6 @@ type PairHistory struct {
|
|||||||
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||||
/// Minimum penalization amount (only applies to failed attempts).
|
/// Minimum penalization amount (only applies to failed attempts).
|
||||||
MinPenalizeAmtSat int64 `protobuf:"varint,4,opt,name=min_penalize_amt_sat,proto3" json:"min_penalize_amt_sat,omitempty"`
|
MinPenalizeAmtSat int64 `protobuf:"varint,4,opt,name=min_penalize_amt_sat,proto3" json:"min_penalize_amt_sat,omitempty"`
|
||||||
/// Estimation of success probability for this pair.
|
|
||||||
SuccessProb float32 `protobuf:"fixed32,5,opt,name=success_prob,proto3" json:"success_prob,omitempty"`
|
|
||||||
/// Whether the last payment attempt through this pair was successful.
|
/// Whether the last payment attempt through this pair was successful.
|
||||||
LastAttemptSuccessful bool `protobuf:"varint,6,opt,name=last_attempt_successful,proto3" json:"last_attempt_successful,omitempty"`
|
LastAttemptSuccessful bool `protobuf:"varint,6,opt,name=last_attempt_successful,proto3" json:"last_attempt_successful,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
@ -1138,7 +1066,7 @@ func (m *PairHistory) Reset() { *m = PairHistory{} }
|
|||||||
func (m *PairHistory) String() string { return proto.CompactTextString(m) }
|
func (m *PairHistory) String() string { return proto.CompactTextString(m) }
|
||||||
func (*PairHistory) ProtoMessage() {}
|
func (*PairHistory) ProtoMessage() {}
|
||||||
func (*PairHistory) Descriptor() ([]byte, []int) {
|
func (*PairHistory) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_7a0613f69d37b0a5, []int{14}
|
return fileDescriptor_7a0613f69d37b0a5, []int{13}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PairHistory) XXX_Unmarshal(b []byte) error {
|
func (m *PairHistory) XXX_Unmarshal(b []byte) error {
|
||||||
@ -1187,13 +1115,6 @@ func (m *PairHistory) GetMinPenalizeAmtSat() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PairHistory) GetSuccessProb() float32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.SuccessProb
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PairHistory) GetLastAttemptSuccessful() bool {
|
func (m *PairHistory) GetLastAttemptSuccessful() bool {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.LastAttemptSuccessful
|
return m.LastAttemptSuccessful
|
||||||
@ -1227,7 +1148,7 @@ func (m *BuildRouteRequest) Reset() { *m = BuildRouteRequest{} }
|
|||||||
func (m *BuildRouteRequest) String() string { return proto.CompactTextString(m) }
|
func (m *BuildRouteRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*BuildRouteRequest) ProtoMessage() {}
|
func (*BuildRouteRequest) ProtoMessage() {}
|
||||||
func (*BuildRouteRequest) Descriptor() ([]byte, []int) {
|
func (*BuildRouteRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_7a0613f69d37b0a5, []int{15}
|
return fileDescriptor_7a0613f69d37b0a5, []int{14}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *BuildRouteRequest) XXX_Unmarshal(b []byte) error {
|
func (m *BuildRouteRequest) XXX_Unmarshal(b []byte) error {
|
||||||
@ -1289,7 +1210,7 @@ func (m *BuildRouteResponse) Reset() { *m = BuildRouteResponse{} }
|
|||||||
func (m *BuildRouteResponse) String() string { return proto.CompactTextString(m) }
|
func (m *BuildRouteResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*BuildRouteResponse) ProtoMessage() {}
|
func (*BuildRouteResponse) ProtoMessage() {}
|
||||||
func (*BuildRouteResponse) Descriptor() ([]byte, []int) {
|
func (*BuildRouteResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_7a0613f69d37b0a5, []int{16}
|
return fileDescriptor_7a0613f69d37b0a5, []int{15}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *BuildRouteResponse) XXX_Unmarshal(b []byte) error {
|
func (m *BuildRouteResponse) XXX_Unmarshal(b []byte) error {
|
||||||
@ -1334,7 +1255,6 @@ func init() {
|
|||||||
proto.RegisterType((*ResetMissionControlResponse)(nil), "routerrpc.ResetMissionControlResponse")
|
proto.RegisterType((*ResetMissionControlResponse)(nil), "routerrpc.ResetMissionControlResponse")
|
||||||
proto.RegisterType((*QueryMissionControlRequest)(nil), "routerrpc.QueryMissionControlRequest")
|
proto.RegisterType((*QueryMissionControlRequest)(nil), "routerrpc.QueryMissionControlRequest")
|
||||||
proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse")
|
proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse")
|
||||||
proto.RegisterType((*NodeHistory)(nil), "routerrpc.NodeHistory")
|
|
||||||
proto.RegisterType((*PairHistory)(nil), "routerrpc.PairHistory")
|
proto.RegisterType((*PairHistory)(nil), "routerrpc.PairHistory")
|
||||||
proto.RegisterType((*BuildRouteRequest)(nil), "routerrpc.BuildRouteRequest")
|
proto.RegisterType((*BuildRouteRequest)(nil), "routerrpc.BuildRouteRequest")
|
||||||
proto.RegisterType((*BuildRouteResponse)(nil), "routerrpc.BuildRouteResponse")
|
proto.RegisterType((*BuildRouteResponse)(nil), "routerrpc.BuildRouteResponse")
|
||||||
@ -1343,125 +1263,120 @@ func init() {
|
|||||||
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
|
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
|
||||||
|
|
||||||
var fileDescriptor_7a0613f69d37b0a5 = []byte{
|
var fileDescriptor_7a0613f69d37b0a5 = []byte{
|
||||||
// 1875 bytes of a gzipped FileDescriptorProto
|
// 1805 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0xd1, 0x72, 0x22, 0xb9,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x73, 0x1a, 0xc9,
|
||||||
0xd5, 0xde, 0x36, 0x60, 0xe0, 0x00, 0x76, 0x5b, 0xf6, 0x78, 0x7a, 0xb0, 0xbd, 0xe3, 0x65, 0xf7,
|
0x15, 0xdf, 0x11, 0x20, 0xe0, 0x01, 0xd2, 0xa8, 0x25, 0xcb, 0x63, 0x64, 0xad, 0xb5, 0xec, 0x66,
|
||||||
0x9f, 0x75, 0x4d, 0xcd, 0x6f, 0x4f, 0x9c, 0xda, 0xad, 0xa9, 0xbd, 0x48, 0x0a, 0x83, 0x58, 0xf7,
|
0x57, 0xe5, 0x72, 0x24, 0x47, 0xa9, 0xdd, 0x72, 0xed, 0x21, 0x29, 0x0c, 0xcd, 0x6a, 0x6c, 0x98,
|
||||||
0x0c, 0x34, 0x5e, 0x01, 0xb3, 0x3b, 0xc9, 0x85, 0x4a, 0x06, 0xd9, 0x74, 0xb9, 0xe9, 0x66, 0xbb,
|
0x91, 0x1b, 0xf0, 0xae, 0x93, 0x43, 0x57, 0x1b, 0x5a, 0x62, 0x4a, 0xc3, 0x0c, 0x3b, 0xd3, 0x38,
|
||||||
0x85, 0x33, 0xce, 0x45, 0xaa, 0x52, 0xb9, 0xce, 0x73, 0xe4, 0x26, 0x77, 0x79, 0x91, 0x3c, 0x45,
|
0x56, 0x0e, 0xa9, 0xca, 0x07, 0xc8, 0xe7, 0xc8, 0x25, 0xb7, 0x7c, 0x91, 0xdc, 0xf2, 0x0d, 0x92,
|
||||||
0xf2, 0x04, 0x7b, 0x9f, 0x92, 0xd4, 0x0d, 0x8d, 0x8d, 0x27, 0xb9, 0x72, 0xeb, 0x3b, 0x9f, 0x8e,
|
0x4f, 0x90, 0x7b, 0xaa, 0xbb, 0x67, 0x60, 0x90, 0x90, 0x93, 0x93, 0xe8, 0xdf, 0xfb, 0xd7, 0xf3,
|
||||||
0xa4, 0x73, 0x8e, 0x3e, 0x1d, 0x0c, 0xbb, 0x61, 0x30, 0x13, 0x3c, 0x0c, 0xa7, 0xc3, 0x13, 0xfd,
|
0xde, 0xeb, 0xdf, 0x7b, 0x82, 0xfd, 0x28, 0x9c, 0x0b, 0x1e, 0x45, 0xb3, 0xd1, 0xa9, 0xfe, 0x75,
|
||||||
0x75, 0x3c, 0x0d, 0x03, 0x11, 0xa0, 0xe2, 0x1c, 0xaf, 0x16, 0xc3, 0xe9, 0x50, 0xa3, 0xb5, 0x3f,
|
0x32, 0x8b, 0x42, 0x11, 0xa2, 0xf2, 0x02, 0xaf, 0x97, 0xa3, 0xd9, 0x48, 0xa3, 0x8d, 0x3f, 0xe7,
|
||||||
0x67, 0x01, 0xf5, 0xb8, 0x3f, 0xba, 0x60, 0x77, 0x13, 0xee, 0x0b, 0xc2, 0x7f, 0x9e, 0xf1, 0x48,
|
0x01, 0xf5, 0x79, 0x30, 0xbe, 0x60, 0x37, 0x53, 0x1e, 0x08, 0xc2, 0x7f, 0x9e, 0xf3, 0x58, 0x20,
|
||||||
0x20, 0x04, 0xd9, 0x11, 0x8f, 0x84, 0x65, 0x1c, 0x1a, 0x47, 0x65, 0xa2, 0xbe, 0x91, 0x09, 0x19,
|
0x04, 0xf9, 0x31, 0x8f, 0x85, 0x65, 0x1c, 0x19, 0xc7, 0x55, 0xa2, 0x7e, 0x23, 0x13, 0x72, 0x6c,
|
||||||
0x36, 0x11, 0xd6, 0xda, 0xa1, 0x71, 0x94, 0x21, 0xf2, 0x13, 0x7d, 0x01, 0xe5, 0xa9, 0x9e, 0x47,
|
0x2a, 0xac, 0x8d, 0x23, 0xe3, 0x38, 0x47, 0xe4, 0x4f, 0xf4, 0x05, 0x54, 0x67, 0xda, 0x8e, 0x4e,
|
||||||
0xc7, 0x2c, 0x1a, 0x5b, 0x19, 0xc5, 0x2e, 0xc5, 0xd8, 0x39, 0x8b, 0xc6, 0xe8, 0x08, 0xcc, 0x2b,
|
0x58, 0x3c, 0xb1, 0x72, 0x4a, 0xbb, 0x92, 0x60, 0xe7, 0x2c, 0x9e, 0xa0, 0x63, 0x30, 0x2f, 0xbd,
|
||||||
0xd7, 0x67, 0x1e, 0x1d, 0x7a, 0xe2, 0x96, 0x8e, 0xb8, 0x27, 0x98, 0x95, 0x3d, 0x34, 0x8e, 0x72,
|
0x80, 0xf9, 0x74, 0xe4, 0x8b, 0x0f, 0x74, 0xcc, 0x7d, 0xc1, 0xac, 0xfc, 0x91, 0x71, 0x5c, 0x20,
|
||||||
0x64, 0x43, 0xe1, 0x0d, 0x4f, 0xdc, 0x36, 0x25, 0x8a, 0xbe, 0x86, 0xcd, 0xc4, 0x59, 0xa8, 0x77,
|
0x5b, 0x0a, 0x6f, 0xf9, 0xe2, 0x43, 0x5b, 0xa2, 0xe8, 0x1b, 0xd8, 0x4e, 0x9d, 0x45, 0xfa, 0x16,
|
||||||
0x61, 0xe5, 0x0e, 0x8d, 0xa3, 0x22, 0xd9, 0x98, 0x2e, 0xef, 0xed, 0x6b, 0xd8, 0x14, 0xee, 0x84,
|
0x56, 0xe1, 0xc8, 0x38, 0x2e, 0x93, 0xad, 0xd9, 0xea, 0xdd, 0xbe, 0x81, 0x6d, 0xe1, 0x4d, 0x79,
|
||||||
0x07, 0x33, 0x41, 0x23, 0x3e, 0x0c, 0xfc, 0x51, 0x64, 0xad, 0x6b, 0x8f, 0x31, 0xdc, 0xd3, 0x28,
|
0x38, 0x17, 0x34, 0xe6, 0xa3, 0x30, 0x18, 0xc7, 0xd6, 0xa6, 0xf6, 0x98, 0xc0, 0x7d, 0x8d, 0xa2,
|
||||||
0xaa, 0x41, 0xe5, 0x8a, 0x73, 0xea, 0xb9, 0x13, 0x57, 0xd0, 0x88, 0x09, 0x2b, 0xaf, 0xb6, 0x5e,
|
0x06, 0xd4, 0x2e, 0x39, 0xa7, 0xbe, 0x37, 0xf5, 0x04, 0x8d, 0x99, 0xb0, 0x8a, 0xea, 0xea, 0x95,
|
||||||
0xba, 0xe2, 0xbc, 0x2d, 0xb1, 0x1e, 0x13, 0xe8, 0x15, 0x98, 0xc1, 0x4c, 0x5c, 0x07, 0xae, 0x7f,
|
0x4b, 0xce, 0xbb, 0x12, 0xeb, 0x33, 0x81, 0x9e, 0x81, 0x19, 0xce, 0xc5, 0x55, 0xe8, 0x05, 0x57,
|
||||||
0x4d, 0x87, 0x63, 0xe6, 0x53, 0x77, 0x64, 0x15, 0x0e, 0x8d, 0xa3, 0xec, 0xd9, 0xda, 0x6b, 0x83,
|
0x74, 0x34, 0x61, 0x01, 0xf5, 0xc6, 0x56, 0xe9, 0xc8, 0x38, 0xce, 0xbf, 0xdc, 0x78, 0x6e, 0x90,
|
||||||
0x6c, 0x24, 0xb6, 0xc6, 0x98, 0xf9, 0xf6, 0x08, 0x1d, 0x00, 0xa8, 0x73, 0x28, 0x97, 0x56, 0x51,
|
0xad, 0x54, 0xd6, 0x9a, 0xb0, 0xc0, 0x1e, 0xa3, 0x43, 0x00, 0xf5, 0x1d, 0xca, 0xa5, 0x55, 0x56,
|
||||||
0xad, 0x5a, 0x94, 0x88, 0xf2, 0x87, 0x4e, 0xa1, 0xa4, 0x82, 0x4c, 0xc7, 0xae, 0x2f, 0x22, 0x0b,
|
0x51, 0xcb, 0x12, 0x51, 0xfe, 0xd0, 0x19, 0x54, 0x54, 0x92, 0xe9, 0xc4, 0x0b, 0x44, 0x6c, 0xc1,
|
||||||
0x0e, 0x33, 0x47, 0xa5, 0x53, 0xf3, 0xd8, 0xf3, 0x65, 0xbc, 0x89, 0xb4, 0x9c, 0xbb, 0xbe, 0x20,
|
0x51, 0xee, 0xb8, 0x72, 0x66, 0x9e, 0xf8, 0x81, 0xcc, 0x37, 0x91, 0x92, 0x73, 0x2f, 0x10, 0x24,
|
||||||
0x69, 0x12, 0xc2, 0x50, 0x90, 0xd1, 0xa5, 0xc2, 0xbb, 0xb5, 0x4a, 0x6a, 0xc2, 0xcb, 0xe3, 0x79,
|
0xab, 0x84, 0x30, 0x94, 0x64, 0x76, 0xa9, 0xf0, 0x3f, 0x58, 0x15, 0x65, 0xf0, 0xf4, 0x64, 0x51,
|
||||||
0xa6, 0x8e, 0x1f, 0xa6, 0xe6, 0xb8, 0xc9, 0x23, 0xd1, 0xf7, 0x6e, 0xb1, 0x2f, 0xc2, 0x3b, 0x92,
|
0xa9, 0x93, 0xbb, 0xa5, 0x39, 0x69, 0xf3, 0x58, 0x0c, 0xfc, 0x0f, 0x38, 0x10, 0xd1, 0x0d, 0x29,
|
||||||
0x1f, 0xe9, 0x51, 0xf5, 0x3b, 0x28, 0xa7, 0x0d, 0x32, 0x59, 0x37, 0xfc, 0x4e, 0xe5, 0x2f, 0x4b,
|
0x8e, 0xf5, 0xa9, 0xfe, 0x3d, 0x54, 0xb3, 0x02, 0x59, 0xac, 0x6b, 0x7e, 0xa3, 0xea, 0x97, 0x27,
|
||||||
0xe4, 0x27, 0xda, 0x81, 0xdc, 0x2d, 0xf3, 0x66, 0x5c, 0x25, 0xb0, 0x4c, 0xf4, 0xe0, 0xbb, 0xb5,
|
0xf2, 0x27, 0xda, 0x83, 0xc2, 0x07, 0xe6, 0xcf, 0xb9, 0x2a, 0x60, 0x95, 0xe8, 0xc3, 0xf7, 0x1b,
|
||||||
0x37, 0x46, 0xed, 0x0d, 0x6c, 0xf7, 0x43, 0x36, 0xbc, 0xb9, 0x57, 0x03, 0xf7, 0xb3, 0x6b, 0x3c,
|
0x2f, 0x8c, 0xc6, 0x0b, 0xd8, 0x1d, 0x44, 0x6c, 0x74, 0x7d, 0xab, 0x07, 0x6e, 0x57, 0xd7, 0xb8,
|
||||||
0xc8, 0x6e, 0xed, 0x4f, 0x50, 0x89, 0x27, 0xf5, 0x04, 0x13, 0xb3, 0x08, 0xfd, 0x3f, 0xe4, 0x22,
|
0x53, 0xdd, 0xc6, 0x9f, 0xa0, 0x96, 0x18, 0xf5, 0x05, 0x13, 0xf3, 0x18, 0xfd, 0x12, 0x0a, 0xb1,
|
||||||
0xc1, 0x04, 0x57, 0xe4, 0x8d, 0xd3, 0xa7, 0xa9, 0xa3, 0xa4, 0x88, 0x9c, 0x68, 0x16, 0xaa, 0x42,
|
0x60, 0x82, 0x2b, 0xe5, 0xad, 0xb3, 0x87, 0x99, 0x4f, 0xc9, 0x28, 0x72, 0xa2, 0xb5, 0x50, 0x1d,
|
||||||
0x61, 0x1a, 0x72, 0x77, 0xc2, 0xae, 0x93, 0x6d, 0xcd, 0xc7, 0xa8, 0x06, 0x39, 0x35, 0x59, 0x55,
|
0x4a, 0xb3, 0x88, 0x7b, 0x53, 0x76, 0x95, 0x5e, 0x6b, 0x71, 0x46, 0x0d, 0x28, 0x28, 0x63, 0xd5,
|
||||||
0x55, 0xe9, 0xb4, 0x9c, 0x0e, 0x23, 0xd1, 0xa6, 0xda, 0x6f, 0x60, 0x53, 0x8d, 0x5b, 0x9c, 0x7f,
|
0x55, 0x95, 0xb3, 0x6a, 0x36, 0x8d, 0x44, 0x8b, 0x1a, 0xbf, 0x81, 0x6d, 0x75, 0xee, 0x70, 0xfe,
|
||||||
0xaa, 0x72, 0x9f, 0x42, 0x9e, 0x4d, 0x74, 0x09, 0xe8, 0xea, 0x5d, 0x67, 0x13, 0x99, 0xfd, 0xda,
|
0xa9, 0xce, 0x7d, 0x08, 0x45, 0x36, 0xd5, 0x2d, 0xa0, 0xbb, 0x77, 0x93, 0x4d, 0x65, 0xf5, 0x1b,
|
||||||
0x08, 0xcc, 0xc5, 0xfc, 0x68, 0x1a, 0xf8, 0x11, 0x97, 0x15, 0x2b, 0x9d, 0xcb, 0x82, 0x90, 0xd5,
|
0x63, 0x30, 0x97, 0xf6, 0xf1, 0x2c, 0x0c, 0x62, 0x2e, 0x3b, 0x56, 0x3a, 0x97, 0x0d, 0x21, 0xbb,
|
||||||
0x33, 0x91, 0xb3, 0x0c, 0x35, 0x6b, 0x23, 0xc6, 0x5b, 0x9c, 0x77, 0x22, 0x26, 0xd0, 0x0b, 0x5d,
|
0x67, 0x2a, 0xad, 0x0c, 0x65, 0xb5, 0x95, 0xe0, 0x1d, 0xce, 0x7b, 0x31, 0x13, 0xe8, 0x6b, 0xdd,
|
||||||
0x88, 0xd4, 0x0b, 0x86, 0x37, 0xb2, 0xb4, 0xd9, 0x5d, 0xec, 0xbe, 0x22, 0xe1, 0x76, 0x30, 0xbc,
|
0x88, 0xd4, 0x0f, 0x47, 0xd7, 0xb2, 0xb5, 0xd9, 0x4d, 0xe2, 0xbe, 0x26, 0xe1, 0x6e, 0x38, 0xba,
|
||||||
0x69, 0x4a, 0xb0, 0xf6, 0x7b, 0x7d, 0xc5, 0xfa, 0x81, 0xde, 0xfb, 0xff, 0x1c, 0xde, 0x45, 0x08,
|
0x6e, 0x4b, 0xb0, 0xf1, 0x7b, 0xfd, 0xc4, 0x06, 0xa1, 0xbe, 0xfb, 0xff, 0x9d, 0xde, 0x65, 0x0a,
|
||||||
0xd6, 0x1e, 0x0f, 0x01, 0x85, 0xed, 0x25, 0xe7, 0xf1, 0x29, 0xd2, 0x91, 0x35, 0xee, 0x45, 0xf6,
|
0x36, 0xee, 0x4f, 0x01, 0x85, 0xdd, 0x15, 0xe7, 0xc9, 0x57, 0x64, 0x33, 0x6b, 0xdc, 0xca, 0xec,
|
||||||
0x15, 0xe4, 0xaf, 0x98, 0xeb, 0xcd, 0xc2, 0xc4, 0x31, 0x4a, 0xa5, 0xa9, 0xa5, 0x2d, 0x24, 0xa1,
|
0x33, 0x28, 0x5e, 0x32, 0xcf, 0x9f, 0x47, 0xa9, 0x63, 0x94, 0x29, 0x53, 0x47, 0x4b, 0x48, 0xaa,
|
||||||
0xd4, 0x7e, 0xc9, 0x43, 0x3e, 0x06, 0xd1, 0x29, 0x64, 0x87, 0xc1, 0x28, 0xc9, 0xee, 0xe7, 0x0f,
|
0xd2, 0xf8, 0x4f, 0x11, 0x8a, 0x09, 0x88, 0xce, 0x20, 0x3f, 0x0a, 0xc7, 0x69, 0x75, 0x3f, 0xbf,
|
||||||
0xa7, 0x25, 0x7f, 0x1b, 0xc1, 0x88, 0x13, 0xc5, 0x45, 0xbf, 0x85, 0x0d, 0x79, 0xb1, 0x7c, 0xee,
|
0x6b, 0x96, 0xfe, 0x6d, 0x85, 0x63, 0x4e, 0x94, 0x2e, 0xfa, 0x2d, 0x6c, 0xc9, 0x87, 0x15, 0x70,
|
||||||
0xd1, 0xd9, 0x74, 0xc4, 0xe6, 0x09, 0xb5, 0x52, 0xb3, 0x1b, 0x9a, 0x30, 0x50, 0x76, 0x52, 0x19,
|
0x9f, 0xce, 0x67, 0x63, 0xb6, 0x28, 0xa8, 0x95, 0xb1, 0x6e, 0x69, 0x85, 0xa1, 0x92, 0x93, 0xda,
|
||||||
0xa6, 0x87, 0x68, 0x0f, 0x8a, 0x63, 0xe1, 0x0d, 0x75, 0x26, 0xb2, 0xaa, 0xa0, 0x0b, 0x12, 0x50,
|
0x28, 0x7b, 0x44, 0x07, 0x50, 0x9e, 0x08, 0x7f, 0xa4, 0x2b, 0x91, 0x57, 0x0d, 0x5d, 0x92, 0x80,
|
||||||
0x39, 0xa8, 0x41, 0x25, 0xf0, 0xdd, 0xc0, 0xa7, 0xd1, 0x98, 0xd1, 0xd3, 0x6f, 0xbe, 0x55, 0x9a,
|
0xaa, 0x41, 0x03, 0x6a, 0x61, 0xe0, 0x85, 0x01, 0x8d, 0x27, 0x8c, 0x9e, 0x7d, 0xfb, 0x9d, 0xe2,
|
||||||
0x51, 0x26, 0x25, 0x05, 0xf6, 0xc6, 0xec, 0xf4, 0x9b, 0x6f, 0xd1, 0x73, 0x28, 0xa9, 0x5b, 0xcb,
|
0x8c, 0x2a, 0xa9, 0x28, 0xb0, 0x3f, 0x61, 0x67, 0xdf, 0x7e, 0x87, 0x9e, 0x40, 0x45, 0xbd, 0x5a,
|
||||||
0x3f, 0x4e, 0xdd, 0xf0, 0x4e, 0x89, 0x45, 0x85, 0xa8, 0x8b, 0x8c, 0x15, 0x22, 0xaf, 0xc6, 0x95,
|
0xfe, 0x71, 0xe6, 0x45, 0x37, 0x8a, 0x2c, 0x6a, 0x44, 0x3d, 0x64, 0xac, 0x10, 0xf9, 0x34, 0x2e,
|
||||||
0xc7, 0xae, 0x23, 0x25, 0x10, 0x15, 0xa2, 0x07, 0xe8, 0x35, 0xec, 0xc4, 0x31, 0xa0, 0x51, 0x30,
|
0x7d, 0x76, 0x15, 0x2b, 0x82, 0xa8, 0x11, 0x7d, 0x40, 0xcf, 0x61, 0x2f, 0xc9, 0x01, 0x8d, 0xc3,
|
||||||
0x0b, 0x87, 0x9c, 0xba, 0xfe, 0x88, 0x7f, 0x54, 0xf2, 0x50, 0x21, 0x28, 0xb6, 0xf5, 0x94, 0xc9,
|
0x79, 0x34, 0xe2, 0xd4, 0x0b, 0xc6, 0xfc, 0xa3, 0xa2, 0x87, 0x1a, 0x41, 0x89, 0xac, 0xaf, 0x44,
|
||||||
0x96, 0x16, 0xb4, 0x0b, 0xeb, 0x63, 0xee, 0x5e, 0x8f, 0xb5, 0x34, 0x54, 0x48, 0x3c, 0xaa, 0xfd,
|
0xb6, 0x94, 0xa0, 0x7d, 0xd8, 0x9c, 0x70, 0xef, 0x6a, 0xa2, 0xa9, 0xa1, 0x46, 0x92, 0x53, 0xe3,
|
||||||
0x3d, 0x07, 0xa5, 0x54, 0x60, 0x50, 0x19, 0x0a, 0x04, 0xf7, 0x30, 0x79, 0x8f, 0x9b, 0xe6, 0x67,
|
0x6f, 0x05, 0xa8, 0x64, 0x12, 0x83, 0xaa, 0x50, 0x22, 0xb8, 0x8f, 0xc9, 0x5b, 0xdc, 0x36, 0x3f,
|
||||||
0xe8, 0x08, 0xbe, 0xb2, 0x9d, 0x46, 0x97, 0x10, 0xdc, 0xe8, 0xd3, 0x2e, 0xa1, 0x03, 0xe7, 0x9d,
|
0x43, 0xc7, 0xf0, 0x95, 0xed, 0xb4, 0x5c, 0x42, 0x70, 0x6b, 0x40, 0x5d, 0x42, 0x87, 0xce, 0x6b,
|
||||||
0xd3, 0xfd, 0xd1, 0xa1, 0x17, 0xf5, 0x0f, 0x1d, 0xec, 0xf4, 0x69, 0x13, 0xf7, 0xeb, 0x76, 0xbb,
|
0xc7, 0xfd, 0xd1, 0xa1, 0x17, 0xcd, 0x77, 0x3d, 0xec, 0x0c, 0x68, 0x1b, 0x0f, 0x9a, 0x76, 0xb7,
|
||||||
0x67, 0x1a, 0x68, 0x1f, 0xac, 0x05, 0x33, 0x31, 0xd7, 0x3b, 0xdd, 0x81, 0xd3, 0x37, 0xd7, 0xd0,
|
0x6f, 0x1a, 0xe8, 0x31, 0x58, 0x4b, 0xcd, 0x54, 0xdc, 0xec, 0xb9, 0x43, 0x67, 0x60, 0x6e, 0xa0,
|
||||||
0x73, 0xd8, 0x6b, 0xd9, 0x4e, 0xbd, 0x4d, 0x17, 0x9c, 0x46, 0xbb, 0xff, 0x9e, 0xe2, 0x9f, 0x2e,
|
0x27, 0x70, 0xd0, 0xb1, 0x9d, 0x66, 0x97, 0x2e, 0x75, 0x5a, 0xdd, 0xc1, 0x5b, 0x8a, 0x7f, 0xba,
|
||||||
0x6c, 0xf2, 0xc1, 0xcc, 0xac, 0x22, 0x9c, 0xf7, 0xdb, 0x8d, 0xc4, 0x43, 0x16, 0x3d, 0x83, 0x27,
|
0xb0, 0xc9, 0x3b, 0x33, 0xb7, 0x4e, 0xe1, 0x7c, 0xd0, 0x6d, 0xa5, 0x1e, 0xf2, 0xe8, 0x11, 0x3c,
|
||||||
0x9a, 0xa0, 0xa7, 0xd0, 0x7e, 0xb7, 0x4b, 0x7b, 0xdd, 0xae, 0x63, 0xe6, 0xd0, 0x16, 0x54, 0x6c,
|
0xd0, 0x0a, 0xda, 0x84, 0x0e, 0x5c, 0x97, 0xf6, 0x5d, 0xd7, 0x31, 0x0b, 0x68, 0x07, 0x6a, 0xb6,
|
||||||
0xe7, 0x7d, 0xbd, 0x6d, 0x37, 0x29, 0xc1, 0xf5, 0x76, 0xc7, 0x5c, 0x47, 0xdb, 0xb0, 0x79, 0x9f,
|
0xf3, 0xb6, 0xd9, 0xb5, 0xdb, 0x94, 0xe0, 0x66, 0xb7, 0x67, 0x6e, 0xa2, 0x5d, 0xd8, 0xbe, 0xad,
|
||||||
0x97, 0x97, 0x2e, 0x12, 0x5e, 0xd7, 0xb1, 0xbb, 0x0e, 0x7d, 0x8f, 0x49, 0xcf, 0xee, 0x3a, 0x66,
|
0x57, 0x94, 0x2e, 0x52, 0x3d, 0xd7, 0xb1, 0x5d, 0x87, 0xbe, 0xc5, 0xa4, 0x6f, 0xbb, 0x8e, 0x59,
|
||||||
0x01, 0xed, 0x02, 0x5a, 0x36, 0x9d, 0x77, 0xea, 0x0d, 0xb3, 0x88, 0x9e, 0xc0, 0xd6, 0x32, 0xfe,
|
0x42, 0xfb, 0x80, 0x56, 0x45, 0xe7, 0xbd, 0x66, 0xcb, 0x2c, 0xa3, 0x07, 0xb0, 0xb3, 0x8a, 0xbf,
|
||||||
0x0e, 0x7f, 0x30, 0x01, 0x59, 0xb0, 0xa3, 0x37, 0x46, 0xcf, 0x70, 0xbb, 0xfb, 0x23, 0xed, 0xd8,
|
0xc6, 0xef, 0x4c, 0x40, 0x16, 0xec, 0xe9, 0x8b, 0xd1, 0x97, 0xb8, 0xeb, 0xfe, 0x48, 0x7b, 0xb6,
|
||||||
0x8e, 0xdd, 0x19, 0x74, 0xcc, 0x12, 0xda, 0x01, 0xb3, 0x85, 0x31, 0xb5, 0x9d, 0xde, 0xa0, 0xd5,
|
0x63, 0xf7, 0x86, 0x3d, 0xb3, 0x82, 0xf6, 0xc0, 0xec, 0x60, 0x4c, 0x6d, 0xa7, 0x3f, 0xec, 0x74,
|
||||||
0xb2, 0x1b, 0x36, 0x76, 0xfa, 0x66, 0x59, 0xaf, 0xbc, 0xea, 0xe0, 0x15, 0x39, 0xa1, 0x71, 0x5e,
|
0xec, 0x96, 0x8d, 0x9d, 0x81, 0x59, 0xd5, 0x91, 0xd7, 0x7d, 0x78, 0x4d, 0x1a, 0xb4, 0xce, 0x9b,
|
||||||
0x77, 0x1c, 0xdc, 0xa6, 0x4d, 0xbb, 0x57, 0x3f, 0x6b, 0xe3, 0xa6, 0xb9, 0x81, 0x0e, 0xe0, 0x59,
|
0x8e, 0x83, 0xbb, 0xb4, 0x6d, 0xf7, 0x9b, 0x2f, 0xbb, 0xb8, 0x6d, 0x6e, 0xa1, 0x43, 0x78, 0x34,
|
||||||
0x1f, 0x77, 0x2e, 0xba, 0xa4, 0x4e, 0x3e, 0xd0, 0xc4, 0xde, 0xaa, 0xdb, 0xed, 0x01, 0xc1, 0xe6,
|
0xc0, 0xbd, 0x0b, 0x97, 0x34, 0xc9, 0x3b, 0x9a, 0xca, 0x3b, 0x4d, 0xbb, 0x3b, 0x24, 0xd8, 0xdc,
|
||||||
0x26, 0xfa, 0x02, 0x0e, 0x08, 0xfe, 0x61, 0x60, 0x13, 0xdc, 0xa4, 0x4e, 0xb7, 0x89, 0x69, 0x0b,
|
0x46, 0x5f, 0xc0, 0x21, 0xc1, 0x6f, 0x86, 0x36, 0xc1, 0x6d, 0xea, 0xb8, 0x6d, 0x4c, 0x3b, 0xb8,
|
||||||
0xd7, 0xfb, 0x03, 0x82, 0x69, 0xc7, 0xee, 0xf5, 0x6c, 0xe7, 0x7b, 0xd3, 0x44, 0x5f, 0xc1, 0xe1,
|
0x39, 0x18, 0x12, 0x4c, 0x7b, 0x76, 0xbf, 0x6f, 0x3b, 0x3f, 0x98, 0x26, 0xfa, 0x0a, 0x8e, 0x16,
|
||||||
0x9c, 0x32, 0x77, 0x70, 0x8f, 0xb5, 0x25, 0xcf, 0x97, 0xa4, 0xd4, 0xc1, 0x3f, 0xf5, 0xe9, 0x05,
|
0x2a, 0x0b, 0x07, 0xb7, 0xb4, 0x76, 0xe4, 0xf7, 0xa5, 0x25, 0x75, 0xf0, 0x4f, 0x03, 0x7a, 0x81,
|
||||||
0xc6, 0xc4, 0x44, 0xa8, 0x0a, 0xbb, 0x8b, 0xe5, 0xf5, 0x02, 0xf1, 0xda, 0xdb, 0xd2, 0x76, 0x81,
|
0x31, 0x31, 0x11, 0xaa, 0xc3, 0xfe, 0x32, 0xbc, 0x0e, 0x90, 0xc4, 0xde, 0x95, 0xb2, 0x0b, 0x4c,
|
||||||
0x49, 0xa7, 0xee, 0xc8, 0x04, 0x2f, 0xd9, 0x76, 0xe4, 0xb6, 0x17, 0xb6, 0xfb, 0xdb, 0x7e, 0x82,
|
0x7a, 0x4d, 0x47, 0x16, 0x78, 0x45, 0xb6, 0x27, 0xaf, 0xbd, 0x94, 0xdd, 0xbe, 0xf6, 0x03, 0x84,
|
||||||
0x10, 0x6c, 0xa4, 0xb2, 0xd2, 0xaa, 0x13, 0x73, 0x17, 0xed, 0xc0, 0x66, 0xb2, 0x83, 0x84, 0xf8,
|
0x60, 0x2b, 0x53, 0x95, 0x4e, 0x93, 0x98, 0xfb, 0x68, 0x0f, 0xb6, 0xd3, 0x1b, 0xa4, 0x8a, 0xff,
|
||||||
0xaf, 0x3c, 0x7a, 0x0a, 0x68, 0xe0, 0x10, 0x5c, 0x6f, 0xca, 0x80, 0xcc, 0x0d, 0xff, 0xce, 0xbf,
|
0x2a, 0xa2, 0x87, 0x80, 0x86, 0x0e, 0xc1, 0xcd, 0xb6, 0x4c, 0xc8, 0x42, 0xf0, 0xef, 0xe2, 0xab,
|
||||||
0xcd, 0x16, 0xd6, 0xcc, 0x4c, 0xed, 0x1f, 0x19, 0xa8, 0x2c, 0xdd, 0x4b, 0xb4, 0x0f, 0xc5, 0xc8,
|
0x7c, 0x69, 0xc3, 0xcc, 0x35, 0xfe, 0x9e, 0x83, 0xda, 0xca, 0xbb, 0x44, 0x8f, 0xa1, 0x1c, 0x7b,
|
||||||
0xbd, 0xf6, 0x99, 0x90, 0xca, 0xa1, 0x45, 0x65, 0x01, 0xa8, 0xb7, 0x71, 0xcc, 0x5c, 0x5f, 0xab,
|
0x57, 0x01, 0x13, 0x92, 0x39, 0x34, 0xa9, 0x2c, 0x01, 0x35, 0x1b, 0x27, 0xcc, 0x0b, 0x34, 0x9b,
|
||||||
0x99, 0x56, 0xf3, 0xa2, 0x42, 0x94, 0x96, 0xed, 0x41, 0x3e, 0x79, 0x5f, 0x33, 0xf3, 0xf7, 0x75,
|
0x69, 0x36, 0x2f, 0x2b, 0x44, 0x71, 0xd9, 0x01, 0x14, 0xd3, 0xf9, 0x9a, 0x5b, 0xcc, 0xd7, 0xcd,
|
||||||
0x7d, 0xa8, 0xdf, 0xd5, 0x7d, 0x28, 0x4a, 0xc9, 0x8c, 0x04, 0x9b, 0x4c, 0xd5, 0x15, 0xaf, 0x90,
|
0x91, 0x9e, 0xab, 0x8f, 0xa1, 0x2c, 0x29, 0x33, 0x16, 0x6c, 0x3a, 0x53, 0x4f, 0xbc, 0x46, 0x96,
|
||||||
0x05, 0x80, 0xbe, 0x84, 0xca, 0x84, 0x47, 0x11, 0xbb, 0xe6, 0x54, 0x5f, 0x53, 0x50, 0x8c, 0x72,
|
0x00, 0xfa, 0x12, 0x6a, 0x53, 0x1e, 0xc7, 0xec, 0x8a, 0x53, 0xfd, 0x4c, 0x41, 0x69, 0x54, 0x13,
|
||||||
0x0c, 0xb6, 0xd4, 0x6d, 0xfd, 0x12, 0x12, 0xd9, 0x88, 0x49, 0x39, 0x4d, 0x8a, 0x41, 0x4d, 0xba,
|
0xb0, 0xa3, 0x5e, 0xeb, 0x97, 0x90, 0xd2, 0x46, 0xa2, 0x54, 0xd0, 0x4a, 0x09, 0xa8, 0x95, 0x6e,
|
||||||
0xaf, 0xd8, 0x82, 0xc5, 0x6a, 0x90, 0x56, 0x6c, 0xc1, 0xd0, 0x4b, 0xd8, 0xd2, 0x92, 0xe3, 0xfa,
|
0x33, 0xb6, 0x60, 0x09, 0x1b, 0x64, 0x19, 0x5b, 0x30, 0xf4, 0x14, 0x76, 0x34, 0xe5, 0x78, 0x81,
|
||||||
0xee, 0x64, 0x36, 0xd1, 0xd2, 0x93, 0x57, 0xd2, 0xb3, 0xa9, 0xa4, 0x47, 0xe3, 0x4a, 0x81, 0x9e,
|
0x37, 0x9d, 0x4f, 0x35, 0xf5, 0x14, 0x15, 0xf5, 0x6c, 0x2b, 0xea, 0xd1, 0xb8, 0x62, 0xa0, 0x47,
|
||||||
0x41, 0xe1, 0x92, 0x45, 0x5c, 0x3e, 0x16, 0xb1, 0x34, 0xe4, 0xe5, 0xb8, 0xc5, 0xb9, 0x34, 0xc9,
|
0x50, 0x7a, 0xcf, 0x62, 0x2e, 0x87, 0x45, 0x42, 0x0d, 0x45, 0x79, 0xee, 0x70, 0x2e, 0x45, 0x72,
|
||||||
0x27, 0x24, 0x94, 0xa2, 0xa7, 0x15, 0x21, 0x7f, 0xc5, 0x39, 0x91, 0xb1, 0x9c, 0xaf, 0xc0, 0x3e,
|
0x84, 0x44, 0x92, 0xf4, 0x34, 0x23, 0x14, 0x2f, 0x39, 0x27, 0x32, 0x97, 0x8b, 0x08, 0xec, 0xe3,
|
||||||
0x2e, 0x56, 0x28, 0xa5, 0x56, 0xd0, 0xb8, 0x5a, 0xe1, 0x25, 0x6c, 0xf1, 0x8f, 0x22, 0x64, 0x34,
|
0x32, 0x42, 0x25, 0x13, 0x41, 0xe3, 0x2a, 0xc2, 0x53, 0xd8, 0xe1, 0x1f, 0x45, 0xc4, 0x68, 0x38,
|
||||||
0x98, 0xb2, 0x9f, 0x67, 0x9c, 0x8e, 0x98, 0x60, 0x56, 0x59, 0x05, 0x78, 0x53, 0x19, 0xba, 0x0a,
|
0x63, 0x3f, 0xcf, 0x39, 0x1d, 0x33, 0xc1, 0xac, 0xaa, 0x4a, 0xf0, 0xb6, 0x12, 0xb8, 0x0a, 0x6f,
|
||||||
0x6f, 0x32, 0xc1, 0x6a, 0xfb, 0x50, 0x25, 0x3c, 0xe2, 0xa2, 0xe3, 0x46, 0x91, 0x1b, 0xf8, 0x8d,
|
0x33, 0xc1, 0x1a, 0x8f, 0xa1, 0x4e, 0x78, 0xcc, 0x45, 0xcf, 0x8b, 0x63, 0x2f, 0x0c, 0x5a, 0x61,
|
||||||
0xc0, 0x17, 0x61, 0xe0, 0xc5, 0x6f, 0x4e, 0xed, 0x00, 0xf6, 0x56, 0x5a, 0xf5, 0xa3, 0x21, 0x27,
|
0x20, 0xa2, 0xd0, 0x4f, 0x66, 0x4e, 0xe3, 0x10, 0x0e, 0xd6, 0x4a, 0xf5, 0xd0, 0x90, 0xc6, 0x6f,
|
||||||
0xff, 0x30, 0xe3, 0xe1, 0xdd, 0xea, 0xc9, 0x77, 0xb0, 0xb7, 0xd2, 0x1a, 0xbf, 0x38, 0xaf, 0x20,
|
0xe6, 0x3c, 0xba, 0x59, 0x6f, 0xfc, 0x06, 0x0e, 0xd6, 0x4a, 0x93, 0x89, 0xf3, 0x0c, 0x0a, 0x33,
|
||||||
0xe7, 0x07, 0x23, 0x1e, 0x59, 0x86, 0xea, 0x62, 0x76, 0x53, 0xf2, 0xee, 0x04, 0x23, 0x7e, 0xee,
|
0xe6, 0x45, 0xb1, 0xb5, 0xa1, 0xb6, 0x98, 0xfd, 0x95, 0xd1, 0xef, 0x45, 0xe7, 0x5e, 0x2c, 0xc2,
|
||||||
0x46, 0x22, 0x08, 0xef, 0x88, 0x26, 0x49, 0xf6, 0x94, 0xb9, 0x61, 0x64, 0xad, 0x3d, 0x60, 0x5f,
|
0xe8, 0x86, 0x68, 0xa5, 0x57, 0xf9, 0x92, 0x61, 0x6e, 0x34, 0xfe, 0x69, 0x40, 0x25, 0x23, 0x94,
|
||||||
0x30, 0x37, 0x9c, 0xb3, 0x15, 0xa9, 0xf6, 0x17, 0x03, 0x4a, 0x29, 0x27, 0x52, 0x68, 0xa7, 0xb3,
|
0x7d, 0x10, 0x84, 0x63, 0x4e, 0x2f, 0xa3, 0x70, 0x9a, 0x76, 0xd8, 0x02, 0x40, 0x16, 0x14, 0xd5,
|
||||||
0xcb, 0xa4, 0xc1, 0x29, 0x93, 0x78, 0x84, 0x5e, 0xc0, 0x86, 0xc7, 0x22, 0x41, 0xa5, 0x36, 0x53,
|
0x41, 0x84, 0x49, 0x7b, 0xa5, 0xc7, 0xd5, 0xfe, 0xc9, 0xa9, 0x19, 0x9c, 0xe9, 0x9f, 0x33, 0xd8,
|
||||||
0x99, 0xd2, 0xf8, 0x41, 0xbe, 0x87, 0xa2, 0x63, 0x40, 0x81, 0x18, 0xf3, 0x90, 0x46, 0xb3, 0xe1,
|
0x9b, 0x7a, 0x01, 0x9d, 0xf1, 0x80, 0xf9, 0xde, 0x1f, 0x39, 0x4d, 0x77, 0x81, 0xbc, 0x52, 0x5c,
|
||||||
0x90, 0x47, 0x11, 0x9d, 0x86, 0xc1, 0xa5, 0xaa, 0xcb, 0x35, 0xb2, 0xc2, 0xf2, 0x36, 0x5b, 0xc8,
|
0x2b, 0x43, 0x2f, 0xe0, 0xa1, 0xcf, 0x62, 0x41, 0x99, 0x10, 0x7c, 0x3a, 0x13, 0x34, 0x9e, 0x8f,
|
||||||
0x9a, 0xb9, 0xda, 0x2f, 0x06, 0x94, 0x52, 0x9b, 0x93, 0x55, 0x2b, 0x0f, 0x43, 0xaf, 0xc2, 0x60,
|
0x46, 0x3c, 0x8e, 0x2f, 0xe7, 0xbe, 0xea, 0x98, 0x12, 0xb9, 0x4f, 0xfc, 0x2a, 0x5f, 0x2a, 0x98,
|
||||||
0x92, 0xdc, 0x87, 0x39, 0x80, 0x2c, 0xc8, 0xab, 0x81, 0x08, 0xe2, 0xcb, 0x90, 0x0c, 0x97, 0xab,
|
0x9b, 0x8d, 0xbf, 0x1a, 0xb0, 0xf3, 0x72, 0xee, 0xf9, 0xe3, 0x95, 0x99, 0xff, 0x08, 0x4a, 0x32,
|
||||||
0x3d, 0xa3, 0x36, 0x98, 0xaa, 0xf6, 0x53, 0xd8, 0x99, 0xb8, 0x3e, 0x9d, 0x72, 0x9f, 0x79, 0xee,
|
0x40, 0x66, 0xa7, 0x90, 0x8b, 0x89, 0x2a, 0xf2, 0xba, 0x45, 0x79, 0x63, 0xed, 0xa2, 0xbc, 0x6e,
|
||||||
0x1f, 0x39, 0x4d, 0x3a, 0x97, 0xac, 0x22, 0xae, 0xb4, 0xa1, 0x1a, 0x94, 0x97, 0x4e, 0x92, 0x53,
|
0x65, 0xcd, 0xdd, 0xbb, 0xb2, 0x3e, 0x81, 0xca, 0x24, 0x9c, 0xd1, 0xd9, 0xfc, 0xfd, 0x35, 0xbf,
|
||||||
0x27, 0x59, 0xc2, 0xd0, 0x1b, 0x78, 0xaa, 0xa2, 0xc0, 0x84, 0xe0, 0x93, 0xa9, 0x48, 0x0e, 0x78,
|
0x89, 0xad, 0xfc, 0x51, 0xee, 0xb8, 0x4a, 0x60, 0x12, 0xce, 0x2e, 0x34, 0xd2, 0x78, 0x01, 0x28,
|
||||||
0x35, 0xf3, 0xd4, 0x1d, 0x28, 0x90, 0xc7, 0xcc, 0xb5, 0xbf, 0x19, 0xb0, 0x75, 0x36, 0x73, 0xbd,
|
0x7b, 0xd1, 0xa4, 0x9a, 0x8b, 0xd5, 0xc3, 0xb8, 0x77, 0xf5, 0x78, 0xfa, 0x17, 0x03, 0xaa, 0xd9,
|
||||||
0xd1, 0x52, 0xff, 0xf2, 0x0c, 0x0a, 0x72, 0xf9, 0x54, 0x7f, 0x24, 0x9b, 0x2c, 0x55, 0xb0, 0xab,
|
0xad, 0x0e, 0xd5, 0xa0, 0x6c, 0x3b, 0xb4, 0xd3, 0xb5, 0x7f, 0x38, 0x1f, 0x98, 0x9f, 0xc9, 0x63,
|
||||||
0x9a, 0xfe, 0xb5, 0x95, 0x4d, 0xff, 0xaa, 0xf6, 0x3b, 0xf3, 0x68, 0xfb, 0xfd, 0x1c, 0x4a, 0xe3,
|
0x7f, 0xd8, 0x6a, 0x61, 0xdc, 0xc6, 0x6d, 0xd3, 0x90, 0xcc, 0x24, 0x49, 0x06, 0xb7, 0xe9, 0xc0,
|
||||||
0x60, 0x4a, 0x75, 0xb2, 0x23, 0x2b, 0x7b, 0x98, 0x39, 0x2a, 0x13, 0x18, 0x07, 0xd3, 0x0b, 0x8d,
|
0xee, 0x61, 0x77, 0x28, 0x67, 0xd6, 0x2e, 0x6c, 0x27, 0x98, 0xe3, 0x52, 0xe2, 0x0e, 0x07, 0xd8,
|
||||||
0xd4, 0xde, 0x00, 0x4a, 0x6f, 0x34, 0xae, 0xcc, 0x79, 0x1b, 0x65, 0x3c, 0xda, 0x46, 0xbd, 0xfc,
|
0xcc, 0x21, 0x13, 0xaa, 0x09, 0x88, 0x09, 0x71, 0x89, 0x99, 0x97, 0x44, 0x9b, 0x20, 0x77, 0xe7,
|
||||||
0xab, 0x01, 0xe5, 0x74, 0x87, 0x8a, 0x2a, 0x50, 0xb4, 0x1d, 0xda, 0x6a, 0xdb, 0xdf, 0x9f, 0xf7,
|
0x5f, 0x3a, 0x1e, 0x0b, 0x67, 0xff, 0xc8, 0xc3, 0xa6, 0xba, 0x60, 0x84, 0xce, 0xa1, 0x92, 0x59,
|
||||||
0xcd, 0xcf, 0xe4, 0xb0, 0x37, 0x68, 0x34, 0x30, 0x6e, 0xe2, 0xa6, 0x69, 0x48, 0x95, 0x95, 0x82,
|
0x9d, 0xd1, 0xe1, 0x27, 0x57, 0xea, 0xba, 0xb5, 0x7e, 0x4d, 0x9d, 0xc7, 0xcf, 0x0d, 0xf4, 0x0a,
|
||||||
0x89, 0x9b, 0xb4, 0x6f, 0x77, 0x70, 0x77, 0x20, 0xdf, 0xdf, 0x6d, 0xd8, 0x8c, 0x31, 0xa7, 0x4b,
|
0xaa, 0xd9, 0xe5, 0x18, 0x65, 0x97, 0x9e, 0x35, 0x5b, 0xf3, 0x27, 0x7d, 0xbd, 0x06, 0x13, 0xc7,
|
||||||
0x49, 0x77, 0xd0, 0xc7, 0x66, 0x06, 0x99, 0x50, 0x8e, 0x41, 0x4c, 0x48, 0x97, 0x98, 0x59, 0xf9,
|
0xc2, 0x9b, 0xca, 0x25, 0x27, 0x59, 0x3b, 0x51, 0x3d, 0xa3, 0x7f, 0x6b, 0x97, 0xad, 0x1f, 0xac,
|
||||||
0x68, 0xc4, 0xc8, 0xc3, 0xb7, 0x3c, 0x79, 0xea, 0x73, 0xa7, 0xff, 0xcc, 0xc2, 0xba, 0xda, 0x60,
|
0x95, 0x25, 0x15, 0xea, 0xea, 0x4f, 0x4c, 0x16, 0xbf, 0x3b, 0x9f, 0xb8, 0xba, 0x6d, 0xd6, 0x3f,
|
||||||
0x88, 0xce, 0xa1, 0x94, 0xfa, 0x19, 0x80, 0x0e, 0x3e, 0xf9, 0xf3, 0xa0, 0x6a, 0xad, 0x6e, 0xb9,
|
0xbf, 0x4f, 0x9c, 0x78, 0x1b, 0xc3, 0xee, 0x1a, 0x66, 0x40, 0xbf, 0xc8, 0xde, 0xe0, 0x5e, 0x5e,
|
||||||
0x67, 0xd1, 0x6b, 0x03, 0xbd, 0x85, 0x72, 0xba, 0xd1, 0x47, 0xe9, 0x06, 0x6e, 0xc5, 0x2f, 0x80,
|
0xa9, 0x7f, 0xfd, 0xbf, 0xd4, 0x96, 0x51, 0xd6, 0x50, 0xc8, 0x4a, 0x94, 0xfb, 0x09, 0x68, 0x25,
|
||||||
0x4f, 0xfa, 0x7a, 0x07, 0x26, 0x8e, 0x84, 0x3b, 0x91, 0x0d, 0x5b, 0xdc, 0x42, 0xa3, 0x6a, 0x8a,
|
0xca, 0xa7, 0x98, 0xc8, 0x06, 0x58, 0x76, 0x34, 0x7a, 0x9c, 0xb1, 0xba, 0xf3, 0x22, 0xeb, 0x87,
|
||||||
0x7f, 0xaf, 0x2f, 0xaf, 0xee, 0xad, 0xb4, 0xc5, 0x19, 0x6a, 0xeb, 0x23, 0xc6, 0x4d, 0xec, 0x83,
|
0xf7, 0x48, 0xb5, 0xab, 0x97, 0xbf, 0xfa, 0xdd, 0xe9, 0x95, 0x27, 0x26, 0xf3, 0xf7, 0x27, 0xa3,
|
||||||
0x23, 0x2e, 0x77, 0xce, 0xd5, 0xcf, 0x1f, 0x33, 0xc7, 0xde, 0x46, 0xb0, 0xbd, 0x42, 0xe5, 0xd0,
|
0x70, 0x7a, 0xea, 0xcb, 0x6d, 0x2e, 0xf0, 0x82, 0xab, 0x80, 0x8b, 0x3f, 0x84, 0xd1, 0xf5, 0xa9,
|
||||||
0xff, 0xa5, 0x77, 0xf0, 0xa8, 0x46, 0x56, 0x5f, 0xfc, 0x37, 0xda, 0x62, 0x95, 0x15, 0x72, 0xb8,
|
0x1f, 0x8c, 0x4f, 0xd5, 0xc3, 0x38, 0x5d, 0x78, 0x79, 0xbf, 0xa9, 0xfe, 0xb1, 0xfe, 0xf5, 0x7f,
|
||||||
0xb4, 0xca, 0xe3, 0x62, 0xba, 0xb4, 0xca, 0xa7, 0x54, 0xd5, 0x06, 0x58, 0x54, 0x34, 0xda, 0x4f,
|
0x03, 0x00, 0x00, 0xff, 0xff, 0x3e, 0x15, 0x37, 0x36, 0x88, 0x0f, 0x00, 0x00,
|
||||||
0xcd, 0x7a, 0x70, 0x23, 0xab, 0x07, 0x8f, 0x58, 0xb5, 0xab, 0xb3, 0x5f, 0xfd, 0xee, 0xe4, 0xda,
|
|
||||||
0x15, 0xe3, 0xd9, 0xe5, 0xf1, 0x30, 0x98, 0x9c, 0x78, 0xb2, 0x33, 0xf5, 0x5d, 0xff, 0xda, 0xe7,
|
|
||||||
0xe2, 0x0f, 0x41, 0x78, 0x73, 0xe2, 0xf9, 0xa3, 0x13, 0x75, 0x31, 0x4e, 0xe6, 0x5e, 0x2e, 0xd7,
|
|
||||||
0xd5, 0x3f, 0x09, 0x7e, 0xfd, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe1, 0xb1, 0x0f, 0x8c, 0x54,
|
|
||||||
0x10, 0x00, 0x00,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -337,30 +337,12 @@ message QueryMissionControlRequest {}
|
|||||||
|
|
||||||
/// QueryMissionControlResponse contains mission control state.
|
/// QueryMissionControlResponse contains mission control state.
|
||||||
message QueryMissionControlResponse {
|
message QueryMissionControlResponse {
|
||||||
/// Node-level mission control state.
|
reserved 1;
|
||||||
repeated NodeHistory nodes = 1 [json_name = "nodes"];
|
|
||||||
|
|
||||||
/// Node pair-level mission control state.
|
/// Node pair-level mission control state.
|
||||||
repeated PairHistory pairs = 2 [json_name = "pairs"];
|
repeated PairHistory pairs = 2 [json_name = "pairs"];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NodeHistory contains the mission control state for a particular node.
|
|
||||||
message NodeHistory {
|
|
||||||
/// Node pubkey
|
|
||||||
bytes pubkey = 1 [json_name = "pubkey"];
|
|
||||||
|
|
||||||
/// Time stamp of last failure. Set to zero if no failure happened yet.
|
|
||||||
int64 last_fail_time = 2 [json_name = "last_fail_time"];
|
|
||||||
|
|
||||||
/**
|
|
||||||
Estimation of success probability of forwarding towards peers of this node
|
|
||||||
for which no specific history is available.
|
|
||||||
**/
|
|
||||||
float other_success_prob = 3 [json_name = "other_success_prob"];
|
|
||||||
|
|
||||||
reserved 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PairHistory contains the mission control state for a particular node pair.
|
/// PairHistory contains the mission control state for a particular node pair.
|
||||||
message PairHistory {
|
message PairHistory {
|
||||||
/// The source node pubkey of the pair.
|
/// The source node pubkey of the pair.
|
||||||
@ -375,8 +357,7 @@ message PairHistory {
|
|||||||
/// Minimum penalization amount (only applies to failed attempts).
|
/// Minimum penalization amount (only applies to failed attempts).
|
||||||
int64 min_penalize_amt_sat = 4 [json_name = "min_penalize_amt_sat"];
|
int64 min_penalize_amt_sat = 4 [json_name = "min_penalize_amt_sat"];
|
||||||
|
|
||||||
/// Estimation of success probability for this pair.
|
reserved 5;
|
||||||
float success_prob = 5 [json_name = "success_prob"];
|
|
||||||
|
|
||||||
/// Whether the last payment attempt through this pair was successful.
|
/// Whether the last payment attempt through this pair was successful.
|
||||||
bool last_attempt_successful = 6 [json_name = "last_attempt_successful"];
|
bool last_attempt_successful = 6 [json_name = "last_attempt_successful"];
|
||||||
|
@ -466,22 +466,6 @@ func (s *Server) QueryMissionControl(ctx context.Context,
|
|||||||
|
|
||||||
snapshot := s.cfg.RouterBackend.MissionControl.GetHistorySnapshot()
|
snapshot := s.cfg.RouterBackend.MissionControl.GetHistorySnapshot()
|
||||||
|
|
||||||
rpcNodes := make([]*NodeHistory, 0, len(snapshot.Nodes))
|
|
||||||
for _, n := range snapshot.Nodes {
|
|
||||||
// Copy node struct to prevent loop variable binding bugs.
|
|
||||||
node := n
|
|
||||||
|
|
||||||
rpcNode := NodeHistory{
|
|
||||||
Pubkey: node.Node[:],
|
|
||||||
LastFailTime: node.LastFail.Unix(),
|
|
||||||
OtherSuccessProb: float32(
|
|
||||||
node.OtherSuccessProb,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcNodes = append(rpcNodes, &rpcNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcPairs := make([]*PairHistory, 0, len(snapshot.Pairs))
|
rpcPairs := make([]*PairHistory, 0, len(snapshot.Pairs))
|
||||||
for _, p := range snapshot.Pairs {
|
for _, p := range snapshot.Pairs {
|
||||||
// Prevent binding to loop variable.
|
// Prevent binding to loop variable.
|
||||||
@ -494,7 +478,6 @@ func (s *Server) QueryMissionControl(ctx context.Context,
|
|||||||
MinPenalizeAmtSat: int64(
|
MinPenalizeAmtSat: int64(
|
||||||
pair.MinPenalizeAmt.ToSatoshis(),
|
pair.MinPenalizeAmt.ToSatoshis(),
|
||||||
),
|
),
|
||||||
SuccessProb: float32(pair.SuccessProb),
|
|
||||||
LastAttemptSuccessful: pair.LastAttemptSuccessful,
|
LastAttemptSuccessful: pair.LastAttemptSuccessful,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,7 +485,6 @@ func (s *Server) QueryMissionControl(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
response := QueryMissionControlResponse{
|
response := QueryMissionControlResponse{
|
||||||
Nodes: rpcNodes,
|
|
||||||
Pairs: rpcPairs,
|
Pairs: rpcPairs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -47,8 +46,15 @@ const (
|
|||||||
// prevSuccessProbability is the assumed probability for node pairs that
|
// prevSuccessProbability is the assumed probability for node pairs that
|
||||||
// successfully relayed the previous attempt.
|
// successfully relayed the previous attempt.
|
||||||
prevSuccessProbability = 0.95
|
prevSuccessProbability = 0.95
|
||||||
|
|
||||||
|
// DefaultAprioriWeight is the default a priori weight. See
|
||||||
|
// MissionControlConfig for further explanation.
|
||||||
|
DefaultAprioriWeight = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NodeResults contains previous results from a node to its peers.
|
||||||
|
type NodeResults map[route.Vertex]timedPairResult
|
||||||
|
|
||||||
// MissionControl contains state which summarizes the past attempts of HTLC
|
// MissionControl contains state which summarizes the past attempts of HTLC
|
||||||
// routing by external callers when sending payments throughout the network. It
|
// routing by external callers when sending payments throughout the network. It
|
||||||
// acts as a shared memory during routing attempts with the goal to optimize the
|
// acts as a shared memory during routing attempts with the goal to optimize the
|
||||||
@ -59,11 +65,11 @@ const (
|
|||||||
// since the last failure is used to estimate a success probability that is fed
|
// since the last failure is used to estimate a success probability that is fed
|
||||||
// into the path finding process for subsequent payment attempts.
|
// into the path finding process for subsequent payment attempts.
|
||||||
type MissionControl struct {
|
type MissionControl struct {
|
||||||
// lastPairResult tracks the last payment result per node pair.
|
// lastPairResult tracks the last payment result (on a pair basis) for
|
||||||
lastPairResult map[DirectedNodePair]timedPairResult
|
// each transited node. This is a multi-layer map that allows us to look
|
||||||
|
// up the failure history of all connected channels (node pairs) for a
|
||||||
// lastNodeFailure tracks the last node level failure per node.
|
// particular node.
|
||||||
lastNodeFailure map[route.Vertex]time.Time
|
lastPairResult map[route.Vertex]NodeResults
|
||||||
|
|
||||||
// lastSecondChance tracks the last time a second chance was granted for
|
// lastSecondChance tracks the last time a second chance was granted for
|
||||||
// a directed node pair.
|
// a directed node pair.
|
||||||
@ -77,6 +83,10 @@ type MissionControl struct {
|
|||||||
|
|
||||||
store *missionControlStore
|
store *missionControlStore
|
||||||
|
|
||||||
|
// estimator is the probability estimator that is used with the payment
|
||||||
|
// results that mission control collects.
|
||||||
|
estimator *probabilityEstimator
|
||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
// TODO(roasbeef): further counters, if vertex continually unavailable,
|
// TODO(roasbeef): further counters, if vertex continually unavailable,
|
||||||
@ -99,6 +109,15 @@ type MissionControlConfig struct {
|
|||||||
// MaxMcHistory defines the maximum number of payment results that are
|
// MaxMcHistory defines the maximum number of payment results that are
|
||||||
// held on disk.
|
// held on disk.
|
||||||
MaxMcHistory int
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// timedPairResult describes a timestamped pair result.
|
// timedPairResult describes a timestamped pair result.
|
||||||
@ -112,28 +131,11 @@ type timedPairResult struct {
|
|||||||
// MissionControlSnapshot contains a snapshot of the current state of mission
|
// MissionControlSnapshot contains a snapshot of the current state of mission
|
||||||
// control.
|
// control.
|
||||||
type MissionControlSnapshot struct {
|
type MissionControlSnapshot struct {
|
||||||
// Nodes contains the per node information of this snapshot.
|
|
||||||
Nodes []MissionControlNodeSnapshot
|
|
||||||
|
|
||||||
// Pairs is a list of channels for which specific information is
|
// Pairs is a list of channels for which specific information is
|
||||||
// logged.
|
// logged.
|
||||||
Pairs []MissionControlPairSnapshot
|
Pairs []MissionControlPairSnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
// MissionControlNodeSnapshot contains a snapshot of the current node state in
|
|
||||||
// mission control.
|
|
||||||
type MissionControlNodeSnapshot struct {
|
|
||||||
// Node pubkey.
|
|
||||||
Node route.Vertex
|
|
||||||
|
|
||||||
// LastFail is the time of last failure.
|
|
||||||
LastFail time.Time
|
|
||||||
|
|
||||||
// OtherSuccessProb is the success probability for pairs not in
|
|
||||||
// the Pairs slice.
|
|
||||||
OtherSuccessProb float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// MissionControlPairSnapshot contains a snapshot of the current node pair
|
// MissionControlPairSnapshot contains a snapshot of the current node pair
|
||||||
// state in mission control.
|
// state in mission control.
|
||||||
type MissionControlPairSnapshot struct {
|
type MissionControlPairSnapshot struct {
|
||||||
@ -147,9 +149,6 @@ type MissionControlPairSnapshot struct {
|
|||||||
// penalized.
|
// penalized.
|
||||||
MinPenalizeAmt lnwire.MilliSatoshi
|
MinPenalizeAmt lnwire.MilliSatoshi
|
||||||
|
|
||||||
// SuccessProb is the success probability estimation for this channel.
|
|
||||||
SuccessProb float64
|
|
||||||
|
|
||||||
// LastAttemptSuccessful indicates whether the last payment attempt
|
// LastAttemptSuccessful indicates whether the last payment attempt
|
||||||
// through this pair was successful.
|
// through this pair was successful.
|
||||||
LastAttemptSuccessful bool
|
LastAttemptSuccessful bool
|
||||||
@ -171,21 +170,29 @@ func NewMissionControl(db *bbolt.DB, cfg *MissionControlConfig) (
|
|||||||
*MissionControl, error) {
|
*MissionControl, error) {
|
||||||
|
|
||||||
log.Debugf("Instantiating mission control with config: "+
|
log.Debugf("Instantiating mission control with config: "+
|
||||||
"PenaltyHalfLife=%v, AprioriHopProbability=%v",
|
"PenaltyHalfLife=%v, AprioriHopProbability=%v, "+
|
||||||
cfg.PenaltyHalfLife, cfg.AprioriHopProbability)
|
"AprioriWeight=%v", cfg.PenaltyHalfLife,
|
||||||
|
cfg.AprioriHopProbability, cfg.AprioriWeight)
|
||||||
|
|
||||||
store, err := newMissionControlStore(db, cfg.MaxMcHistory)
|
store, err := newMissionControlStore(db, cfg.MaxMcHistory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
estimator := &probabilityEstimator{
|
||||||
|
aprioriHopProbability: cfg.AprioriHopProbability,
|
||||||
|
aprioriWeight: cfg.AprioriWeight,
|
||||||
|
penaltyHalfLife: cfg.PenaltyHalfLife,
|
||||||
|
prevSuccessProbability: prevSuccessProbability,
|
||||||
|
}
|
||||||
|
|
||||||
mc := &MissionControl{
|
mc := &MissionControl{
|
||||||
lastPairResult: make(map[DirectedNodePair]timedPairResult),
|
lastPairResult: make(map[route.Vertex]NodeResults),
|
||||||
lastNodeFailure: make(map[route.Vertex]time.Time),
|
|
||||||
lastSecondChance: make(map[DirectedNodePair]time.Time),
|
lastSecondChance: make(map[DirectedNodePair]time.Time),
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
store: store,
|
store: store,
|
||||||
|
estimator: estimator,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mc.init(); err != nil {
|
if err := mc.init(); err != nil {
|
||||||
@ -226,8 +233,7 @@ func (m *MissionControl) ResetHistory() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.lastPairResult = make(map[DirectedNodePair]timedPairResult)
|
m.lastPairResult = make(map[route.Vertex]NodeResults)
|
||||||
m.lastNodeFailure = make(map[route.Vertex]time.Time)
|
|
||||||
m.lastSecondChance = make(map[DirectedNodePair]time.Time)
|
m.lastSecondChance = make(map[DirectedNodePair]time.Time)
|
||||||
|
|
||||||
log.Debugf("Mission control history cleared")
|
log.Debugf("Mission control history cleared")
|
||||||
@ -243,62 +249,40 @@ func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex,
|
|||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
return m.getPairProbability(fromNode, toNode, amt)
|
now := m.now()
|
||||||
|
results := m.lastPairResult[fromNode]
|
||||||
|
|
||||||
|
return m.estimator.getPairProbability(now, results, toNode, amt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getProbAfterFail returns a probability estimate based on a last failure time.
|
// setLastPairResult stores a result for a node pair.
|
||||||
func (m *MissionControl) getProbAfterFail(lastFailure time.Time) float64 {
|
func (m *MissionControl) setLastPairResult(fromNode,
|
||||||
if lastFailure.IsZero() {
|
toNode route.Vertex, result timedPairResult) {
|
||||||
return m.cfg.AprioriHopProbability
|
|
||||||
|
nodePairs, ok := m.lastPairResult[fromNode]
|
||||||
|
if !ok {
|
||||||
|
nodePairs = make(NodeResults)
|
||||||
|
m.lastPairResult[fromNode] = nodePairs
|
||||||
}
|
}
|
||||||
|
|
||||||
timeSinceLastFailure := m.now().Sub(lastFailure)
|
nodePairs[toNode] = result
|
||||||
|
|
||||||
// Calculate success probability. It is an exponential curve that brings
|
|
||||||
// the probability down to zero when a failure occurs. From there it
|
|
||||||
// recovers asymptotically back to the a priori probability. The rate at
|
|
||||||
// which this happens is controlled by the penaltyHalfLife parameter.
|
|
||||||
exp := -timeSinceLastFailure.Hours() / m.cfg.PenaltyHalfLife.Hours()
|
|
||||||
probability := m.cfg.AprioriHopProbability * (1 - math.Pow(2, exp))
|
|
||||||
|
|
||||||
return probability
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPairProbability estimates the probability of successfully
|
// setAllFail stores a fail result for all known connection of the given node.
|
||||||
// traversing from fromNode to toNode based on historical payment outcomes.
|
func (m *MissionControl) setAllFail(fromNode route.Vertex,
|
||||||
func (m *MissionControl) getPairProbability(fromNode,
|
timestamp time.Time) {
|
||||||
toNode route.Vertex, amt lnwire.MilliSatoshi) float64 {
|
|
||||||
|
|
||||||
// Start by getting the last node level failure. A node failure is
|
nodePairs, ok := m.lastPairResult[fromNode]
|
||||||
// considered a failure that would have affected every edge. Therefore
|
if !ok {
|
||||||
// we insert a node level failure into the history of every channel. If
|
return
|
||||||
// there is none, lastFail will be zero.
|
|
||||||
lastFail := m.lastNodeFailure[fromNode]
|
|
||||||
|
|
||||||
// Retrieve the last pair outcome.
|
|
||||||
pair := NewDirectedNodePair(fromNode, toNode)
|
|
||||||
lastPairResult, ok := m.lastPairResult[pair]
|
|
||||||
|
|
||||||
// Only look at the last pair outcome if it happened after the last node
|
|
||||||
// level failure. Otherwise the node level failure is the most recent
|
|
||||||
// and used as the basis for calculation of the probability.
|
|
||||||
if ok && lastPairResult.timestamp.After(lastFail) {
|
|
||||||
if lastPairResult.success {
|
|
||||||
return prevSuccessProbability
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take into account a minimum penalize amount. For balance
|
|
||||||
// errors, a failure may be reported with such a minimum to
|
|
||||||
// prevent too aggresive penalization. We only take into account
|
|
||||||
// a previous failure if the amount that we currently get the
|
|
||||||
// probability for is greater or equal than the minPenalizeAmt
|
|
||||||
// of the previous failure.
|
|
||||||
if amt >= lastPairResult.minPenalizeAmt {
|
|
||||||
lastFail = lastPairResult.timestamp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.getProbAfterFail(lastFail)
|
for connection := range nodePairs {
|
||||||
|
nodePairs[connection] = timedPairResult{
|
||||||
|
timestamp: timestamp,
|
||||||
|
pairResult: failPairResult(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requestSecondChance checks whether the node fromNode can have a second chance
|
// requestSecondChance checks whether the node fromNode can have a second chance
|
||||||
@ -339,40 +323,27 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
|||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
log.Debugf("Requesting history snapshot from mission control: "+
|
log.Debugf("Requesting history snapshot from mission control: "+
|
||||||
"node_failure_count=%v, pair_result_count=%v",
|
"pair_result_count=%v", len(m.lastPairResult))
|
||||||
len(m.lastNodeFailure), len(m.lastPairResult))
|
|
||||||
|
|
||||||
nodes := make([]MissionControlNodeSnapshot, 0, len(m.lastNodeFailure))
|
|
||||||
for v, h := range m.lastNodeFailure {
|
|
||||||
otherProb := m.getPairProbability(v, route.Vertex{}, 0)
|
|
||||||
|
|
||||||
nodes = append(nodes, MissionControlNodeSnapshot{
|
|
||||||
Node: v,
|
|
||||||
LastFail: h,
|
|
||||||
OtherSuccessProb: otherProb,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairResult))
|
pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairResult))
|
||||||
|
|
||||||
for v, h := range m.lastPairResult {
|
for fromNode, fromPairs := range m.lastPairResult {
|
||||||
// Show probability assuming amount meets min
|
for toNode, result := range fromPairs {
|
||||||
// penalization amount.
|
|
||||||
prob := m.getPairProbability(v.From, v.To, h.minPenalizeAmt)
|
|
||||||
|
|
||||||
pair := MissionControlPairSnapshot{
|
pair := NewDirectedNodePair(fromNode, toNode)
|
||||||
Pair: v,
|
|
||||||
MinPenalizeAmt: h.minPenalizeAmt,
|
pairSnapshot := MissionControlPairSnapshot{
|
||||||
Timestamp: h.timestamp,
|
Pair: pair,
|
||||||
SuccessProb: prob,
|
MinPenalizeAmt: result.minPenalizeAmt,
|
||||||
LastAttemptSuccessful: h.success,
|
Timestamp: result.timestamp,
|
||||||
|
LastAttemptSuccessful: result.success,
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs = append(pairs, pairSnapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
pairs = append(pairs, pair)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot := MissionControlSnapshot{
|
snapshot := MissionControlSnapshot{
|
||||||
Nodes: nodes,
|
|
||||||
Pairs: pairs,
|
Pairs: pairs,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,11 +434,28 @@ func (m *MissionControl) applyPaymentResult(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a node-level failure, record a failure for every tried
|
||||||
|
// connection of that node. A node-level failure can be considered as a
|
||||||
|
// failure that would have occurred with any of the node's channels.
|
||||||
|
//
|
||||||
|
// Ideally we'd also record the failure for the untried connections of
|
||||||
|
// the node. Unfortunately this would require access to the graph and
|
||||||
|
// adding this dependency and db calls does not outweigh the benefits.
|
||||||
|
//
|
||||||
|
// Untried connections will fall back to the node probability. After the
|
||||||
|
// call to setAllPairResult below, the node probability will be equal to
|
||||||
|
// the probability of the tried channels except that the a priori
|
||||||
|
// probability is mixed in too. This effect is controlled by the
|
||||||
|
// aprioriWeight parameter. If that parameter isn't set to an extreme
|
||||||
|
// and there are a few known connections, there shouldn't be much of a
|
||||||
|
// difference. The largest difference occurs when aprioriWeight is 1. In
|
||||||
|
// that case, a node-level failure would not be applied to untried
|
||||||
|
// channels.
|
||||||
if i.nodeFailure != nil {
|
if i.nodeFailure != nil {
|
||||||
log.Debugf("Reporting node failure to Mission Control: "+
|
log.Debugf("Reporting node failure to Mission Control: "+
|
||||||
"node=%v", *i.nodeFailure)
|
"node=%v", *i.nodeFailure)
|
||||||
|
|
||||||
m.lastNodeFailure[*i.nodeFailure] = result.timeReply
|
m.setAllFail(*i.nodeFailure, result.timeReply)
|
||||||
}
|
}
|
||||||
|
|
||||||
for pair, pairResult := range i.pairResults {
|
for pair, pairResult := range i.pairResults {
|
||||||
@ -480,10 +468,10 @@ func (m *MissionControl) applyPaymentResult(
|
|||||||
pair, pairResult.minPenalizeAmt)
|
pair, pairResult.minPenalizeAmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.lastPairResult[pair] = timedPairResult{
|
m.setLastPairResult(pair.From, pair.To, timedPairResult{
|
||||||
timestamp: result.timeReply,
|
timestamp: result.timeReply,
|
||||||
pairResult: pairResult,
|
pairResult: pairResult,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return i.finalFailureReason
|
return i.finalFailureReason
|
||||||
|
@ -32,6 +32,10 @@ var (
|
|||||||
mcTestTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
mcTestTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
||||||
mcTestNode1 = mcTestRoute.Hops[0].PubKeyBytes
|
mcTestNode1 = mcTestRoute.Hops[0].PubKeyBytes
|
||||||
mcTestNode2 = mcTestRoute.Hops[1].PubKeyBytes
|
mcTestNode2 = mcTestRoute.Hops[1].PubKeyBytes
|
||||||
|
|
||||||
|
testPenaltyHalfLife = 30 * time.Minute
|
||||||
|
testAprioriHopProbability = 0.9
|
||||||
|
testAprioriWeight = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
type mcTestContext struct {
|
type mcTestContext struct {
|
||||||
@ -73,8 +77,9 @@ func (ctx *mcTestContext) restartMc() {
|
|||||||
mc, err := NewMissionControl(
|
mc, err := NewMissionControl(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
&MissionControlConfig{
|
&MissionControlConfig{
|
||||||
PenaltyHalfLife: 30 * time.Minute,
|
PenaltyHalfLife: testPenaltyHalfLife,
|
||||||
AprioriHopProbability: 0.8,
|
AprioriHopProbability: testAprioriHopProbability,
|
||||||
|
AprioriWeight: testAprioriWeight,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -133,20 +138,23 @@ func TestMissionControl(t *testing.T) {
|
|||||||
|
|
||||||
testTime := time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
testTime := time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
||||||
|
|
||||||
// Initial probability is expected to be 1.
|
// Initial probability is expected to be the a priori.
|
||||||
ctx.expectP(1000, 0.8)
|
ctx.expectP(1000, testAprioriHopProbability)
|
||||||
|
|
||||||
// Expect probability to be zero after reporting the edge as failed.
|
// Expect probability to be zero after reporting the edge as failed.
|
||||||
ctx.reportFailure(1000, lnwire.NewTemporaryChannelFailure(nil))
|
ctx.reportFailure(1000, lnwire.NewTemporaryChannelFailure(nil))
|
||||||
ctx.expectP(1000, 0)
|
ctx.expectP(1000, 0)
|
||||||
|
|
||||||
// As we reported with a min penalization amt, a lower amt than reported
|
// As we reported with a min penalization amt, a lower amt than reported
|
||||||
// should be unaffected.
|
// should return the node probability, which is the a priori
|
||||||
ctx.expectP(500, 0.8)
|
// probability.
|
||||||
|
ctx.expectP(500, testAprioriHopProbability)
|
||||||
|
|
||||||
// Edge decay started.
|
// Edge decay started. The node probability weighted average should now
|
||||||
|
// have shifted from 1:1 to 1:0.5 -> 60%. The connection probability is
|
||||||
|
// half way through the recovery, so we expect 30% here.
|
||||||
ctx.now = testTime.Add(30 * time.Minute)
|
ctx.now = testTime.Add(30 * time.Minute)
|
||||||
ctx.expectP(1000, 0.4)
|
ctx.expectP(1000, 0.3)
|
||||||
|
|
||||||
// Edge fails again, this time without a min penalization amt. The edge
|
// Edge fails again, this time without a min penalization amt. The edge
|
||||||
// should be penalized regardless of amount.
|
// should be penalized regardless of amount.
|
||||||
@ -156,26 +164,22 @@ func TestMissionControl(t *testing.T) {
|
|||||||
|
|
||||||
// Edge decay started.
|
// Edge decay started.
|
||||||
ctx.now = testTime.Add(60 * time.Minute)
|
ctx.now = testTime.Add(60 * time.Minute)
|
||||||
ctx.expectP(1000, 0.4)
|
ctx.expectP(1000, 0.3)
|
||||||
|
|
||||||
// Restart mission control to test persistence.
|
// Restart mission control to test persistence.
|
||||||
ctx.restartMc()
|
ctx.restartMc()
|
||||||
ctx.expectP(1000, 0.4)
|
ctx.expectP(1000, 0.3)
|
||||||
|
|
||||||
// A node level failure should bring probability of every channel back
|
// A node level failure should bring probability of all known channels
|
||||||
// to zero.
|
// back to zero.
|
||||||
ctx.reportFailure(0, lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}))
|
ctx.reportFailure(0, lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}))
|
||||||
ctx.expectP(1000, 0)
|
ctx.expectP(1000, 0)
|
||||||
|
|
||||||
// Check whether history snapshot looks sane.
|
// Check whether history snapshot looks sane.
|
||||||
history := ctx.mc.GetHistorySnapshot()
|
history := ctx.mc.GetHistorySnapshot()
|
||||||
if len(history.Nodes) != 1 {
|
|
||||||
t.Fatalf("unexpected number of nodes: expected 1 got %v",
|
|
||||||
len(history.Nodes))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(history.Pairs) != 2 {
|
if len(history.Pairs) != 3 {
|
||||||
t.Fatalf("expected 2 pairs, but got %v", len(history.Pairs))
|
t.Fatalf("expected 3 pairs, but got %v", len(history.Pairs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test reporting a success.
|
// Test reporting a success.
|
||||||
@ -192,7 +196,7 @@ func TestMissionControlChannelUpdate(t *testing.T) {
|
|||||||
ctx.reportFailure(
|
ctx.reportFailure(
|
||||||
0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}),
|
0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}),
|
||||||
)
|
)
|
||||||
ctx.expectP(0, 0.8)
|
ctx.expectP(0, testAprioriHopProbability)
|
||||||
|
|
||||||
// Report another failure for the same channel. We expect it to be
|
// Report another failure for the same channel. We expect it to be
|
||||||
// pruned.
|
// pruned.
|
||||||
|
155
routing/probability_estimator.go
Normal file
155
routing/probability_estimator.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"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
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// prevSuccessProbability is the assumed probability for node pairs that
|
||||||
|
// successfully relayed the previous attempt.
|
||||||
|
prevSuccessProbability float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNodeProbability calculates the probability for connections from a node
|
||||||
|
// that have not been tried before. The results parameter is a list of last
|
||||||
|
// payment results for that node.
|
||||||
|
func (p *probabilityEstimator) getNodeProbability(now time.Time,
|
||||||
|
results NodeResults, amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
|
// 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 there is no channel history, our best estimate is still the a
|
||||||
|
// priori probability.
|
||||||
|
if len(results) == 0 {
|
||||||
|
return p.aprioriHopProbability
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value of the apriori weight is in the range [0, 1]. Convert it to
|
||||||
|
// a factor that properly expresses the intention of the weight in the
|
||||||
|
// following weight average calculation. When the apriori weight is 0,
|
||||||
|
// the apriori factor is also 0. This means it won't have any effect on
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Calculate a weighted average consisting of the apriori probability
|
||||||
|
// and historical observations. This is the part that incentivizes nodes
|
||||||
|
// to make sure that all (not just some) of their channels are in good
|
||||||
|
// shape. Senders will steer around nodes that have shown a few
|
||||||
|
// failures, even though there may be many channels still untried.
|
||||||
|
//
|
||||||
|
// If there is just a single observation and the apriori weight is 0,
|
||||||
|
// this single observation will totally determine the node probability.
|
||||||
|
// The node probability is returned for all other channels of the node.
|
||||||
|
// This means that one failure will lead to the success probability
|
||||||
|
// estimates for all other channels being 0 too. The probability for the
|
||||||
|
// channel that was tried will not even recover, because it is
|
||||||
|
// recovering to the node probability (which is zero). So one failure
|
||||||
|
// 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
|
||||||
|
totalWeight := aprioriFactor
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
age := now.Sub(result.timestamp)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// Weigh success with a constant high weight of 1. There is no
|
||||||
|
// decay.
|
||||||
|
case result.success:
|
||||||
|
totalWeight++
|
||||||
|
probabilitiesTotal += p.prevSuccessProbability
|
||||||
|
|
||||||
|
// Weigh failures in accordance with their age. The base
|
||||||
|
// probability of a failure is considered zero, so nothing needs
|
||||||
|
// to be added to probabilitiesTotal.
|
||||||
|
case amt >= result.minPenalizeAmt:
|
||||||
|
totalWeight += p.getWeight(age)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return probabilitiesTotal / totalWeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWeight calculates a weight in the range [0, 1] that should be assigned to
|
||||||
|
// a payment result. Weight follows an exponential curve that starts at 1 when
|
||||||
|
// 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()
|
||||||
|
return math.Pow(2, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPairProbability estimates the probability of successfully traversing to
|
||||||
|
// toNode based on historical payment outcomes for the from node. Those outcomes
|
||||||
|
// are passed in via the results parameter.
|
||||||
|
func (p *probabilityEstimator) getPairProbability(
|
||||||
|
now time.Time, results NodeResults,
|
||||||
|
toNode route.Vertex, amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
|
// Retrieve the last pair outcome.
|
||||||
|
lastPairResult, ok := results[toNode]
|
||||||
|
|
||||||
|
// If there is no history for this pair, return the node probability
|
||||||
|
// that is a probability estimate for untried channel.
|
||||||
|
if !ok {
|
||||||
|
return p.getNodeProbability(now, results, amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For successes, we have a fixed (high) probability. Those pairs
|
||||||
|
// will be assumed good until proven otherwise.
|
||||||
|
if lastPairResult.success {
|
||||||
|
return p.prevSuccessProbability
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeProbability := p.getNodeProbability(now, results, amt)
|
||||||
|
|
||||||
|
// Take into account a minimum penalize amount. For balance errors, a
|
||||||
|
// failure may be reported with such a minimum to prevent too aggressive
|
||||||
|
// penalization. If the current amount is smaller than the amount that
|
||||||
|
// previously triggered a failure, we act as if this is an untried
|
||||||
|
// channel.
|
||||||
|
if amt < lastPairResult.minPenalizeAmt {
|
||||||
|
return nodeProbability
|
||||||
|
}
|
||||||
|
|
||||||
|
timeSinceLastFailure := now.Sub(lastPairResult.timestamp)
|
||||||
|
|
||||||
|
// Calculate success probability based on the weight of the last
|
||||||
|
// failure. When the failure is fresh, its weight is 1 and we'll return
|
||||||
|
// probability 0. Over time the probability recovers to the node
|
||||||
|
// probability. It would be as if this channel was never tried before.
|
||||||
|
weight := p.getWeight(timeSinceLastFailure)
|
||||||
|
probability := nodeProbability * (1 - weight)
|
||||||
|
|
||||||
|
return probability
|
||||||
|
}
|
163
routing/probability_estimator_test.go
Normal file
163
routing/probability_estimator_test.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Define node identifiers
|
||||||
|
node1 = 1
|
||||||
|
node2 = 2
|
||||||
|
node3 = 3
|
||||||
|
|
||||||
|
// untriedNode is a node id for which we don't record any results in
|
||||||
|
// this test. This can be used to assert the probability for untried
|
||||||
|
// ndoes.
|
||||||
|
untriedNode = 255
|
||||||
|
|
||||||
|
// Define test estimator parameters.
|
||||||
|
aprioriHopProb = 0.6
|
||||||
|
aprioriWeight = 0.75
|
||||||
|
aprioriPrevSucProb = 0.95
|
||||||
|
)
|
||||||
|
|
||||||
|
type estimatorTestContext struct {
|
||||||
|
t *testing.T
|
||||||
|
estimator *probabilityEstimator
|
||||||
|
|
||||||
|
// results contains a list of last results. Every element in the list
|
||||||
|
// corresponds to the last result towards a node. The list index equals
|
||||||
|
// the node id. So the first element in the list is the result towards
|
||||||
|
// node 0.
|
||||||
|
results map[int]timedPairResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEstimatorTestContext(t *testing.T) *estimatorTestContext {
|
||||||
|
return &estimatorTestContext{
|
||||||
|
t: t,
|
||||||
|
estimator: &probabilityEstimator{
|
||||||
|
aprioriHopProbability: aprioriHopProb,
|
||||||
|
aprioriWeight: aprioriWeight,
|
||||||
|
penaltyHalfLife: time.Hour,
|
||||||
|
prevSuccessProbability: aprioriPrevSucProb,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertPairProbability asserts that the calculated success probability is
|
||||||
|
// correct.
|
||||||
|
func (c *estimatorTestContext) assertPairProbability(now time.Time,
|
||||||
|
toNode byte, amt lnwire.MilliSatoshi, expectedProb float64) {
|
||||||
|
|
||||||
|
c.t.Helper()
|
||||||
|
|
||||||
|
results := make(NodeResults)
|
||||||
|
for i, r := range c.results {
|
||||||
|
results[route.Vertex{byte(i)}] = r
|
||||||
|
}
|
||||||
|
|
||||||
|
const tolerance = 0.01
|
||||||
|
|
||||||
|
p := c.estimator.getPairProbability(now, results, route.Vertex{toNode}, amt)
|
||||||
|
diff := p - expectedProb
|
||||||
|
if diff > tolerance || diff < -tolerance {
|
||||||
|
c.t.Fatalf("expected probability %v for node %v, but got %v",
|
||||||
|
expectedProb, toNode, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbabilityEstimatorNoResults tests the probability estimation when no
|
||||||
|
// results are available.
|
||||||
|
func TestProbabilityEstimatorNoResults(t *testing.T) {
|
||||||
|
ctx := newEstimatorTestContext(t)
|
||||||
|
|
||||||
|
ctx.assertPairProbability(testTime, 0, 0, aprioriHopProb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbabilityEstimatorOneSuccess tests the probability estimation for nodes
|
||||||
|
// that have a single success result.
|
||||||
|
func TestProbabilityEstimatorOneSuccess(t *testing.T) {
|
||||||
|
ctx := newEstimatorTestContext(t)
|
||||||
|
|
||||||
|
ctx.results = map[int]timedPairResult{
|
||||||
|
node1: {
|
||||||
|
timestamp: testTime.Add(-time.Hour),
|
||||||
|
pairResult: successPairResult(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because of the previous success, this channel keep reporting a high
|
||||||
|
// probability.
|
||||||
|
ctx.assertPairProbability(
|
||||||
|
testTime, node1, 100, aprioriPrevSucProb,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Untried channels are also influenced by the success. With a
|
||||||
|
// aprioriWeight of 0.75, the a priori probability is assigned weight 3.
|
||||||
|
expectedP := (3*aprioriHopProb + 1*aprioriPrevSucProb) / 4
|
||||||
|
ctx.assertPairProbability(testTime, untriedNode, 100, expectedP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbabilityEstimatorOneFailure tests the probability estimation for nodes
|
||||||
|
// that have a single failure.
|
||||||
|
func TestProbabilityEstimatorOneFailure(t *testing.T) {
|
||||||
|
ctx := newEstimatorTestContext(t)
|
||||||
|
|
||||||
|
ctx.results = map[int]timedPairResult{
|
||||||
|
node1: {
|
||||||
|
timestamp: testTime.Add(-time.Hour),
|
||||||
|
pairResult: failPairResult(0),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// For an untried node, we expected the node probability. The weight for
|
||||||
|
// the failure after one hour is 0.5. This makes the node probability
|
||||||
|
// 0.51:
|
||||||
|
expectedNodeProb := (3*aprioriHopProb + 0.5*0) / 3.5
|
||||||
|
ctx.assertPairProbability(testTime, untriedNode, 100, expectedNodeProb)
|
||||||
|
|
||||||
|
// The pair probability decays back to the node probability. With the
|
||||||
|
// weight at 0.5, we expected a pair probability of 0.5 * 0.51 = 0.25.
|
||||||
|
ctx.assertPairProbability(testTime, node1, 100, expectedNodeProb/2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbabilityEstimatorMix tests the probability estimation for nodes for
|
||||||
|
// which a mix of successes and failures is recorded.
|
||||||
|
func TestProbabilityEstimatorMix(t *testing.T) {
|
||||||
|
ctx := newEstimatorTestContext(t)
|
||||||
|
|
||||||
|
ctx.results = map[int]timedPairResult{
|
||||||
|
node1: {
|
||||||
|
timestamp: testTime.Add(-time.Hour),
|
||||||
|
pairResult: successPairResult(),
|
||||||
|
},
|
||||||
|
node2: {
|
||||||
|
timestamp: testTime.Add(-2 * time.Hour),
|
||||||
|
pairResult: failPairResult(0),
|
||||||
|
},
|
||||||
|
node3: {
|
||||||
|
timestamp: testTime.Add(-3 * time.Hour),
|
||||||
|
pairResult: failPairResult(0),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect the probability for a previously successful channel to
|
||||||
|
// remain high.
|
||||||
|
ctx.assertPairProbability(testTime, node1, 100, prevSuccessProbability)
|
||||||
|
|
||||||
|
// For an untried node, we expected the node probability to be returned.
|
||||||
|
// This is a weighted average of the results above and the a priori
|
||||||
|
// probability: 0.62.
|
||||||
|
expectedNodeProb := (3*aprioriHopProb + 1*prevSuccessProbability) /
|
||||||
|
(3 + 1 + 0.25 + 0.125)
|
||||||
|
|
||||||
|
ctx.assertPairProbability(testTime, untriedNode, 100, expectedNodeProb)
|
||||||
|
|
||||||
|
// For the previously failed connection with node 1, we expect 0.75 *
|
||||||
|
// the node probability = 0.47.
|
||||||
|
ctx.assertPairProbability(testTime, node2, 100, expectedNodeProb*0.75)
|
||||||
|
}
|
@ -26,6 +26,20 @@ type pairResult struct {
|
|||||||
success bool
|
success bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// failPairResult creates a new result struct for a failure.
|
||||||
|
func failPairResult(minPenalizeAmt lnwire.MilliSatoshi) pairResult {
|
||||||
|
return pairResult{
|
||||||
|
minPenalizeAmt: minPenalizeAmt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// successPairResult creates a new result struct for a success.
|
||||||
|
func successPairResult() pairResult {
|
||||||
|
return pairResult{
|
||||||
|
success: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the human-readable representation of a pair result.
|
// String returns the human-readable representation of a pair result.
|
||||||
func (p pairResult) String() string {
|
func (p pairResult) String() string {
|
||||||
if p.success {
|
if p.success {
|
||||||
@ -364,10 +378,30 @@ func (i *interpretedResult) processPaymentOutcomeUnknown(route *route.Route) {
|
|||||||
i.failPairRange(route, 0, n-1)
|
i.failPairRange(route, 0, n-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// failNode marks the node indicated by idx in the route as failed. This
|
// failNode marks the node indicated by idx in the route as failed. It also
|
||||||
// function intentionally panics when the self node is failed.
|
// marks the incoming and outgoing channels of the node as failed. This function
|
||||||
|
// intentionally panics when the self node is failed.
|
||||||
func (i *interpretedResult) failNode(rt *route.Route, idx int) {
|
func (i *interpretedResult) failNode(rt *route.Route, idx int) {
|
||||||
|
// Mark the node as failing.
|
||||||
i.nodeFailure = &rt.Hops[idx-1].PubKeyBytes
|
i.nodeFailure = &rt.Hops[idx-1].PubKeyBytes
|
||||||
|
|
||||||
|
// Mark the incoming connection as failed for the node. We intent to
|
||||||
|
// penalize as much as we can for a node level failure, including future
|
||||||
|
// outgoing traffic for this connection. The pair as it is returned by
|
||||||
|
// getPair is directed towards the failed node. Therefore we first
|
||||||
|
// reverse the pair. We don't want to affect the score of the node
|
||||||
|
// sending towards the failing node.
|
||||||
|
incomingChannelIdx := idx - 1
|
||||||
|
inPair, _ := getPair(rt, incomingChannelIdx)
|
||||||
|
i.pairResults[inPair.Reverse()] = failPairResult(0)
|
||||||
|
|
||||||
|
// If not the ultimate node, mark the outgoing connection as failed for
|
||||||
|
// the node.
|
||||||
|
if idx < len(rt.Hops) {
|
||||||
|
outgoingChannelIdx := idx
|
||||||
|
outPair, _ := getPair(rt, outgoingChannelIdx)
|
||||||
|
i.pairResults[outPair] = failPairResult(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// failPairRange marks the node pairs from node fromIdx to node toIdx as failed
|
// failPairRange marks the node pairs from node fromIdx to node toIdx as failed
|
||||||
@ -387,8 +421,8 @@ func (i *interpretedResult) failPair(
|
|||||||
pair, _ := getPair(rt, idx)
|
pair, _ := getPair(rt, idx)
|
||||||
|
|
||||||
// Report pair in both directions without a minimum penalization amount.
|
// Report pair in both directions without a minimum penalization amount.
|
||||||
i.pairResults[pair] = pairResult{}
|
i.pairResults[pair] = failPairResult(0)
|
||||||
i.pairResults[pair.Reverse()] = pairResult{}
|
i.pairResults[pair.Reverse()] = failPairResult(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// failPairBalance marks a pair as failed with a minimum penalization amount.
|
// failPairBalance marks a pair as failed with a minimum penalization amount.
|
||||||
@ -397,9 +431,7 @@ func (i *interpretedResult) failPairBalance(
|
|||||||
|
|
||||||
pair, amt := getPair(rt, channelIdx)
|
pair, amt := getPair(rt, channelIdx)
|
||||||
|
|
||||||
i.pairResults[pair] = pairResult{
|
i.pairResults[pair] = failPairResult(amt)
|
||||||
minPenalizeAmt: amt,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// successPairRange marks the node pairs from node fromIdx to node toIdx as
|
// successPairRange marks the node pairs from node fromIdx to node toIdx as
|
||||||
@ -410,9 +442,7 @@ func (i *interpretedResult) successPairRange(
|
|||||||
for idx := fromIdx; idx <= toIdx; idx++ {
|
for idx := fromIdx; idx <= toIdx; idx++ {
|
||||||
pair, _ := getPair(rt, idx)
|
pair, _ := getPair(rt, idx)
|
||||||
|
|
||||||
i.pairResults[pair] = pairResult{
|
i.pairResults[pair] = successPairResult()
|
||||||
success: true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,12 +68,8 @@ var resultTestCases = []resultTestCase{
|
|||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]pairResult{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): {
|
getTestPair(0, 1): successPairResult(),
|
||||||
success: true,
|
getTestPair(1, 2): failPairResult(99),
|
||||||
},
|
|
||||||
getTestPair(1, 2): {
|
|
||||||
minPenalizeAmt: 99,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -87,12 +83,12 @@ var resultTestCases = []resultTestCase{
|
|||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]pairResult{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): {},
|
getTestPair(0, 1): failPairResult(0),
|
||||||
getTestPair(1, 0): {},
|
getTestPair(1, 0): failPairResult(0),
|
||||||
getTestPair(1, 2): {},
|
getTestPair(1, 2): failPairResult(0),
|
||||||
getTestPair(2, 1): {},
|
getTestPair(2, 1): failPairResult(0),
|
||||||
getTestPair(2, 3): {},
|
getTestPair(2, 3): failPairResult(0),
|
||||||
getTestPair(3, 2): {},
|
getTestPair(3, 2): failPairResult(0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -107,12 +103,8 @@ var resultTestCases = []resultTestCase{
|
|||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]pairResult{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): {
|
getTestPair(0, 1): successPairResult(),
|
||||||
success: true,
|
getTestPair(1, 2): successPairResult(),
|
||||||
},
|
|
||||||
getTestPair(1, 2): {
|
|
||||||
success: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
finalFailureReason: &reasonIncorrectDetails,
|
finalFailureReason: &reasonIncorrectDetails,
|
||||||
},
|
},
|
||||||
@ -126,9 +118,7 @@ var resultTestCases = []resultTestCase{
|
|||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]pairResult{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): {
|
getTestPair(0, 1): successPairResult(),
|
||||||
success: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -141,12 +131,8 @@ var resultTestCases = []resultTestCase{
|
|||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]pairResult{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): {
|
getTestPair(0, 1): successPairResult(),
|
||||||
success: true,
|
getTestPair(1, 2): successPairResult(),
|
||||||
},
|
|
||||||
getTestPair(1, 2): {
|
|
||||||
success: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -160,6 +146,10 @@ var resultTestCases = []resultTestCase{
|
|||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
nodeFailure: &hops[1],
|
nodeFailure: &hops[1],
|
||||||
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
|
getTestPair(1, 0): failPairResult(0),
|
||||||
|
getTestPair(1, 2): failPairResult(0),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -174,6 +164,9 @@ var resultTestCases = []resultTestCase{
|
|||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
finalFailureReason: &reasonError,
|
finalFailureReason: &reasonError,
|
||||||
nodeFailure: &hops[1],
|
nodeFailure: &hops[1],
|
||||||
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
|
getTestPair(1, 0): failPairResult(0),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,7 @@ func createTestCtxFromGraphInstance(startingHeight uint32, graphInstance *testGr
|
|||||||
mcConfig := &MissionControlConfig{
|
mcConfig := &MissionControlConfig{
|
||||||
PenaltyHalfLife: time.Hour,
|
PenaltyHalfLife: time.Hour,
|
||||||
AprioriHopProbability: 0.9,
|
AprioriHopProbability: 0.9,
|
||||||
|
AprioriWeight: 0.5,
|
||||||
}
|
}
|
||||||
|
|
||||||
mc, err := NewMissionControl(
|
mc, err := NewMissionControl(
|
||||||
|
@ -660,6 +660,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
|||||||
AprioriHopProbability: routingConfig.AprioriHopProbability,
|
AprioriHopProbability: routingConfig.AprioriHopProbability,
|
||||||
PenaltyHalfLife: routingConfig.PenaltyHalfLife,
|
PenaltyHalfLife: routingConfig.PenaltyHalfLife,
|
||||||
MaxMcHistory: routingConfig.MaxMcHistory,
|
MaxMcHistory: routingConfig.MaxMcHistory,
|
||||||
|
AprioriWeight: routingConfig.AprioriWeight,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user