mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
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
|
||||
}
|
||||
|
||||
type displayNodeHistory struct {
|
||||
Pubkey string
|
||||
LastFailTime int64
|
||||
OtherSuccessProb float32
|
||||
}
|
||||
|
||||
type displayPairHistory struct {
|
||||
NodeFrom, NodeTo string
|
||||
LastAttemptSuccessful bool
|
||||
Timestamp int64
|
||||
SuccessProb float32
|
||||
MinPenalizeAmtSat int64
|
||||
}
|
||||
|
||||
displayResp := struct {
|
||||
Nodes []displayNodeHistory
|
||||
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 {
|
||||
displayResp.Pairs = append(
|
||||
displayResp.Pairs,
|
||||
@ -69,7 +50,6 @@ func queryMissionControl(ctx *cli.Context) error {
|
||||
NodeTo: hex.EncodeToString(n.NodeTo),
|
||||
LastAttemptSuccessful: n.LastAttemptSuccessful,
|
||||
Timestamp: n.Timestamp,
|
||||
SuccessProb: n.SuccessProb,
|
||||
MinPenalizeAmtSat: n.MinPenalizeAmtSat,
|
||||
},
|
||||
)
|
||||
|
@ -16,6 +16,15 @@ type RoutingConfig struct {
|
||||
// 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
|
||||
// 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 {
|
||||
defaultRoutingConfig := RoutingConfig{
|
||||
AprioriHopProbability: routing.DefaultAprioriHopProbability,
|
||||
AprioriWeight: routing.DefaultAprioriWeight,
|
||||
MinRouteProbability: routing.DefaultMinRouteProbability,
|
||||
PenaltyHalfLife: routing.DefaultPenaltyHalfLife,
|
||||
AttemptCost: routing.DefaultPaymentAttemptPenalty.
|
||||
@ -61,6 +62,7 @@ func DefaultConfig() *Config {
|
||||
func GetRoutingConfig(cfg *Config) *RoutingConfig {
|
||||
return &RoutingConfig{
|
||||
AprioriHopProbability: cfg.AprioriHopProbability,
|
||||
AprioriWeight: cfg.AprioriWeight,
|
||||
MinRouteProbability: cfg.MinRouteProbability,
|
||||
AttemptCost: cfg.AttemptCost,
|
||||
PenaltyHalfLife: cfg.PenaltyHalfLife,
|
||||
|
@ -18,6 +18,7 @@ func DefaultConfig() *Config {
|
||||
func GetRoutingConfig(cfg *Config) *RoutingConfig {
|
||||
return &RoutingConfig{
|
||||
AprioriHopProbability: routing.DefaultAprioriHopProbability,
|
||||
AprioriWeight: routing.DefaultAprioriWeight,
|
||||
MinRouteProbability: routing.DefaultMinRouteProbability,
|
||||
AttemptCost: routing.DefaultPaymentAttemptPenalty.
|
||||
ToSatoshis(),
|
||||
|
@ -1006,8 +1006,6 @@ var xxx_messageInfo_QueryMissionControlRequest proto.InternalMessageInfo
|
||||
|
||||
/// QueryMissionControlResponse contains mission control state.
|
||||
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.
|
||||
Pairs []*PairHistory `protobuf:"bytes,2,rep,name=pairs,proto3" json:"pairs,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
@ -1040,13 +1038,6 @@ func (m *QueryMissionControlResponse) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_QueryMissionControlResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *QueryMissionControlResponse) GetNodes() []*NodeHistory {
|
||||
if m != nil {
|
||||
return m.Nodes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *QueryMissionControlResponse) GetPairs() []*PairHistory {
|
||||
if m != nil {
|
||||
return m.Pairs
|
||||
@ -1054,67 +1045,6 @@ func (m *QueryMissionControlResponse) GetPairs() []*PairHistory {
|
||||
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.
|
||||
type PairHistory struct {
|
||||
/// 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"`
|
||||
/// 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"`
|
||||
/// 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.
|
||||
LastAttemptSuccessful bool `protobuf:"varint,6,opt,name=last_attempt_successful,proto3" json:"last_attempt_successful,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
@ -1138,7 +1066,7 @@ func (m *PairHistory) Reset() { *m = PairHistory{} }
|
||||
func (m *PairHistory) String() string { return proto.CompactTextString(m) }
|
||||
func (*PairHistory) ProtoMessage() {}
|
||||
func (*PairHistory) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7a0613f69d37b0a5, []int{14}
|
||||
return fileDescriptor_7a0613f69d37b0a5, []int{13}
|
||||
}
|
||||
|
||||
func (m *PairHistory) XXX_Unmarshal(b []byte) error {
|
||||
@ -1187,13 +1115,6 @@ func (m *PairHistory) GetMinPenalizeAmtSat() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *PairHistory) GetSuccessProb() float32 {
|
||||
if m != nil {
|
||||
return m.SuccessProb
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *PairHistory) GetLastAttemptSuccessful() bool {
|
||||
if m != nil {
|
||||
return m.LastAttemptSuccessful
|
||||
@ -1227,7 +1148,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{15}
|
||||
return fileDescriptor_7a0613f69d37b0a5, []int{14}
|
||||
}
|
||||
|
||||
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 (*BuildRouteResponse) ProtoMessage() {}
|
||||
func (*BuildRouteResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7a0613f69d37b0a5, []int{16}
|
||||
return fileDescriptor_7a0613f69d37b0a5, []int{15}
|
||||
}
|
||||
|
||||
func (m *BuildRouteResponse) XXX_Unmarshal(b []byte) error {
|
||||
@ -1334,7 +1255,6 @@ func init() {
|
||||
proto.RegisterType((*ResetMissionControlResponse)(nil), "routerrpc.ResetMissionControlResponse")
|
||||
proto.RegisterType((*QueryMissionControlRequest)(nil), "routerrpc.QueryMissionControlRequest")
|
||||
proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse")
|
||||
proto.RegisterType((*NodeHistory)(nil), "routerrpc.NodeHistory")
|
||||
proto.RegisterType((*PairHistory)(nil), "routerrpc.PairHistory")
|
||||
proto.RegisterType((*BuildRouteRequest)(nil), "routerrpc.BuildRouteRequest")
|
||||
proto.RegisterType((*BuildRouteResponse)(nil), "routerrpc.BuildRouteResponse")
|
||||
@ -1343,125 +1263,120 @@ func init() {
|
||||
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
|
||||
|
||||
var fileDescriptor_7a0613f69d37b0a5 = []byte{
|
||||
// 1875 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0xd1, 0x72, 0x22, 0xb9,
|
||||
0xd5, 0xde, 0x36, 0x60, 0xe0, 0x00, 0x76, 0x5b, 0xf6, 0x78, 0x7a, 0xb0, 0xbd, 0xe3, 0x65, 0xf7,
|
||||
0x9f, 0x75, 0x4d, 0xcd, 0x6f, 0x4f, 0x9c, 0xda, 0xad, 0xa9, 0xbd, 0x48, 0x0a, 0x83, 0x58, 0xf7,
|
||||
0x0c, 0x34, 0x5e, 0x01, 0xb3, 0x3b, 0xc9, 0x85, 0x4a, 0x06, 0xd9, 0x74, 0xb9, 0xe9, 0x66, 0xbb,
|
||||
0x85, 0x33, 0xce, 0x45, 0xaa, 0x52, 0xb9, 0xce, 0x73, 0xe4, 0x26, 0x77, 0x79, 0x91, 0x3c, 0x45,
|
||||
0xf2, 0x04, 0x7b, 0x9f, 0x92, 0xd4, 0x0d, 0x8d, 0x8d, 0x27, 0xb9, 0x72, 0xeb, 0x3b, 0x9f, 0x8e,
|
||||
0xa4, 0x73, 0x8e, 0x3e, 0x1d, 0x0c, 0xbb, 0x61, 0x30, 0x13, 0x3c, 0x0c, 0xa7, 0xc3, 0x13, 0xfd,
|
||||
0x75, 0x3c, 0x0d, 0x03, 0x11, 0xa0, 0xe2, 0x1c, 0xaf, 0x16, 0xc3, 0xe9, 0x50, 0xa3, 0xb5, 0x3f,
|
||||
0x67, 0x01, 0xf5, 0xb8, 0x3f, 0xba, 0x60, 0x77, 0x13, 0xee, 0x0b, 0xc2, 0x7f, 0x9e, 0xf1, 0x48,
|
||||
0x20, 0x04, 0xd9, 0x11, 0x8f, 0x84, 0x65, 0x1c, 0x1a, 0x47, 0x65, 0xa2, 0xbe, 0x91, 0x09, 0x19,
|
||||
0x36, 0x11, 0xd6, 0xda, 0xa1, 0x71, 0x94, 0x21, 0xf2, 0x13, 0x7d, 0x01, 0xe5, 0xa9, 0x9e, 0x47,
|
||||
0xc7, 0x2c, 0x1a, 0x5b, 0x19, 0xc5, 0x2e, 0xc5, 0xd8, 0x39, 0x8b, 0xc6, 0xe8, 0x08, 0xcc, 0x2b,
|
||||
0xd7, 0x67, 0x1e, 0x1d, 0x7a, 0xe2, 0x96, 0x8e, 0xb8, 0x27, 0x98, 0x95, 0x3d, 0x34, 0x8e, 0x72,
|
||||
0x64, 0x43, 0xe1, 0x0d, 0x4f, 0xdc, 0x36, 0x25, 0x8a, 0xbe, 0x86, 0xcd, 0xc4, 0x59, 0xa8, 0x77,
|
||||
0x61, 0xe5, 0x0e, 0x8d, 0xa3, 0x22, 0xd9, 0x98, 0x2e, 0xef, 0xed, 0x6b, 0xd8, 0x14, 0xee, 0x84,
|
||||
0x07, 0x33, 0x41, 0x23, 0x3e, 0x0c, 0xfc, 0x51, 0x64, 0xad, 0x6b, 0x8f, 0x31, 0xdc, 0xd3, 0x28,
|
||||
0xaa, 0x41, 0xe5, 0x8a, 0x73, 0xea, 0xb9, 0x13, 0x57, 0xd0, 0x88, 0x09, 0x2b, 0xaf, 0xb6, 0x5e,
|
||||
0xba, 0xe2, 0xbc, 0x2d, 0xb1, 0x1e, 0x13, 0xe8, 0x15, 0x98, 0xc1, 0x4c, 0x5c, 0x07, 0xae, 0x7f,
|
||||
0x4d, 0x87, 0x63, 0xe6, 0x53, 0x77, 0x64, 0x15, 0x0e, 0x8d, 0xa3, 0xec, 0xd9, 0xda, 0x6b, 0x83,
|
||||
0x6c, 0x24, 0xb6, 0xc6, 0x98, 0xf9, 0xf6, 0x08, 0x1d, 0x00, 0xa8, 0x73, 0x28, 0x97, 0x56, 0x51,
|
||||
0xad, 0x5a, 0x94, 0x88, 0xf2, 0x87, 0x4e, 0xa1, 0xa4, 0x82, 0x4c, 0xc7, 0xae, 0x2f, 0x22, 0x0b,
|
||||
0x0e, 0x33, 0x47, 0xa5, 0x53, 0xf3, 0xd8, 0xf3, 0x65, 0xbc, 0x89, 0xb4, 0x9c, 0xbb, 0xbe, 0x20,
|
||||
0x69, 0x12, 0xc2, 0x50, 0x90, 0xd1, 0xa5, 0xc2, 0xbb, 0xb5, 0x4a, 0x6a, 0xc2, 0xcb, 0xe3, 0x79,
|
||||
0xa6, 0x8e, 0x1f, 0xa6, 0xe6, 0xb8, 0xc9, 0x23, 0xd1, 0xf7, 0x6e, 0xb1, 0x2f, 0xc2, 0x3b, 0x92,
|
||||
0x1f, 0xe9, 0x51, 0xf5, 0x3b, 0x28, 0xa7, 0x0d, 0x32, 0x59, 0x37, 0xfc, 0x4e, 0xe5, 0x2f, 0x4b,
|
||||
0xe4, 0x27, 0xda, 0x81, 0xdc, 0x2d, 0xf3, 0x66, 0x5c, 0x25, 0xb0, 0x4c, 0xf4, 0xe0, 0xbb, 0xb5,
|
||||
0x37, 0x46, 0xed, 0x0d, 0x6c, 0xf7, 0x43, 0x36, 0xbc, 0xb9, 0x57, 0x03, 0xf7, 0xb3, 0x6b, 0x3c,
|
||||
0xc8, 0x6e, 0xed, 0x4f, 0x50, 0x89, 0x27, 0xf5, 0x04, 0x13, 0xb3, 0x08, 0xfd, 0x3f, 0xe4, 0x22,
|
||||
0xc1, 0x04, 0x57, 0xe4, 0x8d, 0xd3, 0xa7, 0xa9, 0xa3, 0xa4, 0x88, 0x9c, 0x68, 0x16, 0xaa, 0x42,
|
||||
0x61, 0x1a, 0x72, 0x77, 0xc2, 0xae, 0x93, 0x6d, 0xcd, 0xc7, 0xa8, 0x06, 0x39, 0x35, 0x59, 0x55,
|
||||
0x55, 0xe9, 0xb4, 0x9c, 0x0e, 0x23, 0xd1, 0xa6, 0xda, 0x6f, 0x60, 0x53, 0x8d, 0x5b, 0x9c, 0x7f,
|
||||
0xaa, 0x72, 0x9f, 0x42, 0x9e, 0x4d, 0x74, 0x09, 0xe8, 0xea, 0x5d, 0x67, 0x13, 0x99, 0xfd, 0xda,
|
||||
0x08, 0xcc, 0xc5, 0xfc, 0x68, 0x1a, 0xf8, 0x11, 0x97, 0x15, 0x2b, 0x9d, 0xcb, 0x82, 0x90, 0xd5,
|
||||
0x33, 0x91, 0xb3, 0x0c, 0x35, 0x6b, 0x23, 0xc6, 0x5b, 0x9c, 0x77, 0x22, 0x26, 0xd0, 0x0b, 0x5d,
|
||||
0x88, 0xd4, 0x0b, 0x86, 0x37, 0xb2, 0xb4, 0xd9, 0x5d, 0xec, 0xbe, 0x22, 0xe1, 0x76, 0x30, 0xbc,
|
||||
0x69, 0x4a, 0xb0, 0xf6, 0x7b, 0x7d, 0xc5, 0xfa, 0x81, 0xde, 0xfb, 0xff, 0x1c, 0xde, 0x45, 0x08,
|
||||
0xd6, 0x1e, 0x0f, 0x01, 0x85, 0xed, 0x25, 0xe7, 0xf1, 0x29, 0xd2, 0x91, 0x35, 0xee, 0x45, 0xf6,
|
||||
0x15, 0xe4, 0xaf, 0x98, 0xeb, 0xcd, 0xc2, 0xc4, 0x31, 0x4a, 0xa5, 0xa9, 0xa5, 0x2d, 0x24, 0xa1,
|
||||
0xd4, 0x7e, 0xc9, 0x43, 0x3e, 0x06, 0xd1, 0x29, 0x64, 0x87, 0xc1, 0x28, 0xc9, 0xee, 0xe7, 0x0f,
|
||||
0xa7, 0x25, 0x7f, 0x1b, 0xc1, 0x88, 0x13, 0xc5, 0x45, 0xbf, 0x85, 0x0d, 0x79, 0xb1, 0x7c, 0xee,
|
||||
0xd1, 0xd9, 0x74, 0xc4, 0xe6, 0x09, 0xb5, 0x52, 0xb3, 0x1b, 0x9a, 0x30, 0x50, 0x76, 0x52, 0x19,
|
||||
0xa6, 0x87, 0x68, 0x0f, 0x8a, 0x63, 0xe1, 0x0d, 0x75, 0x26, 0xb2, 0xaa, 0xa0, 0x0b, 0x12, 0x50,
|
||||
0x39, 0xa8, 0x41, 0x25, 0xf0, 0xdd, 0xc0, 0xa7, 0xd1, 0x98, 0xd1, 0xd3, 0x6f, 0xbe, 0x55, 0x9a,
|
||||
0x51, 0x26, 0x25, 0x05, 0xf6, 0xc6, 0xec, 0xf4, 0x9b, 0x6f, 0xd1, 0x73, 0x28, 0xa9, 0x5b, 0xcb,
|
||||
0x3f, 0x4e, 0xdd, 0xf0, 0x4e, 0x89, 0x45, 0x85, 0xa8, 0x8b, 0x8c, 0x15, 0x22, 0xaf, 0xc6, 0x95,
|
||||
0xc7, 0xae, 0x23, 0x25, 0x10, 0x15, 0xa2, 0x07, 0xe8, 0x35, 0xec, 0xc4, 0x31, 0xa0, 0x51, 0x30,
|
||||
0x0b, 0x87, 0x9c, 0xba, 0xfe, 0x88, 0x7f, 0x54, 0xf2, 0x50, 0x21, 0x28, 0xb6, 0xf5, 0x94, 0xc9,
|
||||
0x96, 0x16, 0xb4, 0x0b, 0xeb, 0x63, 0xee, 0x5e, 0x8f, 0xb5, 0x34, 0x54, 0x48, 0x3c, 0xaa, 0xfd,
|
||||
0x3d, 0x07, 0xa5, 0x54, 0x60, 0x50, 0x19, 0x0a, 0x04, 0xf7, 0x30, 0x79, 0x8f, 0x9b, 0xe6, 0x67,
|
||||
0xe8, 0x08, 0xbe, 0xb2, 0x9d, 0x46, 0x97, 0x10, 0xdc, 0xe8, 0xd3, 0x2e, 0xa1, 0x03, 0xe7, 0x9d,
|
||||
0xd3, 0xfd, 0xd1, 0xa1, 0x17, 0xf5, 0x0f, 0x1d, 0xec, 0xf4, 0x69, 0x13, 0xf7, 0xeb, 0x76, 0xbb,
|
||||
0x67, 0x1a, 0x68, 0x1f, 0xac, 0x05, 0x33, 0x31, 0xd7, 0x3b, 0xdd, 0x81, 0xd3, 0x37, 0xd7, 0xd0,
|
||||
0x73, 0xd8, 0x6b, 0xd9, 0x4e, 0xbd, 0x4d, 0x17, 0x9c, 0x46, 0xbb, 0xff, 0x9e, 0xe2, 0x9f, 0x2e,
|
||||
0x6c, 0xf2, 0xc1, 0xcc, 0xac, 0x22, 0x9c, 0xf7, 0xdb, 0x8d, 0xc4, 0x43, 0x16, 0x3d, 0x83, 0x27,
|
||||
0x9a, 0xa0, 0xa7, 0xd0, 0x7e, 0xb7, 0x4b, 0x7b, 0xdd, 0xae, 0x63, 0xe6, 0xd0, 0x16, 0x54, 0x6c,
|
||||
0xe7, 0x7d, 0xbd, 0x6d, 0x37, 0x29, 0xc1, 0xf5, 0x76, 0xc7, 0x5c, 0x47, 0xdb, 0xb0, 0x79, 0x9f,
|
||||
0x97, 0x97, 0x2e, 0x12, 0x5e, 0xd7, 0xb1, 0xbb, 0x0e, 0x7d, 0x8f, 0x49, 0xcf, 0xee, 0x3a, 0x66,
|
||||
0x01, 0xed, 0x02, 0x5a, 0x36, 0x9d, 0x77, 0xea, 0x0d, 0xb3, 0x88, 0x9e, 0xc0, 0xd6, 0x32, 0xfe,
|
||||
0x0e, 0x7f, 0x30, 0x01, 0x59, 0xb0, 0xa3, 0x37, 0x46, 0xcf, 0x70, 0xbb, 0xfb, 0x23, 0xed, 0xd8,
|
||||
0x8e, 0xdd, 0x19, 0x74, 0xcc, 0x12, 0xda, 0x01, 0xb3, 0x85, 0x31, 0xb5, 0x9d, 0xde, 0xa0, 0xd5,
|
||||
0xb2, 0x1b, 0x36, 0x76, 0xfa, 0x66, 0x59, 0xaf, 0xbc, 0xea, 0xe0, 0x15, 0x39, 0xa1, 0x71, 0x5e,
|
||||
0x77, 0x1c, 0xdc, 0xa6, 0x4d, 0xbb, 0x57, 0x3f, 0x6b, 0xe3, 0xa6, 0xb9, 0x81, 0x0e, 0xe0, 0x59,
|
||||
0x1f, 0x77, 0x2e, 0xba, 0xa4, 0x4e, 0x3e, 0xd0, 0xc4, 0xde, 0xaa, 0xdb, 0xed, 0x01, 0xc1, 0xe6,
|
||||
0x26, 0xfa, 0x02, 0x0e, 0x08, 0xfe, 0x61, 0x60, 0x13, 0xdc, 0xa4, 0x4e, 0xb7, 0x89, 0x69, 0x0b,
|
||||
0xd7, 0xfb, 0x03, 0x82, 0x69, 0xc7, 0xee, 0xf5, 0x6c, 0xe7, 0x7b, 0xd3, 0x44, 0x5f, 0xc1, 0xe1,
|
||||
0x9c, 0x32, 0x77, 0x70, 0x8f, 0xb5, 0x25, 0xcf, 0x97, 0xa4, 0xd4, 0xc1, 0x3f, 0xf5, 0xe9, 0x05,
|
||||
0xc6, 0xc4, 0x44, 0xa8, 0x0a, 0xbb, 0x8b, 0xe5, 0xf5, 0x02, 0xf1, 0xda, 0xdb, 0xd2, 0x76, 0x81,
|
||||
0x49, 0xa7, 0xee, 0xc8, 0x04, 0x2f, 0xd9, 0x76, 0xe4, 0xb6, 0x17, 0xb6, 0xfb, 0xdb, 0x7e, 0x82,
|
||||
0x10, 0x6c, 0xa4, 0xb2, 0xd2, 0xaa, 0x13, 0x73, 0x17, 0xed, 0xc0, 0x66, 0xb2, 0x83, 0x84, 0xf8,
|
||||
0xaf, 0x3c, 0x7a, 0x0a, 0x68, 0xe0, 0x10, 0x5c, 0x6f, 0xca, 0x80, 0xcc, 0x0d, 0xff, 0xce, 0xbf,
|
||||
0xcd, 0x16, 0xd6, 0xcc, 0x4c, 0xed, 0x1f, 0x19, 0xa8, 0x2c, 0xdd, 0x4b, 0xb4, 0x0f, 0xc5, 0xc8,
|
||||
0xbd, 0xf6, 0x99, 0x90, 0xca, 0xa1, 0x45, 0x65, 0x01, 0xa8, 0xb7, 0x71, 0xcc, 0x5c, 0x5f, 0xab,
|
||||
0x99, 0x56, 0xf3, 0xa2, 0x42, 0x94, 0x96, 0xed, 0x41, 0x3e, 0x79, 0x5f, 0x33, 0xf3, 0xf7, 0x75,
|
||||
0x7d, 0xa8, 0xdf, 0xd5, 0x7d, 0x28, 0x4a, 0xc9, 0x8c, 0x04, 0x9b, 0x4c, 0xd5, 0x15, 0xaf, 0x90,
|
||||
0x05, 0x80, 0xbe, 0x84, 0xca, 0x84, 0x47, 0x11, 0xbb, 0xe6, 0x54, 0x5f, 0x53, 0x50, 0x8c, 0x72,
|
||||
0x0c, 0xb6, 0xd4, 0x6d, 0xfd, 0x12, 0x12, 0xd9, 0x88, 0x49, 0x39, 0x4d, 0x8a, 0x41, 0x4d, 0xba,
|
||||
0xaf, 0xd8, 0x82, 0xc5, 0x6a, 0x90, 0x56, 0x6c, 0xc1, 0xd0, 0x4b, 0xd8, 0xd2, 0x92, 0xe3, 0xfa,
|
||||
0xee, 0x64, 0x36, 0xd1, 0xd2, 0x93, 0x57, 0xd2, 0xb3, 0xa9, 0xa4, 0x47, 0xe3, 0x4a, 0x81, 0x9e,
|
||||
0x41, 0xe1, 0x92, 0x45, 0x5c, 0x3e, 0x16, 0xb1, 0x34, 0xe4, 0xe5, 0xb8, 0xc5, 0xb9, 0x34, 0xc9,
|
||||
0x27, 0x24, 0x94, 0xa2, 0xa7, 0x15, 0x21, 0x7f, 0xc5, 0x39, 0x91, 0xb1, 0x9c, 0xaf, 0xc0, 0x3e,
|
||||
0x2e, 0x56, 0x28, 0xa5, 0x56, 0xd0, 0xb8, 0x5a, 0xe1, 0x25, 0x6c, 0xf1, 0x8f, 0x22, 0x64, 0x34,
|
||||
0x98, 0xb2, 0x9f, 0x67, 0x9c, 0x8e, 0x98, 0x60, 0x56, 0x59, 0x05, 0x78, 0x53, 0x19, 0xba, 0x0a,
|
||||
0x6f, 0x32, 0xc1, 0x6a, 0xfb, 0x50, 0x25, 0x3c, 0xe2, 0xa2, 0xe3, 0x46, 0x91, 0x1b, 0xf8, 0x8d,
|
||||
0xc0, 0x17, 0x61, 0xe0, 0xc5, 0x6f, 0x4e, 0xed, 0x00, 0xf6, 0x56, 0x5a, 0xf5, 0xa3, 0x21, 0x27,
|
||||
0xff, 0x30, 0xe3, 0xe1, 0xdd, 0xea, 0xc9, 0x77, 0xb0, 0xb7, 0xd2, 0x1a, 0xbf, 0x38, 0xaf, 0x20,
|
||||
0xe7, 0x07, 0x23, 0x1e, 0x59, 0x86, 0xea, 0x62, 0x76, 0x53, 0xf2, 0xee, 0x04, 0x23, 0x7e, 0xee,
|
||||
0x46, 0x22, 0x08, 0xef, 0x88, 0x26, 0x49, 0xf6, 0x94, 0xb9, 0x61, 0x64, 0xad, 0x3d, 0x60, 0x5f,
|
||||
0x30, 0x37, 0x9c, 0xb3, 0x15, 0xa9, 0xf6, 0x17, 0x03, 0x4a, 0x29, 0x27, 0x52, 0x68, 0xa7, 0xb3,
|
||||
0xcb, 0xa4, 0xc1, 0x29, 0x93, 0x78, 0x84, 0x5e, 0xc0, 0x86, 0xc7, 0x22, 0x41, 0xa5, 0x36, 0x53,
|
||||
0x99, 0xd2, 0xf8, 0x41, 0xbe, 0x87, 0xa2, 0x63, 0x40, 0x81, 0x18, 0xf3, 0x90, 0x46, 0xb3, 0xe1,
|
||||
0x90, 0x47, 0x11, 0x9d, 0x86, 0xc1, 0xa5, 0xaa, 0xcb, 0x35, 0xb2, 0xc2, 0xf2, 0x36, 0x5b, 0xc8,
|
||||
0x9a, 0xb9, 0xda, 0x2f, 0x06, 0x94, 0x52, 0x9b, 0x93, 0x55, 0x2b, 0x0f, 0x43, 0xaf, 0xc2, 0x60,
|
||||
0x92, 0xdc, 0x87, 0x39, 0x80, 0x2c, 0xc8, 0xab, 0x81, 0x08, 0xe2, 0xcb, 0x90, 0x0c, 0x97, 0xab,
|
||||
0x3d, 0xa3, 0x36, 0x98, 0xaa, 0xf6, 0x53, 0xd8, 0x99, 0xb8, 0x3e, 0x9d, 0x72, 0x9f, 0x79, 0xee,
|
||||
0x1f, 0x39, 0x4d, 0x3a, 0x97, 0xac, 0x22, 0xae, 0xb4, 0xa1, 0x1a, 0x94, 0x97, 0x4e, 0x92, 0x53,
|
||||
0x27, 0x59, 0xc2, 0xd0, 0x1b, 0x78, 0xaa, 0xa2, 0xc0, 0x84, 0xe0, 0x93, 0xa9, 0x48, 0x0e, 0x78,
|
||||
0x35, 0xf3, 0xd4, 0x1d, 0x28, 0x90, 0xc7, 0xcc, 0xb5, 0xbf, 0x19, 0xb0, 0x75, 0x36, 0x73, 0xbd,
|
||||
0xd1, 0x52, 0xff, 0xf2, 0x0c, 0x0a, 0x72, 0xf9, 0x54, 0x7f, 0x24, 0x9b, 0x2c, 0x55, 0xb0, 0xab,
|
||||
0x9a, 0xfe, 0xb5, 0x95, 0x4d, 0xff, 0xaa, 0xf6, 0x3b, 0xf3, 0x68, 0xfb, 0xfd, 0x1c, 0x4a, 0xe3,
|
||||
0x60, 0x4a, 0x75, 0xb2, 0x23, 0x2b, 0x7b, 0x98, 0x39, 0x2a, 0x13, 0x18, 0x07, 0xd3, 0x0b, 0x8d,
|
||||
0xd4, 0xde, 0x00, 0x4a, 0x6f, 0x34, 0xae, 0xcc, 0x79, 0x1b, 0x65, 0x3c, 0xda, 0x46, 0xbd, 0xfc,
|
||||
0xab, 0x01, 0xe5, 0x74, 0x87, 0x8a, 0x2a, 0x50, 0xb4, 0x1d, 0xda, 0x6a, 0xdb, 0xdf, 0x9f, 0xf7,
|
||||
0xcd, 0xcf, 0xe4, 0xb0, 0x37, 0x68, 0x34, 0x30, 0x6e, 0xe2, 0xa6, 0x69, 0x48, 0x95, 0x95, 0x82,
|
||||
0x89, 0x9b, 0xb4, 0x6f, 0x77, 0x70, 0x77, 0x20, 0xdf, 0xdf, 0x6d, 0xd8, 0x8c, 0x31, 0xa7, 0x4b,
|
||||
0x49, 0x77, 0xd0, 0xc7, 0x66, 0x06, 0x99, 0x50, 0x8e, 0x41, 0x4c, 0x48, 0x97, 0x98, 0x59, 0xf9,
|
||||
0x68, 0xc4, 0xc8, 0xc3, 0xb7, 0x3c, 0x79, 0xea, 0x73, 0xa7, 0xff, 0xcc, 0xc2, 0xba, 0xda, 0x60,
|
||||
0x88, 0xce, 0xa1, 0x94, 0xfa, 0x19, 0x80, 0x0e, 0x3e, 0xf9, 0xf3, 0xa0, 0x6a, 0xad, 0x6e, 0xb9,
|
||||
0x67, 0xd1, 0x6b, 0x03, 0xbd, 0x85, 0x72, 0xba, 0xd1, 0x47, 0xe9, 0x06, 0x6e, 0xc5, 0x2f, 0x80,
|
||||
0x4f, 0xfa, 0x7a, 0x07, 0x26, 0x8e, 0x84, 0x3b, 0x91, 0x0d, 0x5b, 0xdc, 0x42, 0xa3, 0x6a, 0x8a,
|
||||
0x7f, 0xaf, 0x2f, 0xaf, 0xee, 0xad, 0xb4, 0xc5, 0x19, 0x6a, 0xeb, 0x23, 0xc6, 0x4d, 0xec, 0x83,
|
||||
0x23, 0x2e, 0x77, 0xce, 0xd5, 0xcf, 0x1f, 0x33, 0xc7, 0xde, 0x46, 0xb0, 0xbd, 0x42, 0xe5, 0xd0,
|
||||
0xff, 0xa5, 0x77, 0xf0, 0xa8, 0x46, 0x56, 0x5f, 0xfc, 0x37, 0xda, 0x62, 0x95, 0x15, 0x72, 0xb8,
|
||||
0xb4, 0xca, 0xe3, 0x62, 0xba, 0xb4, 0xca, 0xa7, 0x54, 0xd5, 0x06, 0x58, 0x54, 0x34, 0xda, 0x4f,
|
||||
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,
|
||||
// 1805 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x73, 0x1a, 0xc9,
|
||||
0x15, 0xdf, 0x11, 0x20, 0xe0, 0x01, 0xd2, 0xa8, 0x25, 0xcb, 0x63, 0x64, 0xad, 0xb5, 0xec, 0x66,
|
||||
0x57, 0xe5, 0x72, 0x24, 0x47, 0xa9, 0xdd, 0x72, 0xed, 0x21, 0x29, 0x0c, 0xcd, 0x6a, 0x6c, 0x98,
|
||||
0x91, 0x1b, 0xf0, 0xae, 0x93, 0x43, 0x57, 0x1b, 0x5a, 0x62, 0x4a, 0xc3, 0x0c, 0x3b, 0xd3, 0x38,
|
||||
0x56, 0x0e, 0xa9, 0xca, 0x07, 0xc8, 0xe7, 0xc8, 0x25, 0xb7, 0x7c, 0x91, 0xdc, 0xf2, 0x0d, 0x92,
|
||||
0x4f, 0x90, 0x7b, 0xaa, 0xbb, 0x67, 0x60, 0x90, 0x90, 0x93, 0x93, 0xe8, 0xdf, 0xfb, 0xd7, 0xf3,
|
||||
0xde, 0xeb, 0xdf, 0x7b, 0x82, 0xfd, 0x28, 0x9c, 0x0b, 0x1e, 0x45, 0xb3, 0xd1, 0xa9, 0xfe, 0x75,
|
||||
0x32, 0x8b, 0x42, 0x11, 0xa2, 0xf2, 0x02, 0xaf, 0x97, 0xa3, 0xd9, 0x48, 0xa3, 0x8d, 0x3f, 0xe7,
|
||||
0x01, 0xf5, 0x79, 0x30, 0xbe, 0x60, 0x37, 0x53, 0x1e, 0x08, 0xc2, 0x7f, 0x9e, 0xf3, 0x58, 0x20,
|
||||
0x04, 0xf9, 0x31, 0x8f, 0x85, 0x65, 0x1c, 0x19, 0xc7, 0x55, 0xa2, 0x7e, 0x23, 0x13, 0x72, 0x6c,
|
||||
0x2a, 0xac, 0x8d, 0x23, 0xe3, 0x38, 0x47, 0xe4, 0x4f, 0xf4, 0x05, 0x54, 0x67, 0xda, 0x8e, 0x4e,
|
||||
0x58, 0x3c, 0xb1, 0x72, 0x4a, 0xbb, 0x92, 0x60, 0xe7, 0x2c, 0x9e, 0xa0, 0x63, 0x30, 0x2f, 0xbd,
|
||||
0x80, 0xf9, 0x74, 0xe4, 0x8b, 0x0f, 0x74, 0xcc, 0x7d, 0xc1, 0xac, 0xfc, 0x91, 0x71, 0x5c, 0x20,
|
||||
0x5b, 0x0a, 0x6f, 0xf9, 0xe2, 0x43, 0x5b, 0xa2, 0xe8, 0x1b, 0xd8, 0x4e, 0x9d, 0x45, 0xfa, 0x16,
|
||||
0x56, 0xe1, 0xc8, 0x38, 0x2e, 0x93, 0xad, 0xd9, 0xea, 0xdd, 0xbe, 0x81, 0x6d, 0xe1, 0x4d, 0x79,
|
||||
0x38, 0x17, 0x34, 0xe6, 0xa3, 0x30, 0x18, 0xc7, 0xd6, 0xa6, 0xf6, 0x98, 0xc0, 0x7d, 0x8d, 0xa2,
|
||||
0x06, 0xd4, 0x2e, 0x39, 0xa7, 0xbe, 0x37, 0xf5, 0x04, 0x8d, 0x99, 0xb0, 0x8a, 0xea, 0xea, 0x95,
|
||||
0x4b, 0xce, 0xbb, 0x12, 0xeb, 0x33, 0x81, 0x9e, 0x81, 0x19, 0xce, 0xc5, 0x55, 0xe8, 0x05, 0x57,
|
||||
0x74, 0x34, 0x61, 0x01, 0xf5, 0xc6, 0x56, 0xe9, 0xc8, 0x38, 0xce, 0xbf, 0xdc, 0x78, 0x6e, 0x90,
|
||||
0xad, 0x54, 0xd6, 0x9a, 0xb0, 0xc0, 0x1e, 0xa3, 0x43, 0x00, 0xf5, 0x1d, 0xca, 0xa5, 0x55, 0x56,
|
||||
0x51, 0xcb, 0x12, 0x51, 0xfe, 0xd0, 0x19, 0x54, 0x54, 0x92, 0xe9, 0xc4, 0x0b, 0x44, 0x6c, 0xc1,
|
||||
0x51, 0xee, 0xb8, 0x72, 0x66, 0x9e, 0xf8, 0x81, 0xcc, 0x37, 0x91, 0x92, 0x73, 0x2f, 0x10, 0x24,
|
||||
0xab, 0x84, 0x30, 0x94, 0x64, 0x76, 0xa9, 0xf0, 0x3f, 0x58, 0x15, 0x65, 0xf0, 0xf4, 0x64, 0x51,
|
||||
0xa9, 0x93, 0xbb, 0xa5, 0x39, 0x69, 0xf3, 0x58, 0x0c, 0xfc, 0x0f, 0x38, 0x10, 0xd1, 0x0d, 0x29,
|
||||
0x8e, 0xf5, 0xa9, 0xfe, 0x3d, 0x54, 0xb3, 0x02, 0x59, 0xac, 0x6b, 0x7e, 0xa3, 0xea, 0x97, 0x27,
|
||||
0xf2, 0x27, 0xda, 0x83, 0xc2, 0x07, 0xe6, 0xcf, 0xb9, 0x2a, 0x60, 0x95, 0xe8, 0xc3, 0xf7, 0x1b,
|
||||
0x2f, 0x8c, 0xc6, 0x0b, 0xd8, 0x1d, 0x44, 0x6c, 0x74, 0x7d, 0xab, 0x07, 0x6e, 0x57, 0xd7, 0xb8,
|
||||
0x53, 0xdd, 0xc6, 0x9f, 0xa0, 0x96, 0x18, 0xf5, 0x05, 0x13, 0xf3, 0x18, 0xfd, 0x12, 0x0a, 0xb1,
|
||||
0x60, 0x82, 0x2b, 0xe5, 0xad, 0xb3, 0x87, 0x99, 0x4f, 0xc9, 0x28, 0x72, 0xa2, 0xb5, 0x50, 0x1d,
|
||||
0x4a, 0xb3, 0x88, 0x7b, 0x53, 0x76, 0x95, 0x5e, 0x6b, 0x71, 0x46, 0x0d, 0x28, 0x28, 0x63, 0xd5,
|
||||
0x55, 0x95, 0xb3, 0x6a, 0x36, 0x8d, 0x44, 0x8b, 0x1a, 0xbf, 0x81, 0x6d, 0x75, 0xee, 0x70, 0xfe,
|
||||
0xa9, 0xce, 0x7d, 0x08, 0x45, 0x36, 0xd5, 0x2d, 0xa0, 0xbb, 0x77, 0x93, 0x4d, 0x65, 0xf5, 0x1b,
|
||||
0x63, 0x30, 0x97, 0xf6, 0xf1, 0x2c, 0x0c, 0x62, 0x2e, 0x3b, 0x56, 0x3a, 0x97, 0x0d, 0x21, 0xbb,
|
||||
0x67, 0x2a, 0xad, 0x0c, 0x65, 0xb5, 0x95, 0xe0, 0x1d, 0xce, 0x7b, 0x31, 0x13, 0xe8, 0x6b, 0xdd,
|
||||
0x88, 0xd4, 0x0f, 0x47, 0xd7, 0xb2, 0xb5, 0xd9, 0x4d, 0xe2, 0xbe, 0x26, 0xe1, 0x6e, 0x38, 0xba,
|
||||
0x6e, 0x4b, 0xb0, 0xf1, 0x7b, 0xfd, 0xc4, 0x06, 0xa1, 0xbe, 0xfb, 0xff, 0x9d, 0xde, 0x65, 0x0a,
|
||||
0x36, 0xee, 0x4f, 0x01, 0x85, 0xdd, 0x15, 0xe7, 0xc9, 0x57, 0x64, 0x33, 0x6b, 0xdc, 0xca, 0xec,
|
||||
0x33, 0x28, 0x5e, 0x32, 0xcf, 0x9f, 0x47, 0xa9, 0x63, 0x94, 0x29, 0x53, 0x47, 0x4b, 0x48, 0xaa,
|
||||
0xd2, 0xf8, 0x4f, 0x11, 0x8a, 0x09, 0x88, 0xce, 0x20, 0x3f, 0x0a, 0xc7, 0x69, 0x75, 0x3f, 0xbf,
|
||||
0x6b, 0x96, 0xfe, 0x6d, 0x85, 0x63, 0x4e, 0x94, 0x2e, 0xfa, 0x2d, 0x6c, 0xc9, 0x87, 0x15, 0x70,
|
||||
0x9f, 0xce, 0x67, 0x63, 0xb6, 0x28, 0xa8, 0x95, 0xb1, 0x6e, 0x69, 0x85, 0xa1, 0x92, 0x93, 0xda,
|
||||
0x28, 0x7b, 0x44, 0x07, 0x50, 0x9e, 0x08, 0x7f, 0xa4, 0x2b, 0x91, 0x57, 0x0d, 0x5d, 0x92, 0x80,
|
||||
0xaa, 0x41, 0x03, 0x6a, 0x61, 0xe0, 0x85, 0x01, 0x8d, 0x27, 0x8c, 0x9e, 0x7d, 0xfb, 0x9d, 0xe2,
|
||||
0x8c, 0x2a, 0xa9, 0x28, 0xb0, 0x3f, 0x61, 0x67, 0xdf, 0x7e, 0x87, 0x9e, 0x40, 0x45, 0xbd, 0x5a,
|
||||
0xfe, 0x71, 0xe6, 0x45, 0x37, 0x8a, 0x2c, 0x6a, 0x44, 0x3d, 0x64, 0xac, 0x10, 0xf9, 0x34, 0x2e,
|
||||
0x7d, 0x76, 0x15, 0x2b, 0x82, 0xa8, 0x11, 0x7d, 0x40, 0xcf, 0x61, 0x2f, 0xc9, 0x01, 0x8d, 0xc3,
|
||||
0x79, 0x34, 0xe2, 0xd4, 0x0b, 0xc6, 0xfc, 0xa3, 0xa2, 0x87, 0x1a, 0x41, 0x89, 0xac, 0xaf, 0x44,
|
||||
0xb6, 0x94, 0xa0, 0x7d, 0xd8, 0x9c, 0x70, 0xef, 0x6a, 0xa2, 0xa9, 0xa1, 0x46, 0x92, 0x53, 0xe3,
|
||||
0x6f, 0x05, 0xa8, 0x64, 0x12, 0x83, 0xaa, 0x50, 0x22, 0xb8, 0x8f, 0xc9, 0x5b, 0xdc, 0x36, 0x3f,
|
||||
0x43, 0xc7, 0xf0, 0x95, 0xed, 0xb4, 0x5c, 0x42, 0x70, 0x6b, 0x40, 0x5d, 0x42, 0x87, 0xce, 0x6b,
|
||||
0xc7, 0xfd, 0xd1, 0xa1, 0x17, 0xcd, 0x77, 0x3d, 0xec, 0x0c, 0x68, 0x1b, 0x0f, 0x9a, 0x76, 0xb7,
|
||||
0x6f, 0x1a, 0xe8, 0x31, 0x58, 0x4b, 0xcd, 0x54, 0xdc, 0xec, 0xb9, 0x43, 0x67, 0x60, 0x6e, 0xa0,
|
||||
0x27, 0x70, 0xd0, 0xb1, 0x9d, 0x66, 0x97, 0x2e, 0x75, 0x5a, 0xdd, 0xc1, 0x5b, 0x8a, 0x7f, 0xba,
|
||||
0xb0, 0xc9, 0x3b, 0x33, 0xb7, 0x4e, 0xe1, 0x7c, 0xd0, 0x6d, 0xa5, 0x1e, 0xf2, 0xe8, 0x11, 0x3c,
|
||||
0xd0, 0x0a, 0xda, 0x84, 0x0e, 0x5c, 0x97, 0xf6, 0x5d, 0xd7, 0x31, 0x0b, 0x68, 0x07, 0x6a, 0xb6,
|
||||
0xf3, 0xb6, 0xd9, 0xb5, 0xdb, 0x94, 0xe0, 0x66, 0xb7, 0x67, 0x6e, 0xa2, 0x5d, 0xd8, 0xbe, 0xad,
|
||||
0x57, 0x94, 0x2e, 0x52, 0x3d, 0xd7, 0xb1, 0x5d, 0x87, 0xbe, 0xc5, 0xa4, 0x6f, 0xbb, 0x8e, 0x59,
|
||||
0x42, 0xfb, 0x80, 0x56, 0x45, 0xe7, 0xbd, 0x66, 0xcb, 0x2c, 0xa3, 0x07, 0xb0, 0xb3, 0x8a, 0xbf,
|
||||
0xc6, 0xef, 0x4c, 0x40, 0x16, 0xec, 0xe9, 0x8b, 0xd1, 0x97, 0xb8, 0xeb, 0xfe, 0x48, 0x7b, 0xb6,
|
||||
0x63, 0xf7, 0x86, 0x3d, 0xb3, 0x82, 0xf6, 0xc0, 0xec, 0x60, 0x4c, 0x6d, 0xa7, 0x3f, 0xec, 0x74,
|
||||
0xec, 0x96, 0x8d, 0x9d, 0x81, 0x59, 0xd5, 0x91, 0xd7, 0x7d, 0x78, 0x4d, 0x1a, 0xb4, 0xce, 0x9b,
|
||||
0x8e, 0x83, 0xbb, 0xb4, 0x6d, 0xf7, 0x9b, 0x2f, 0xbb, 0xb8, 0x6d, 0x6e, 0xa1, 0x43, 0x78, 0x34,
|
||||
0xc0, 0xbd, 0x0b, 0x97, 0x34, 0xc9, 0x3b, 0x9a, 0xca, 0x3b, 0x4d, 0xbb, 0x3b, 0x24, 0xd8, 0xdc,
|
||||
0x46, 0x5f, 0xc0, 0x21, 0xc1, 0x6f, 0x86, 0x36, 0xc1, 0x6d, 0xea, 0xb8, 0x6d, 0x4c, 0x3b, 0xb8,
|
||||
0x39, 0x18, 0x12, 0x4c, 0x7b, 0x76, 0xbf, 0x6f, 0x3b, 0x3f, 0x98, 0x26, 0xfa, 0x0a, 0x8e, 0x16,
|
||||
0x2a, 0x0b, 0x07, 0xb7, 0xb4, 0x76, 0xe4, 0xf7, 0xa5, 0x25, 0x75, 0xf0, 0x4f, 0x03, 0x7a, 0x81,
|
||||
0x31, 0x31, 0x11, 0xaa, 0xc3, 0xfe, 0x32, 0xbc, 0x0e, 0x90, 0xc4, 0xde, 0x95, 0xb2, 0x0b, 0x4c,
|
||||
0x7a, 0x4d, 0x47, 0x16, 0x78, 0x45, 0xb6, 0x27, 0xaf, 0xbd, 0x94, 0xdd, 0xbe, 0xf6, 0x03, 0x84,
|
||||
0x60, 0x2b, 0x53, 0x95, 0x4e, 0x93, 0x98, 0xfb, 0x68, 0x0f, 0xb6, 0xd3, 0x1b, 0xa4, 0x8a, 0xff,
|
||||
0x2a, 0xa2, 0x87, 0x80, 0x86, 0x0e, 0xc1, 0xcd, 0xb6, 0x4c, 0xc8, 0x42, 0xf0, 0xef, 0xe2, 0xab,
|
||||
0x7c, 0x69, 0xc3, 0xcc, 0x35, 0xfe, 0x9e, 0x83, 0xda, 0xca, 0xbb, 0x44, 0x8f, 0xa1, 0x1c, 0x7b,
|
||||
0x57, 0x01, 0x13, 0x92, 0x39, 0x34, 0xa9, 0x2c, 0x01, 0x35, 0x1b, 0x27, 0xcc, 0x0b, 0x34, 0x9b,
|
||||
0x69, 0x36, 0x2f, 0x2b, 0x44, 0x71, 0xd9, 0x01, 0x14, 0xd3, 0xf9, 0x9a, 0x5b, 0xcc, 0xd7, 0xcd,
|
||||
0x91, 0x9e, 0xab, 0x8f, 0xa1, 0x2c, 0x29, 0x33, 0x16, 0x6c, 0x3a, 0x53, 0x4f, 0xbc, 0x46, 0x96,
|
||||
0x00, 0xfa, 0x12, 0x6a, 0x53, 0x1e, 0xc7, 0xec, 0x8a, 0x53, 0xfd, 0x4c, 0x41, 0x69, 0x54, 0x13,
|
||||
0xb0, 0xa3, 0x5e, 0xeb, 0x97, 0x90, 0xd2, 0x46, 0xa2, 0x54, 0xd0, 0x4a, 0x09, 0xa8, 0x95, 0x6e,
|
||||
0x33, 0xb6, 0x60, 0x09, 0x1b, 0x64, 0x19, 0x5b, 0x30, 0xf4, 0x14, 0x76, 0x34, 0xe5, 0x78, 0x81,
|
||||
0x37, 0x9d, 0x4f, 0x35, 0xf5, 0x14, 0x15, 0xf5, 0x6c, 0x2b, 0xea, 0xd1, 0xb8, 0x62, 0xa0, 0x47,
|
||||
0x50, 0x7a, 0xcf, 0x62, 0x2e, 0x87, 0x45, 0x42, 0x0d, 0x45, 0x79, 0xee, 0x70, 0x2e, 0x45, 0x72,
|
||||
0x84, 0x44, 0x92, 0xf4, 0x34, 0x23, 0x14, 0x2f, 0x39, 0x27, 0x32, 0x97, 0x8b, 0x08, 0xec, 0xe3,
|
||||
0x32, 0x42, 0x25, 0x13, 0x41, 0xe3, 0x2a, 0xc2, 0x53, 0xd8, 0xe1, 0x1f, 0x45, 0xc4, 0x68, 0x38,
|
||||
0x63, 0x3f, 0xcf, 0x39, 0x1d, 0x33, 0xc1, 0xac, 0xaa, 0x4a, 0xf0, 0xb6, 0x12, 0xb8, 0x0a, 0x6f,
|
||||
0x33, 0xc1, 0x1a, 0x8f, 0xa1, 0x4e, 0x78, 0xcc, 0x45, 0xcf, 0x8b, 0x63, 0x2f, 0x0c, 0x5a, 0x61,
|
||||
0x20, 0xa2, 0xd0, 0x4f, 0x66, 0x4e, 0xe3, 0x10, 0x0e, 0xd6, 0x4a, 0xf5, 0xd0, 0x90, 0xc6, 0x6f,
|
||||
0xe6, 0x3c, 0xba, 0x59, 0x6f, 0xfc, 0x06, 0x0e, 0xd6, 0x4a, 0x93, 0x89, 0xf3, 0x0c, 0x0a, 0x33,
|
||||
0xe6, 0x45, 0xb1, 0xb5, 0xa1, 0xb6, 0x98, 0xfd, 0x95, 0xd1, 0xef, 0x45, 0xe7, 0x5e, 0x2c, 0xc2,
|
||||
0xe8, 0x86, 0x68, 0xa5, 0x57, 0xf9, 0x92, 0x61, 0x6e, 0x34, 0xfe, 0x69, 0x40, 0x25, 0x23, 0x94,
|
||||
0x7d, 0x10, 0x84, 0x63, 0x4e, 0x2f, 0xa3, 0x70, 0x9a, 0x76, 0xd8, 0x02, 0x40, 0x16, 0x14, 0xd5,
|
||||
0x41, 0x84, 0x49, 0x7b, 0xa5, 0xc7, 0xd5, 0xfe, 0xc9, 0xa9, 0x19, 0x9c, 0xe9, 0x9f, 0x33, 0xd8,
|
||||
0x9b, 0x7a, 0x01, 0x9d, 0xf1, 0x80, 0xf9, 0xde, 0x1f, 0x39, 0x4d, 0x77, 0x81, 0xbc, 0x52, 0x5c,
|
||||
0x2b, 0x43, 0x2f, 0xe0, 0xa1, 0xcf, 0x62, 0x41, 0x99, 0x10, 0x7c, 0x3a, 0x13, 0x34, 0x9e, 0x8f,
|
||||
0x46, 0x3c, 0x8e, 0x2f, 0xe7, 0xbe, 0xea, 0x98, 0x12, 0xb9, 0x4f, 0xfc, 0x2a, 0x5f, 0x2a, 0x98,
|
||||
0x9b, 0x8d, 0xbf, 0x1a, 0xb0, 0xf3, 0x72, 0xee, 0xf9, 0xe3, 0x95, 0x99, 0xff, 0x08, 0x4a, 0x32,
|
||||
0x40, 0x66, 0xa7, 0x90, 0x8b, 0x89, 0x2a, 0xf2, 0xba, 0x45, 0x79, 0x63, 0xed, 0xa2, 0xbc, 0x6e,
|
||||
0x65, 0xcd, 0xdd, 0xbb, 0xb2, 0x3e, 0x81, 0xca, 0x24, 0x9c, 0xd1, 0xd9, 0xfc, 0xfd, 0x35, 0xbf,
|
||||
0x89, 0xad, 0xfc, 0x51, 0xee, 0xb8, 0x4a, 0x60, 0x12, 0xce, 0x2e, 0x34, 0xd2, 0x78, 0x01, 0x28,
|
||||
0x7b, 0xd1, 0xa4, 0x9a, 0x8b, 0xd5, 0xc3, 0xb8, 0x77, 0xf5, 0x78, 0xfa, 0x17, 0x03, 0xaa, 0xd9,
|
||||
0xad, 0x0e, 0xd5, 0xa0, 0x6c, 0x3b, 0xb4, 0xd3, 0xb5, 0x7f, 0x38, 0x1f, 0x98, 0x9f, 0xc9, 0x63,
|
||||
0x7f, 0xd8, 0x6a, 0x61, 0xdc, 0xc6, 0x6d, 0xd3, 0x90, 0xcc, 0x24, 0x49, 0x06, 0xb7, 0xe9, 0xc0,
|
||||
0xee, 0x61, 0x77, 0x28, 0x67, 0xd6, 0x2e, 0x6c, 0x27, 0x98, 0xe3, 0x52, 0xe2, 0x0e, 0x07, 0xd8,
|
||||
0xcc, 0x21, 0x13, 0xaa, 0x09, 0x88, 0x09, 0x71, 0x89, 0x99, 0x97, 0x44, 0x9b, 0x20, 0x77, 0xe7,
|
||||
0x5f, 0x3a, 0x1e, 0x0b, 0x67, 0xff, 0xc8, 0xc3, 0xa6, 0xba, 0x60, 0x84, 0xce, 0xa1, 0x92, 0x59,
|
||||
0x9d, 0xd1, 0xe1, 0x27, 0x57, 0xea, 0xba, 0xb5, 0x7e, 0x4d, 0x9d, 0xc7, 0xcf, 0x0d, 0xf4, 0x0a,
|
||||
0xaa, 0xd9, 0xe5, 0x18, 0x65, 0x97, 0x9e, 0x35, 0x5b, 0xf3, 0x27, 0x7d, 0xbd, 0x06, 0x13, 0xc7,
|
||||
0xc2, 0x9b, 0xca, 0x25, 0x27, 0x59, 0x3b, 0x51, 0x3d, 0xa3, 0x7f, 0x6b, 0x97, 0xad, 0x1f, 0xac,
|
||||
0x95, 0x25, 0x15, 0xea, 0xea, 0x4f, 0x4c, 0x16, 0xbf, 0x3b, 0x9f, 0xb8, 0xba, 0x6d, 0xd6, 0x3f,
|
||||
0xbf, 0x4f, 0x9c, 0x78, 0x1b, 0xc3, 0xee, 0x1a, 0x66, 0x40, 0xbf, 0xc8, 0xde, 0xe0, 0x5e, 0x5e,
|
||||
0xa9, 0x7f, 0xfd, 0xbf, 0xd4, 0x96, 0x51, 0xd6, 0x50, 0xc8, 0x4a, 0x94, 0xfb, 0x09, 0x68, 0x25,
|
||||
0xca, 0xa7, 0x98, 0xc8, 0x06, 0x58, 0x76, 0x34, 0x7a, 0x9c, 0xb1, 0xba, 0xf3, 0x22, 0xeb, 0x87,
|
||||
0xf7, 0x48, 0xb5, 0xab, 0x97, 0xbf, 0xfa, 0xdd, 0xe9, 0x95, 0x27, 0x26, 0xf3, 0xf7, 0x27, 0xa3,
|
||||
0x70, 0x7a, 0xea, 0xcb, 0x6d, 0x2e, 0xf0, 0x82, 0xab, 0x80, 0x8b, 0x3f, 0x84, 0xd1, 0xf5, 0xa9,
|
||||
0x1f, 0x8c, 0x4f, 0xd5, 0xc3, 0x38, 0x5d, 0x78, 0x79, 0xbf, 0xa9, 0xfe, 0xb1, 0xfe, 0xf5, 0x7f,
|
||||
0x03, 0x00, 0x00, 0xff, 0xff, 0x3e, 0x15, 0x37, 0x36, 0x88, 0x0f, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
@ -337,30 +337,12 @@ message QueryMissionControlRequest {}
|
||||
|
||||
/// QueryMissionControlResponse contains mission control state.
|
||||
message QueryMissionControlResponse {
|
||||
/// Node-level mission control state.
|
||||
repeated NodeHistory nodes = 1 [json_name = "nodes"];
|
||||
reserved 1;
|
||||
|
||||
/// Node pair-level mission control state.
|
||||
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.
|
||||
message PairHistory {
|
||||
/// The source node pubkey of the pair.
|
||||
@ -375,8 +357,7 @@ message PairHistory {
|
||||
/// Minimum penalization amount (only applies to failed attempts).
|
||||
int64 min_penalize_amt_sat = 4 [json_name = "min_penalize_amt_sat"];
|
||||
|
||||
/// Estimation of success probability for this pair.
|
||||
float success_prob = 5 [json_name = "success_prob"];
|
||||
reserved 5;
|
||||
|
||||
/// Whether the last payment attempt through this pair was 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()
|
||||
|
||||
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))
|
||||
for _, p := range snapshot.Pairs {
|
||||
// Prevent binding to loop variable.
|
||||
@ -494,7 +478,6 @@ func (s *Server) QueryMissionControl(ctx context.Context,
|
||||
MinPenalizeAmtSat: int64(
|
||||
pair.MinPenalizeAmt.ToSatoshis(),
|
||||
),
|
||||
SuccessProb: float32(pair.SuccessProb),
|
||||
LastAttemptSuccessful: pair.LastAttemptSuccessful,
|
||||
}
|
||||
|
||||
@ -502,7 +485,6 @@ func (s *Server) QueryMissionControl(ctx context.Context,
|
||||
}
|
||||
|
||||
response := QueryMissionControlResponse{
|
||||
Nodes: rpcNodes,
|
||||
Pairs: rpcPairs,
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -47,8 +46,15 @@ const (
|
||||
// prevSuccessProbability is the assumed probability for node pairs that
|
||||
// successfully relayed the previous attempt.
|
||||
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
|
||||
// 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
|
||||
@ -59,11 +65,11 @@ const (
|
||||
// since the last failure is used to estimate a success probability that is fed
|
||||
// into the path finding process for subsequent payment attempts.
|
||||
type MissionControl struct {
|
||||
// lastPairResult tracks the last payment result per node pair.
|
||||
lastPairResult map[DirectedNodePair]timedPairResult
|
||||
|
||||
// lastNodeFailure tracks the last node level failure per node.
|
||||
lastNodeFailure map[route.Vertex]time.Time
|
||||
// lastPairResult tracks the last payment result (on a pair basis) for
|
||||
// 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
|
||||
// particular node.
|
||||
lastPairResult map[route.Vertex]NodeResults
|
||||
|
||||
// lastSecondChance tracks the last time a second chance was granted for
|
||||
// a directed node pair.
|
||||
@ -77,6 +83,10 @@ type MissionControl struct {
|
||||
|
||||
store *missionControlStore
|
||||
|
||||
// estimator is the probability estimator that is used with the payment
|
||||
// results that mission control collects.
|
||||
estimator *probabilityEstimator
|
||||
|
||||
sync.Mutex
|
||||
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
|
||||
// timedPairResult describes a timestamped pair result.
|
||||
@ -112,28 +131,11 @@ type timedPairResult struct {
|
||||
// MissionControlSnapshot contains a snapshot of the current state of mission
|
||||
// control.
|
||||
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
|
||||
// logged.
|
||||
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
|
||||
// state in mission control.
|
||||
type MissionControlPairSnapshot struct {
|
||||
@ -147,9 +149,6 @@ type MissionControlPairSnapshot struct {
|
||||
// penalized.
|
||||
MinPenalizeAmt lnwire.MilliSatoshi
|
||||
|
||||
// SuccessProb is the success probability estimation for this channel.
|
||||
SuccessProb float64
|
||||
|
||||
// LastAttemptSuccessful indicates whether the last payment attempt
|
||||
// through this pair was successful.
|
||||
LastAttemptSuccessful bool
|
||||
@ -171,21 +170,29 @@ func NewMissionControl(db *bbolt.DB, cfg *MissionControlConfig) (
|
||||
*MissionControl, error) {
|
||||
|
||||
log.Debugf("Instantiating mission control with config: "+
|
||||
"PenaltyHalfLife=%v, AprioriHopProbability=%v",
|
||||
cfg.PenaltyHalfLife, cfg.AprioriHopProbability)
|
||||
"PenaltyHalfLife=%v, AprioriHopProbability=%v, "+
|
||||
"AprioriWeight=%v", cfg.PenaltyHalfLife,
|
||||
cfg.AprioriHopProbability, cfg.AprioriWeight)
|
||||
|
||||
store, err := newMissionControlStore(db, cfg.MaxMcHistory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
estimator := &probabilityEstimator{
|
||||
aprioriHopProbability: cfg.AprioriHopProbability,
|
||||
aprioriWeight: cfg.AprioriWeight,
|
||||
penaltyHalfLife: cfg.PenaltyHalfLife,
|
||||
prevSuccessProbability: prevSuccessProbability,
|
||||
}
|
||||
|
||||
mc := &MissionControl{
|
||||
lastPairResult: make(map[DirectedNodePair]timedPairResult),
|
||||
lastNodeFailure: make(map[route.Vertex]time.Time),
|
||||
lastPairResult: make(map[route.Vertex]NodeResults),
|
||||
lastSecondChance: make(map[DirectedNodePair]time.Time),
|
||||
now: time.Now,
|
||||
cfg: cfg,
|
||||
store: store,
|
||||
estimator: estimator,
|
||||
}
|
||||
|
||||
if err := mc.init(); err != nil {
|
||||
@ -226,8 +233,7 @@ func (m *MissionControl) ResetHistory() error {
|
||||
return err
|
||||
}
|
||||
|
||||
m.lastPairResult = make(map[DirectedNodePair]timedPairResult)
|
||||
m.lastNodeFailure = make(map[route.Vertex]time.Time)
|
||||
m.lastPairResult = make(map[route.Vertex]NodeResults)
|
||||
m.lastSecondChance = make(map[DirectedNodePair]time.Time)
|
||||
|
||||
log.Debugf("Mission control history cleared")
|
||||
@ -243,62 +249,40 @@ func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex,
|
||||
m.Lock()
|
||||
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.
|
||||
func (m *MissionControl) getProbAfterFail(lastFailure time.Time) float64 {
|
||||
if lastFailure.IsZero() {
|
||||
return m.cfg.AprioriHopProbability
|
||||
// setLastPairResult stores a result for a node pair.
|
||||
func (m *MissionControl) setLastPairResult(fromNode,
|
||||
toNode route.Vertex, result timedPairResult) {
|
||||
|
||||
nodePairs, ok := m.lastPairResult[fromNode]
|
||||
if !ok {
|
||||
nodePairs = make(NodeResults)
|
||||
m.lastPairResult[fromNode] = nodePairs
|
||||
}
|
||||
|
||||
timeSinceLastFailure := m.now().Sub(lastFailure)
|
||||
|
||||
// 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
|
||||
nodePairs[toNode] = result
|
||||
}
|
||||
|
||||
// getPairProbability estimates the probability of successfully
|
||||
// traversing from fromNode to toNode based on historical payment outcomes.
|
||||
func (m *MissionControl) getPairProbability(fromNode,
|
||||
toNode route.Vertex, amt lnwire.MilliSatoshi) float64 {
|
||||
// setAllFail stores a fail result for all known connection of the given node.
|
||||
func (m *MissionControl) setAllFail(fromNode route.Vertex,
|
||||
timestamp time.Time) {
|
||||
|
||||
// Start by getting the last node level failure. A node failure is
|
||||
// considered a failure that would have affected every edge. Therefore
|
||||
// we insert a node level failure into the history of every channel. If
|
||||
// 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
|
||||
}
|
||||
nodePairs, ok := m.lastPairResult[fromNode]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
@ -339,40 +323,27 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
||||
defer m.Unlock()
|
||||
|
||||
log.Debugf("Requesting history snapshot from mission control: "+
|
||||
"node_failure_count=%v, pair_result_count=%v",
|
||||
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,
|
||||
})
|
||||
}
|
||||
"pair_result_count=%v", len(m.lastPairResult))
|
||||
|
||||
pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairResult))
|
||||
|
||||
for v, h := range m.lastPairResult {
|
||||
// Show probability assuming amount meets min
|
||||
// penalization amount.
|
||||
prob := m.getPairProbability(v.From, v.To, h.minPenalizeAmt)
|
||||
for fromNode, fromPairs := range m.lastPairResult {
|
||||
for toNode, result := range fromPairs {
|
||||
|
||||
pair := MissionControlPairSnapshot{
|
||||
Pair: v,
|
||||
MinPenalizeAmt: h.minPenalizeAmt,
|
||||
Timestamp: h.timestamp,
|
||||
SuccessProb: prob,
|
||||
LastAttemptSuccessful: h.success,
|
||||
pair := NewDirectedNodePair(fromNode, toNode)
|
||||
|
||||
pairSnapshot := MissionControlPairSnapshot{
|
||||
Pair: pair,
|
||||
MinPenalizeAmt: result.minPenalizeAmt,
|
||||
Timestamp: result.timestamp,
|
||||
LastAttemptSuccessful: result.success,
|
||||
}
|
||||
|
||||
pairs = append(pairs, pairSnapshot)
|
||||
}
|
||||
|
||||
pairs = append(pairs, pair)
|
||||
}
|
||||
|
||||
snapshot := MissionControlSnapshot{
|
||||
Nodes: nodes,
|
||||
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 {
|
||||
log.Debugf("Reporting node failure to Mission Control: "+
|
||||
"node=%v", *i.nodeFailure)
|
||||
|
||||
m.lastNodeFailure[*i.nodeFailure] = result.timeReply
|
||||
m.setAllFail(*i.nodeFailure, result.timeReply)
|
||||
}
|
||||
|
||||
for pair, pairResult := range i.pairResults {
|
||||
@ -480,10 +468,10 @@ func (m *MissionControl) applyPaymentResult(
|
||||
pair, pairResult.minPenalizeAmt)
|
||||
}
|
||||
|
||||
m.lastPairResult[pair] = timedPairResult{
|
||||
m.setLastPairResult(pair.From, pair.To, timedPairResult{
|
||||
timestamp: result.timeReply,
|
||||
pairResult: pairResult,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return i.finalFailureReason
|
||||
|
@ -32,6 +32,10 @@ var (
|
||||
mcTestTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
||||
mcTestNode1 = mcTestRoute.Hops[0].PubKeyBytes
|
||||
mcTestNode2 = mcTestRoute.Hops[1].PubKeyBytes
|
||||
|
||||
testPenaltyHalfLife = 30 * time.Minute
|
||||
testAprioriHopProbability = 0.9
|
||||
testAprioriWeight = 0.5
|
||||
)
|
||||
|
||||
type mcTestContext struct {
|
||||
@ -73,8 +77,9 @@ func (ctx *mcTestContext) restartMc() {
|
||||
mc, err := NewMissionControl(
|
||||
ctx.db,
|
||||
&MissionControlConfig{
|
||||
PenaltyHalfLife: 30 * time.Minute,
|
||||
AprioriHopProbability: 0.8,
|
||||
PenaltyHalfLife: testPenaltyHalfLife,
|
||||
AprioriHopProbability: testAprioriHopProbability,
|
||||
AprioriWeight: testAprioriWeight,
|
||||
},
|
||||
)
|
||||
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)
|
||||
|
||||
// Initial probability is expected to be 1.
|
||||
ctx.expectP(1000, 0.8)
|
||||
// Initial probability is expected to be the a priori.
|
||||
ctx.expectP(1000, testAprioriHopProbability)
|
||||
|
||||
// Expect probability to be zero after reporting the edge as failed.
|
||||
ctx.reportFailure(1000, lnwire.NewTemporaryChannelFailure(nil))
|
||||
ctx.expectP(1000, 0)
|
||||
|
||||
// As we reported with a min penalization amt, a lower amt than reported
|
||||
// should be unaffected.
|
||||
ctx.expectP(500, 0.8)
|
||||
// should return the node probability, which is the a priori
|
||||
// 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.expectP(1000, 0.4)
|
||||
ctx.expectP(1000, 0.3)
|
||||
|
||||
// Edge fails again, this time without a min penalization amt. The edge
|
||||
// should be penalized regardless of amount.
|
||||
@ -156,26 +164,22 @@ func TestMissionControl(t *testing.T) {
|
||||
|
||||
// Edge decay started.
|
||||
ctx.now = testTime.Add(60 * time.Minute)
|
||||
ctx.expectP(1000, 0.4)
|
||||
ctx.expectP(1000, 0.3)
|
||||
|
||||
// Restart mission control to test persistence.
|
||||
ctx.restartMc()
|
||||
ctx.expectP(1000, 0.4)
|
||||
ctx.expectP(1000, 0.3)
|
||||
|
||||
// A node level failure should bring probability of every channel back
|
||||
// to zero.
|
||||
// A node level failure should bring probability of all known channels
|
||||
// back to zero.
|
||||
ctx.reportFailure(0, lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}))
|
||||
ctx.expectP(1000, 0)
|
||||
|
||||
// Check whether history snapshot looks sane.
|
||||
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 {
|
||||
t.Fatalf("expected 2 pairs, but got %v", len(history.Pairs))
|
||||
if len(history.Pairs) != 3 {
|
||||
t.Fatalf("expected 3 pairs, but got %v", len(history.Pairs))
|
||||
}
|
||||
|
||||
// Test reporting a success.
|
||||
@ -192,7 +196,7 @@ func TestMissionControlChannelUpdate(t *testing.T) {
|
||||
ctx.reportFailure(
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (p pairResult) String() string {
|
||||
if p.success {
|
||||
@ -364,10 +378,30 @@ func (i *interpretedResult) processPaymentOutcomeUnknown(route *route.Route) {
|
||||
i.failPairRange(route, 0, n-1)
|
||||
}
|
||||
|
||||
// failNode marks the node indicated by idx in the route as failed. This
|
||||
// function intentionally panics when the self node is failed.
|
||||
// failNode marks the node indicated by idx in the route as failed. It also
|
||||
// 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) {
|
||||
// Mark the node as failing.
|
||||
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
|
||||
@ -387,8 +421,8 @@ func (i *interpretedResult) failPair(
|
||||
pair, _ := getPair(rt, idx)
|
||||
|
||||
// Report pair in both directions without a minimum penalization amount.
|
||||
i.pairResults[pair] = pairResult{}
|
||||
i.pairResults[pair.Reverse()] = pairResult{}
|
||||
i.pairResults[pair] = failPairResult(0)
|
||||
i.pairResults[pair.Reverse()] = failPairResult(0)
|
||||
}
|
||||
|
||||
// failPairBalance marks a pair as failed with a minimum penalization amount.
|
||||
@ -397,9 +431,7 @@ func (i *interpretedResult) failPairBalance(
|
||||
|
||||
pair, amt := getPair(rt, channelIdx)
|
||||
|
||||
i.pairResults[pair] = pairResult{
|
||||
minPenalizeAmt: amt,
|
||||
}
|
||||
i.pairResults[pair] = failPairResult(amt)
|
||||
}
|
||||
|
||||
// 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++ {
|
||||
pair, _ := getPair(rt, idx)
|
||||
|
||||
i.pairResults[pair] = pairResult{
|
||||
success: true,
|
||||
}
|
||||
i.pairResults[pair] = successPairResult()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,12 +68,8 @@ var resultTestCases = []resultTestCase{
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
pairResults: map[DirectedNodePair]pairResult{
|
||||
getTestPair(0, 1): {
|
||||
success: true,
|
||||
},
|
||||
getTestPair(1, 2): {
|
||||
minPenalizeAmt: 99,
|
||||
},
|
||||
getTestPair(0, 1): successPairResult(),
|
||||
getTestPair(1, 2): failPairResult(99),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -87,12 +83,12 @@ var resultTestCases = []resultTestCase{
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
pairResults: map[DirectedNodePair]pairResult{
|
||||
getTestPair(0, 1): {},
|
||||
getTestPair(1, 0): {},
|
||||
getTestPair(1, 2): {},
|
||||
getTestPair(2, 1): {},
|
||||
getTestPair(2, 3): {},
|
||||
getTestPair(3, 2): {},
|
||||
getTestPair(0, 1): failPairResult(0),
|
||||
getTestPair(1, 0): failPairResult(0),
|
||||
getTestPair(1, 2): failPairResult(0),
|
||||
getTestPair(2, 1): failPairResult(0),
|
||||
getTestPair(2, 3): failPairResult(0),
|
||||
getTestPair(3, 2): failPairResult(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -107,12 +103,8 @@ var resultTestCases = []resultTestCase{
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
pairResults: map[DirectedNodePair]pairResult{
|
||||
getTestPair(0, 1): {
|
||||
success: true,
|
||||
},
|
||||
getTestPair(1, 2): {
|
||||
success: true,
|
||||
},
|
||||
getTestPair(0, 1): successPairResult(),
|
||||
getTestPair(1, 2): successPairResult(),
|
||||
},
|
||||
finalFailureReason: &reasonIncorrectDetails,
|
||||
},
|
||||
@ -126,9 +118,7 @@ var resultTestCases = []resultTestCase{
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
pairResults: map[DirectedNodePair]pairResult{
|
||||
getTestPair(0, 1): {
|
||||
success: true,
|
||||
},
|
||||
getTestPair(0, 1): successPairResult(),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -141,12 +131,8 @@ var resultTestCases = []resultTestCase{
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
pairResults: map[DirectedNodePair]pairResult{
|
||||
getTestPair(0, 1): {
|
||||
success: true,
|
||||
},
|
||||
getTestPair(1, 2): {
|
||||
success: true,
|
||||
},
|
||||
getTestPair(0, 1): successPairResult(),
|
||||
getTestPair(1, 2): successPairResult(),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -160,6 +146,10 @@ var resultTestCases = []resultTestCase{
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
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{
|
||||
finalFailureReason: &reasonError,
|
||||
nodeFailure: &hops[1],
|
||||
pairResults: map[DirectedNodePair]pairResult{
|
||||
getTestPair(1, 0): failPairResult(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ func createTestCtxFromGraphInstance(startingHeight uint32, graphInstance *testGr
|
||||
mcConfig := &MissionControlConfig{
|
||||
PenaltyHalfLife: time.Hour,
|
||||
AprioriHopProbability: 0.9,
|
||||
AprioriWeight: 0.5,
|
||||
}
|
||||
|
||||
mc, err := NewMissionControl(
|
||||
|
@ -660,6 +660,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
||||
AprioriHopProbability: routingConfig.AprioriHopProbability,
|
||||
PenaltyHalfLife: routingConfig.PenaltyHalfLife,
|
||||
MaxMcHistory: routingConfig.MaxMcHistory,
|
||||
AprioriWeight: routingConfig.AprioriWeight,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user