routing: process successes in mission control
This commit modifies paymentLifecycle so that it not only feeds failures into mission control, but successes as well. This allows for more accurate probability estimates. Previously, the success probability for a successful pair and a pair with no history was equal. There was no force that pushed towards previously successful routes.
This commit is contained in:
parent
2594abfba1
commit
ff0c5a0d5e
@ -39,7 +39,8 @@ func queryMissionControl(ctx *cli.Context) error {
|
|||||||
|
|
||||||
type displayPairHistory struct {
|
type displayPairHistory struct {
|
||||||
NodeFrom, NodeTo string
|
NodeFrom, NodeTo string
|
||||||
LastFailTime int64
|
LastAttemptSuccessful bool
|
||||||
|
Timestamp int64
|
||||||
SuccessProb float32
|
SuccessProb float32
|
||||||
MinPenalizeAmtSat int64
|
MinPenalizeAmtSat int64
|
||||||
}
|
}
|
||||||
@ -66,7 +67,8 @@ func queryMissionControl(ctx *cli.Context) error {
|
|||||||
displayPairHistory{
|
displayPairHistory{
|
||||||
NodeFrom: hex.EncodeToString(n.NodeFrom),
|
NodeFrom: hex.EncodeToString(n.NodeFrom),
|
||||||
NodeTo: hex.EncodeToString(n.NodeTo),
|
NodeTo: hex.EncodeToString(n.NodeTo),
|
||||||
LastFailTime: n.LastFailTime,
|
LastAttemptSuccessful: n.LastAttemptSuccessful,
|
||||||
|
Timestamp: n.Timestamp,
|
||||||
SuccessProb: n.SuccessProb,
|
SuccessProb: n.SuccessProb,
|
||||||
MinPenalizeAmtSat: n.MinPenalizeAmtSat,
|
MinPenalizeAmtSat: n.MinPenalizeAmtSat,
|
||||||
},
|
},
|
||||||
|
@ -1108,12 +1108,14 @@ type PairHistory struct {
|
|||||||
NodeFrom []byte `protobuf:"bytes,1,opt,name=node_from,proto3" json:"node_from,omitempty"`
|
NodeFrom []byte `protobuf:"bytes,1,opt,name=node_from,proto3" json:"node_from,omitempty"`
|
||||||
/// The destination node pubkey of the pair.
|
/// The destination node pubkey of the pair.
|
||||||
NodeTo []byte `protobuf:"bytes,2,opt,name=node_to,proto3" json:"node_to,omitempty"`
|
NodeTo []byte `protobuf:"bytes,2,opt,name=node_to,proto3" json:"node_to,omitempty"`
|
||||||
/// Time stamp of last failure.
|
/// Time stamp of last result.
|
||||||
LastFailTime int64 `protobuf:"varint,3,opt,name=last_fail_time,proto3" json:"last_fail_time,omitempty"`
|
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||||
/// Minimum penalization amount.
|
/// 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.
|
/// Estimation of success probability for this pair.
|
||||||
SuccessProb float32 `protobuf:"fixed32,5,opt,name=success_prob,proto3" json:"success_prob,omitempty"`
|
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:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -1158,9 +1160,9 @@ func (m *PairHistory) GetNodeTo() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PairHistory) GetLastFailTime() int64 {
|
func (m *PairHistory) GetTimestamp() int64 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.LastFailTime
|
return m.Timestamp
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -1179,6 +1181,13 @@ func (m *PairHistory) GetSuccessProb() float32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *PairHistory) GetLastAttemptSuccessful() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.LastAttemptSuccessful
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterEnum("routerrpc.PaymentState", PaymentState_name, PaymentState_value)
|
proto.RegisterEnum("routerrpc.PaymentState", PaymentState_name, PaymentState_value)
|
||||||
proto.RegisterEnum("routerrpc.Failure_FailureCode", Failure_FailureCode_name, Failure_FailureCode_value)
|
proto.RegisterEnum("routerrpc.Failure_FailureCode", Failure_FailureCode_name, Failure_FailureCode_value)
|
||||||
@ -1203,116 +1212,117 @@ 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{
|
||||||
// 1736 bytes of a gzipped FileDescriptorProto
|
// 1759 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x41, 0x73, 0x22, 0xc7,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x41, 0x73, 0x22, 0xb9,
|
||||||
0x15, 0x36, 0x02, 0x84, 0x78, 0x80, 0x98, 0x6d, 0x69, 0xb5, 0xb3, 0x68, 0x65, 0xcb, 0xd8, 0x59,
|
0x15, 0x5e, 0x0c, 0x18, 0x78, 0x80, 0xdd, 0x96, 0x3d, 0x76, 0x0f, 0x1e, 0xef, 0x7a, 0xd9, 0xcd,
|
||||||
0xab, 0xb6, 0x1c, 0xc9, 0x21, 0x65, 0x97, 0xcb, 0x87, 0xa4, 0x58, 0x68, 0xac, 0xd9, 0x85, 0x19,
|
0xac, 0x6b, 0x6a, 0x63, 0x6f, 0x9c, 0xda, 0xad, 0xa9, 0x3d, 0x24, 0xc5, 0x80, 0x58, 0xf7, 0x0c,
|
||||||
0xb9, 0x81, 0xb5, 0x37, 0x39, 0x74, 0xb5, 0xa0, 0x05, 0x53, 0x1a, 0x66, 0xf0, 0x4c, 0xa3, 0xac,
|
0x74, 0x7b, 0x05, 0xcc, 0xee, 0x24, 0x07, 0x95, 0x0c, 0xb2, 0xe9, 0x72, 0xd3, 0xcd, 0x74, 0x0b,
|
||||||
0x72, 0xc8, 0x25, 0x95, 0x63, 0xee, 0xf9, 0x17, 0xf9, 0x15, 0x39, 0xe4, 0x8f, 0x24, 0xbf, 0x22,
|
0x67, 0x9c, 0x43, 0x2e, 0xa9, 0x1c, 0x73, 0xcf, 0xbf, 0xc8, 0xef, 0xc8, 0x1f, 0x49, 0x7e, 0x41,
|
||||||
0x55, 0xa9, 0xee, 0x9e, 0x81, 0x01, 0xb1, 0x9b, 0x9c, 0x98, 0xfe, 0xde, 0xd7, 0xaf, 0x5f, 0xf7,
|
0x8e, 0xa9, 0x4a, 0x49, 0xea, 0x86, 0x06, 0xe3, 0x49, 0x4e, 0xb4, 0xbe, 0xf7, 0xe9, 0x49, 0x7a,
|
||||||
0xeb, 0xf7, 0xf5, 0x03, 0x8e, 0xc2, 0x60, 0x21, 0x78, 0x18, 0xce, 0x47, 0x17, 0xfa, 0xeb, 0x7c,
|
0x4f, 0xef, 0xd3, 0x03, 0xf6, 0xc3, 0x60, 0x26, 0x78, 0x18, 0x4e, 0x87, 0x67, 0xfa, 0xeb, 0x74,
|
||||||
0x1e, 0x06, 0x22, 0x40, 0xc5, 0x25, 0x5e, 0x2b, 0x86, 0xf3, 0x91, 0x46, 0xeb, 0xff, 0xc9, 0x02,
|
0x1a, 0x06, 0x22, 0x40, 0xa5, 0x39, 0x5e, 0x2b, 0x85, 0xd3, 0xa1, 0x46, 0xeb, 0xff, 0xc9, 0x02,
|
||||||
0xea, 0x73, 0x7f, 0x7c, 0xc5, 0xee, 0x67, 0xdc, 0x17, 0x84, 0xff, 0xbc, 0xe0, 0x91, 0x40, 0x08,
|
0xea, 0x71, 0x7f, 0x74, 0xc9, 0xee, 0x27, 0xdc, 0x17, 0x84, 0xbf, 0x9f, 0xf1, 0x48, 0x20, 0x04,
|
||||||
0x72, 0x63, 0x1e, 0x09, 0x33, 0x73, 0x9a, 0x39, 0x2b, 0x13, 0xf5, 0x8d, 0x0c, 0xc8, 0xb2, 0x99,
|
0xb9, 0x11, 0x8f, 0x84, 0x99, 0x39, 0xce, 0x9c, 0x54, 0x88, 0xfa, 0x46, 0x06, 0x64, 0xd9, 0x44,
|
||||||
0x30, 0x77, 0x4e, 0x33, 0x67, 0x59, 0x22, 0x3f, 0xd1, 0xa7, 0x50, 0x9e, 0xeb, 0x79, 0x74, 0xca,
|
0x98, 0x1b, 0xc7, 0x99, 0x93, 0x2c, 0x91, 0x9f, 0xe8, 0x73, 0xa8, 0x4c, 0xf5, 0x3c, 0x3a, 0x66,
|
||||||
0xa2, 0xa9, 0x99, 0x55, 0xec, 0x52, 0x8c, 0x5d, 0xb2, 0x68, 0x8a, 0xce, 0xc0, 0xb8, 0x71, 0x7d,
|
0xd1, 0xd8, 0xcc, 0x2a, 0x76, 0x39, 0xc6, 0x2e, 0x58, 0x34, 0x46, 0x27, 0x60, 0x5c, 0xbb, 0x3e,
|
||||||
0xe6, 0xd1, 0x91, 0x27, 0xee, 0xe8, 0x98, 0x7b, 0x82, 0x99, 0xb9, 0xd3, 0xcc, 0x59, 0x9e, 0xec,
|
0xf3, 0xe8, 0xd0, 0x13, 0x77, 0x74, 0xc4, 0x3d, 0xc1, 0xcc, 0xdc, 0x71, 0xe6, 0x24, 0x4f, 0xb6,
|
||||||
0x2b, 0xbc, 0xe5, 0x89, 0xbb, 0xb6, 0x44, 0xd1, 0x17, 0x50, 0x4d, 0x9c, 0x85, 0x3a, 0x0a, 0x33,
|
0x14, 0xde, 0xf4, 0xc4, 0x5d, 0x4b, 0xa2, 0xe8, 0x2b, 0xd8, 0x4e, 0x9c, 0x85, 0x7a, 0x17, 0x66,
|
||||||
0x7f, 0x9a, 0x39, 0x2b, 0x92, 0xfd, 0xf9, 0x7a, 0x6c, 0x5f, 0x40, 0x55, 0xb8, 0x33, 0x1e, 0x2c,
|
0xfe, 0x38, 0x73, 0x52, 0x22, 0x5b, 0xd3, 0xe5, 0xbd, 0x7d, 0x05, 0xdb, 0xc2, 0x9d, 0xf0, 0x60,
|
||||||
0x04, 0x8d, 0xf8, 0x28, 0xf0, 0xc7, 0x91, 0xb9, 0xab, 0x3d, 0xc6, 0x70, 0x5f, 0xa3, 0xa8, 0x0e,
|
0x26, 0x68, 0xc4, 0x87, 0x81, 0x3f, 0x8a, 0xcc, 0x4d, 0xed, 0x31, 0x86, 0x7b, 0x1a, 0x45, 0x75,
|
||||||
0x95, 0x1b, 0xce, 0xa9, 0xe7, 0xce, 0x5c, 0x41, 0x23, 0x26, 0xcc, 0x82, 0x0a, 0xbd, 0x74, 0xc3,
|
0xa8, 0x5e, 0x73, 0x4e, 0x3d, 0x77, 0xe2, 0x0a, 0x1a, 0x31, 0x61, 0x16, 0xd4, 0xd6, 0xcb, 0xd7,
|
||||||
0x79, 0x57, 0x62, 0x7d, 0x26, 0x64, 0x7c, 0xc1, 0x42, 0x4c, 0x02, 0xd7, 0x9f, 0xd0, 0xd1, 0x94,
|
0x9c, 0x77, 0x24, 0xd6, 0x63, 0x42, 0xee, 0x2f, 0x98, 0x89, 0x9b, 0xc0, 0xf5, 0x6f, 0xe8, 0x70,
|
||||||
0xf9, 0xd4, 0x1d, 0x9b, 0x7b, 0xa7, 0x99, 0xb3, 0x1c, 0xd9, 0x4f, 0xf0, 0xd6, 0x94, 0xf9, 0xd6,
|
0xcc, 0x7c, 0xea, 0x8e, 0xcc, 0xe2, 0x71, 0xe6, 0x24, 0x47, 0xb6, 0x12, 0xbc, 0x39, 0x66, 0xbe,
|
||||||
0x18, 0x9d, 0x00, 0xa8, 0x3d, 0x28, 0x77, 0x66, 0x51, 0xad, 0x58, 0x94, 0x88, 0xf2, 0x85, 0x1a,
|
0x35, 0x42, 0x47, 0x00, 0xea, 0x0c, 0xca, 0x9d, 0x59, 0x52, 0x2b, 0x96, 0x24, 0xa2, 0x7c, 0xa1,
|
||||||
0x50, 0x52, 0x07, 0x4c, 0xa7, 0xae, 0x2f, 0x22, 0x13, 0x4e, 0xb3, 0x67, 0xa5, 0x86, 0x71, 0xee,
|
0x73, 0x28, 0xab, 0x00, 0xd3, 0xb1, 0xeb, 0x8b, 0xc8, 0x84, 0xe3, 0xec, 0x49, 0xf9, 0xdc, 0x38,
|
||||||
0xf9, 0xf2, 0xac, 0x89, 0xb4, 0x5c, 0xba, 0xbe, 0x20, 0x69, 0x12, 0xc2, 0xb0, 0x27, 0x4f, 0x96,
|
0xf5, 0x7c, 0x19, 0x6b, 0x22, 0x2d, 0x17, 0xae, 0x2f, 0x48, 0x9a, 0x84, 0x30, 0x14, 0x65, 0x64,
|
||||||
0x0a, 0xef, 0xce, 0x2c, 0xa9, 0x09, 0x2f, 0xce, 0x97, 0x59, 0x3a, 0x7f, 0x98, 0x96, 0xf3, 0x36,
|
0xa9, 0xf0, 0xee, 0xcc, 0xb2, 0x9a, 0xf0, 0xe2, 0x74, 0x9e, 0xa5, 0xd3, 0x87, 0x69, 0x39, 0x6d,
|
||||||
0x8f, 0xc4, 0xc0, 0xbb, 0xc3, 0xbe, 0x08, 0xef, 0x49, 0x61, 0xac, 0x47, 0xb5, 0xef, 0xa0, 0x9c,
|
0xf1, 0x48, 0xf4, 0xbd, 0x3b, 0xec, 0x8b, 0xf0, 0x9e, 0x14, 0x46, 0x7a, 0x54, 0xfb, 0x1e, 0x2a,
|
||||||
0x36, 0xc8, 0x44, 0xdd, 0xf2, 0x7b, 0x95, 0xbb, 0x1c, 0x91, 0x9f, 0xe8, 0x10, 0xf2, 0x77, 0xcc,
|
0x69, 0x83, 0x4c, 0xd4, 0x2d, 0xbf, 0x57, 0xb9, 0xcb, 0x11, 0xf9, 0x89, 0xf6, 0x20, 0x7f, 0xc7,
|
||||||
0x5b, 0x70, 0x95, 0xbc, 0x32, 0xd1, 0x83, 0xef, 0x76, 0xbe, 0xcd, 0xd4, 0xbf, 0x85, 0x83, 0x41,
|
0xbc, 0x19, 0x57, 0xc9, 0xab, 0x10, 0x3d, 0xf8, 0x7e, 0xe3, 0x65, 0xa6, 0xfe, 0x12, 0x76, 0xfb,
|
||||||
0xc8, 0x46, 0xb7, 0x1b, 0xf9, 0xdf, 0xcc, 0x6c, 0xe6, 0x41, 0x66, 0xeb, 0x7f, 0x82, 0x4a, 0x3c,
|
0x21, 0x1b, 0xde, 0xae, 0xe4, 0x7f, 0x35, 0xb3, 0x99, 0x07, 0x99, 0xad, 0xff, 0x09, 0xaa, 0xf1,
|
||||||
0xa9, 0x2f, 0x98, 0x58, 0x44, 0xe8, 0x97, 0x90, 0x8f, 0x04, 0x13, 0x5c, 0x91, 0xf7, 0x1b, 0x4f,
|
0xa4, 0x9e, 0x60, 0x62, 0x16, 0xa1, 0x5f, 0x42, 0x3e, 0x12, 0x4c, 0x70, 0x45, 0xde, 0x3a, 0x3f,
|
||||||
0x52, 0x5b, 0x49, 0x11, 0x39, 0xd1, 0x2c, 0x54, 0x83, 0xbd, 0x79, 0xc8, 0xdd, 0x19, 0x9b, 0x24,
|
0x48, 0x1d, 0x25, 0x45, 0xe4, 0x44, 0xb3, 0x50, 0x0d, 0x8a, 0xd3, 0x90, 0xbb, 0x13, 0x76, 0x93,
|
||||||
0x61, 0x2d, 0xc7, 0xa8, 0x0e, 0x79, 0x35, 0x59, 0xdd, 0xa8, 0x52, 0xa3, 0x9c, 0x3e, 0x46, 0xa2,
|
0x6c, 0x6b, 0x3e, 0x46, 0x75, 0xc8, 0xab, 0xc9, 0xea, 0x46, 0x95, 0xcf, 0x2b, 0xe9, 0x30, 0x12,
|
||||||
0x4d, 0xf5, 0xdf, 0x40, 0x55, 0x8d, 0x3b, 0x9c, 0x7f, 0xe8, 0xd6, 0x3e, 0x81, 0x02, 0x9b, 0xe9,
|
0x6d, 0xaa, 0xff, 0x06, 0xb6, 0xd5, 0xb8, 0xcd, 0xf9, 0xc7, 0x6e, 0xed, 0x01, 0x14, 0xd8, 0x44,
|
||||||
0xf4, 0xeb, 0x9b, 0xbb, 0xcb, 0x66, 0x32, 0xf3, 0xf5, 0x31, 0x18, 0xab, 0xf9, 0xd1, 0x3c, 0xf0,
|
0xa7, 0x5f, 0xdf, 0xdc, 0x4d, 0x36, 0x91, 0x99, 0xaf, 0x8f, 0xc0, 0x58, 0xcc, 0x8f, 0xa6, 0x81,
|
||||||
0x23, 0x2e, 0x6f, 0x83, 0x74, 0x2e, 0x2f, 0x83, 0xbc, 0x39, 0x33, 0x39, 0x2b, 0xa3, 0x66, 0xed,
|
0x1f, 0x71, 0x79, 0x1b, 0xa4, 0x73, 0x79, 0x19, 0xe4, 0xcd, 0x99, 0xc8, 0x59, 0x19, 0x35, 0x6b,
|
||||||
0xc7, 0x78, 0x87, 0xf3, 0x5e, 0xc4, 0x04, 0x7a, 0xae, 0x2f, 0x21, 0xf5, 0x82, 0xd1, 0xad, 0xbc,
|
0x2b, 0xc6, 0xdb, 0x9c, 0x77, 0x23, 0x26, 0xd0, 0x73, 0x7d, 0x09, 0xa9, 0x17, 0x0c, 0x6f, 0xe5,
|
||||||
0xd6, 0xec, 0x3e, 0x76, 0x5f, 0x91, 0x70, 0x37, 0x18, 0xdd, 0xb6, 0x25, 0x58, 0xff, 0xbd, 0x2e,
|
0xb5, 0x66, 0xf7, 0xb1, 0xfb, 0xaa, 0x84, 0x3b, 0xc1, 0xf0, 0xb6, 0x25, 0xc1, 0xfa, 0xef, 0x75,
|
||||||
0xaf, 0x41, 0xa0, 0x63, 0xff, 0xbf, 0x8f, 0x77, 0x75, 0x04, 0x3b, 0xef, 0x3f, 0x02, 0x0a, 0x07,
|
0x79, 0xf5, 0x03, 0xbd, 0xf7, 0xff, 0x3b, 0xbc, 0x8b, 0x10, 0x6c, 0x3c, 0x1e, 0x02, 0x0a, 0xbb,
|
||||||
0x6b, 0xce, 0xe3, 0x5d, 0xa4, 0x4f, 0x36, 0xb3, 0x71, 0xb2, 0x5f, 0x42, 0xe1, 0x86, 0xb9, 0xde,
|
0x4b, 0xce, 0xe3, 0x53, 0xa4, 0x23, 0x9b, 0x59, 0x89, 0xec, 0xd7, 0x50, 0xb8, 0x66, 0xae, 0x37,
|
||||||
0x22, 0x4c, 0x1c, 0xa3, 0x54, 0x9a, 0x3a, 0xda, 0x42, 0x12, 0x4a, 0xfd, 0x9f, 0x05, 0x28, 0xc4,
|
0x0b, 0x13, 0xc7, 0x28, 0x95, 0xa6, 0xb6, 0xb6, 0x90, 0x84, 0x52, 0xff, 0x47, 0x01, 0x0a, 0x31,
|
||||||
0x20, 0x6a, 0x40, 0x6e, 0x14, 0x8c, 0x93, 0xec, 0x7e, 0xfc, 0x70, 0x5a, 0xf2, 0xdb, 0x0a, 0xc6,
|
0x88, 0xce, 0x21, 0x37, 0x0c, 0x46, 0x49, 0x76, 0x3f, 0x7d, 0x38, 0x2d, 0xf9, 0x6d, 0x06, 0x23,
|
||||||
0x9c, 0x28, 0x2e, 0xfa, 0x2d, 0xec, 0xcb, 0xa2, 0xf2, 0xb9, 0x47, 0x17, 0xf3, 0x31, 0x5b, 0x26,
|
0x4e, 0x14, 0x17, 0xfd, 0x16, 0xb6, 0x64, 0x51, 0xf9, 0xdc, 0xa3, 0xb3, 0xe9, 0x88, 0xcd, 0x13,
|
||||||
0xd4, 0x4c, 0xcd, 0x6e, 0x69, 0xc2, 0x50, 0xd9, 0x49, 0x65, 0x94, 0x1e, 0xa2, 0x63, 0x28, 0x4e,
|
0x6a, 0xa6, 0x66, 0x37, 0x35, 0x61, 0xa0, 0xec, 0xa4, 0x3a, 0x4c, 0x0f, 0xd1, 0x21, 0x94, 0xc6,
|
||||||
0x85, 0x37, 0xd2, 0x99, 0xc8, 0xa9, 0x0b, 0xbd, 0x27, 0x01, 0x95, 0x83, 0x3a, 0x54, 0x02, 0xdf,
|
0xc2, 0x1b, 0xea, 0x4c, 0xe4, 0xd4, 0x85, 0x2e, 0x4a, 0x40, 0xe5, 0xa0, 0x0e, 0xd5, 0xc0, 0x77,
|
||||||
0x0d, 0x7c, 0x1a, 0x4d, 0x19, 0x6d, 0x7c, 0xfd, 0x8d, 0xd2, 0x8b, 0x32, 0x29, 0x29, 0xb0, 0x3f,
|
0x03, 0x9f, 0x46, 0x63, 0x46, 0xcf, 0xbf, 0xfd, 0x4e, 0xe9, 0x45, 0x85, 0x94, 0x15, 0xd8, 0x1b,
|
||||||
0x65, 0x8d, 0xaf, 0xbf, 0x41, 0x9f, 0x40, 0x49, 0x55, 0x2d, 0x7f, 0x37, 0x77, 0xc3, 0x7b, 0x25,
|
0xb3, 0xf3, 0x6f, 0xbf, 0x43, 0x9f, 0x41, 0x59, 0x55, 0x2d, 0xff, 0x30, 0x75, 0xc3, 0x7b, 0x25,
|
||||||
0x14, 0x15, 0xa2, 0x0a, 0x19, 0x2b, 0x44, 0x96, 0xc6, 0x8d, 0xc7, 0x26, 0x91, 0x12, 0x87, 0x0a,
|
0x14, 0x55, 0xa2, 0x0a, 0x19, 0x2b, 0x44, 0x96, 0xc6, 0xb5, 0xc7, 0x6e, 0x22, 0x25, 0x0e, 0x55,
|
||||||
0xd1, 0x03, 0xf4, 0x15, 0x1c, 0xc6, 0x67, 0x40, 0xa3, 0x60, 0x11, 0x8e, 0x38, 0x75, 0xfd, 0x31,
|
0xa2, 0x07, 0xe8, 0x1b, 0xd8, 0x8b, 0x63, 0x40, 0xa3, 0x60, 0x16, 0x0e, 0x39, 0x75, 0xfd, 0x11,
|
||||||
0x7f, 0xa7, 0xa4, 0xa1, 0x42, 0x50, 0x6c, 0xeb, 0x2b, 0x93, 0x25, 0x2d, 0xf5, 0xbf, 0xe5, 0xa1,
|
0xff, 0xa0, 0xa4, 0xa1, 0x4a, 0x50, 0x6c, 0xeb, 0x29, 0x93, 0x25, 0x2d, 0xf5, 0xbf, 0xe5, 0xa1,
|
||||||
0x94, 0x3a, 0x00, 0x54, 0x86, 0x3d, 0x82, 0xfb, 0x98, 0xbc, 0xc1, 0x6d, 0xe3, 0x23, 0x74, 0x06,
|
0x9c, 0x0a, 0x00, 0xaa, 0x40, 0x91, 0xe0, 0x1e, 0x26, 0x6f, 0x71, 0xcb, 0xf8, 0x04, 0x9d, 0xc0,
|
||||||
0x9f, 0x5b, 0x76, 0xcb, 0x21, 0x04, 0xb7, 0x06, 0xd4, 0x21, 0x74, 0x68, 0xbf, 0xb6, 0x9d, 0x1f,
|
0x97, 0x96, 0xdd, 0x74, 0x08, 0xc1, 0xcd, 0x3e, 0x75, 0x08, 0x1d, 0xd8, 0x6f, 0x6c, 0xe7, 0x27,
|
||||||
0x6d, 0x7a, 0xd5, 0x7c, 0xdb, 0xc3, 0xf6, 0x80, 0xb6, 0xf1, 0xa0, 0x69, 0x75, 0xfb, 0x46, 0x06,
|
0x9b, 0x5e, 0x36, 0xde, 0x75, 0xb1, 0xdd, 0xa7, 0x2d, 0xdc, 0x6f, 0x58, 0x9d, 0x9e, 0x91, 0x41,
|
||||||
0x3d, 0x03, 0x73, 0xc5, 0x4c, 0xcc, 0xcd, 0x9e, 0x33, 0xb4, 0x07, 0xc6, 0x0e, 0xfa, 0x04, 0x8e,
|
0xcf, 0xc0, 0x5c, 0x30, 0x13, 0x73, 0xa3, 0xeb, 0x0c, 0xec, 0xbe, 0xb1, 0x81, 0x3e, 0x83, 0xc3,
|
||||||
0x3b, 0x96, 0xdd, 0xec, 0xd2, 0x15, 0xa7, 0xd5, 0x1d, 0xbc, 0xa1, 0xf8, 0xa7, 0x2b, 0x8b, 0xbc,
|
0xb6, 0x65, 0x37, 0x3a, 0x74, 0xc1, 0x69, 0x76, 0xfa, 0x6f, 0x29, 0xfe, 0xf9, 0xd2, 0x22, 0xef,
|
||||||
0x35, 0xb2, 0xdb, 0x08, 0x97, 0x83, 0x6e, 0x2b, 0xf1, 0x90, 0x43, 0x4f, 0xe1, 0xb1, 0x26, 0xe8,
|
0x8c, 0xec, 0x3a, 0xc2, 0x45, 0xbf, 0xd3, 0x4c, 0x3c, 0xe4, 0xd0, 0x53, 0x78, 0xa2, 0x09, 0x7a,
|
||||||
0x29, 0x74, 0xe0, 0x38, 0xb4, 0xef, 0x38, 0xb6, 0x91, 0x47, 0x8f, 0xa0, 0x62, 0xd9, 0x6f, 0x9a,
|
0x0a, 0xed, 0x3b, 0x0e, 0xed, 0x39, 0x8e, 0x6d, 0xe4, 0xd1, 0x0e, 0x54, 0x2d, 0xfb, 0x6d, 0xa3,
|
||||||
0x5d, 0xab, 0x4d, 0x09, 0x6e, 0x76, 0x7b, 0xc6, 0x2e, 0x3a, 0x80, 0xea, 0x26, 0xaf, 0x20, 0x5d,
|
0x63, 0xb5, 0x28, 0xc1, 0x8d, 0x4e, 0xd7, 0xd8, 0x44, 0xbb, 0xb0, 0xbd, 0xca, 0x2b, 0x48, 0x17,
|
||||||
0x24, 0x3c, 0xc7, 0xb6, 0x1c, 0x9b, 0xbe, 0xc1, 0xa4, 0x6f, 0x39, 0xb6, 0xb1, 0x87, 0x8e, 0x00,
|
0x09, 0xcf, 0xb1, 0x2d, 0xc7, 0xa6, 0x6f, 0x31, 0xe9, 0x59, 0x8e, 0x6d, 0x14, 0xd1, 0x3e, 0xa0,
|
||||||
0xad, 0x9b, 0x2e, 0x7b, 0xcd, 0x96, 0x51, 0x44, 0x8f, 0xe1, 0xd1, 0x3a, 0xfe, 0x1a, 0xbf, 0x35,
|
0x65, 0xd3, 0x45, 0xb7, 0xd1, 0x34, 0x4a, 0xe8, 0x09, 0xec, 0x2c, 0xe3, 0x6f, 0xf0, 0x3b, 0x03,
|
||||||
0x00, 0x99, 0x70, 0xa8, 0x03, 0xa3, 0x2f, 0x71, 0xd7, 0xf9, 0x91, 0xf6, 0x2c, 0xdb, 0xea, 0x0d,
|
0x90, 0x09, 0x7b, 0x7a, 0x63, 0xf4, 0x15, 0xee, 0x38, 0x3f, 0xd1, 0xae, 0x65, 0x5b, 0xdd, 0x41,
|
||||||
0x7b, 0x46, 0x09, 0x1d, 0x82, 0xd1, 0xc1, 0x98, 0x5a, 0x76, 0x7f, 0xd8, 0xe9, 0x58, 0x2d, 0x0b,
|
0xd7, 0x28, 0xa3, 0x3d, 0x30, 0xda, 0x18, 0x53, 0xcb, 0xee, 0x0d, 0xda, 0x6d, 0xab, 0x69, 0x61,
|
||||||
0xdb, 0x03, 0xa3, 0xac, 0x57, 0xde, 0xb6, 0xf1, 0x8a, 0x9c, 0xd0, 0xba, 0x6c, 0xda, 0x36, 0xee,
|
0xbb, 0x6f, 0x54, 0xf4, 0xca, 0xeb, 0x0e, 0x5e, 0x95, 0x13, 0x9a, 0x17, 0x0d, 0xdb, 0xc6, 0x1d,
|
||||||
0xd2, 0xb6, 0xd5, 0x6f, 0xbe, 0xec, 0xe2, 0xb6, 0xb1, 0x8f, 0x4e, 0xe0, 0xe9, 0x00, 0xf7, 0xae,
|
0xda, 0xb2, 0x7a, 0x8d, 0x57, 0x1d, 0xdc, 0x32, 0xb6, 0xd0, 0x11, 0x3c, 0xed, 0xe3, 0xee, 0xa5,
|
||||||
0x1c, 0xd2, 0x24, 0x6f, 0x69, 0x62, 0xef, 0x34, 0xad, 0xee, 0x90, 0x60, 0xa3, 0x8a, 0x3e, 0x85,
|
0x43, 0x1a, 0xe4, 0x1d, 0x4d, 0xec, 0xed, 0x86, 0xd5, 0x19, 0x10, 0x6c, 0x6c, 0xa3, 0xcf, 0xe1,
|
||||||
0x13, 0x82, 0x7f, 0x18, 0x5a, 0x04, 0xb7, 0xa9, 0xed, 0xb4, 0x31, 0xed, 0xe0, 0xe6, 0x60, 0x48,
|
0x88, 0xe0, 0x1f, 0x07, 0x16, 0xc1, 0x2d, 0x6a, 0x3b, 0x2d, 0x4c, 0xdb, 0xb8, 0xd1, 0x1f, 0x10,
|
||||||
0x30, 0xed, 0x59, 0xfd, 0xbe, 0x65, 0x7f, 0x6f, 0x18, 0xe8, 0x73, 0x38, 0x5d, 0x52, 0x96, 0x0e,
|
0x4c, 0xbb, 0x56, 0xaf, 0x67, 0xd9, 0x3f, 0x18, 0x06, 0xfa, 0x12, 0x8e, 0xe7, 0x94, 0xb9, 0x83,
|
||||||
0x36, 0x58, 0x8f, 0xe4, 0xfe, 0x92, 0x94, 0xda, 0xf8, 0xa7, 0x01, 0xbd, 0xc2, 0x98, 0x18, 0x08,
|
0x15, 0xd6, 0x8e, 0x3c, 0x5f, 0x92, 0x52, 0x1b, 0xff, 0xdc, 0xa7, 0x97, 0x18, 0x13, 0x03, 0xa1,
|
||||||
0xd5, 0xe0, 0x68, 0xb5, 0xbc, 0x5e, 0x20, 0x5e, 0xfb, 0x40, 0xda, 0xae, 0x30, 0xe9, 0x35, 0x6d,
|
0x1a, 0xec, 0x2f, 0x96, 0xd7, 0x0b, 0xc4, 0x6b, 0xef, 0x4a, 0xdb, 0x25, 0x26, 0xdd, 0x86, 0x2d,
|
||||||
0x99, 0xe0, 0x35, 0xdb, 0xa1, 0x0c, 0x7b, 0x65, 0xdb, 0x0c, 0xfb, 0x31, 0x3a, 0x84, 0x6a, 0xb2,
|
0x13, 0xbc, 0x64, 0xdb, 0x93, 0xdb, 0x5e, 0xd8, 0x56, 0xb7, 0xfd, 0x04, 0xed, 0xc1, 0x76, 0xb2,
|
||||||
0x5a, 0x02, 0xfe, 0xab, 0x80, 0x9e, 0x00, 0x1a, 0xda, 0x04, 0x37, 0xdb, 0x72, 0xf3, 0x4b, 0xc3,
|
0x5a, 0x02, 0xfe, 0xb3, 0x80, 0x0e, 0x00, 0x0d, 0x6c, 0x82, 0x1b, 0x2d, 0x79, 0xf8, 0xb9, 0xe1,
|
||||||
0xbf, 0x0b, 0xaf, 0x72, 0x7b, 0x3b, 0x46, 0xb6, 0xfe, 0xf7, 0x2c, 0x54, 0xd6, 0x6a, 0x0d, 0x3d,
|
0x5f, 0x85, 0xd7, 0xb9, 0xe2, 0x86, 0x91, 0xad, 0xff, 0x3d, 0x0b, 0xd5, 0xa5, 0x5a, 0x43, 0xcf,
|
||||||
0x83, 0x62, 0xe4, 0x4e, 0x7c, 0x26, 0xa4, 0x1a, 0x68, 0xa1, 0x58, 0x01, 0xea, 0xbd, 0x9b, 0x32,
|
0xa0, 0x14, 0xb9, 0x37, 0x3e, 0x13, 0x52, 0x0d, 0xb4, 0x50, 0x2c, 0x00, 0xf5, 0xde, 0x8d, 0x99,
|
||||||
0xd7, 0xd7, 0x0a, 0xa5, 0x15, 0xba, 0xa8, 0x10, 0xa5, 0x4f, 0x4f, 0xa0, 0x90, 0xbc, 0x97, 0x59,
|
0xeb, 0x6b, 0x85, 0xd2, 0x0a, 0x5d, 0x52, 0x88, 0xd2, 0xa7, 0x03, 0x28, 0x24, 0xef, 0x65, 0x56,
|
||||||
0x55, 0x97, 0xbb, 0x23, 0xfd, 0x4e, 0x3e, 0x83, 0xa2, 0x94, 0xc0, 0x48, 0xb0, 0xd9, 0x5c, 0x95,
|
0xd5, 0xe5, 0xe6, 0x50, 0xbf, 0x93, 0xcf, 0xa0, 0x24, 0x25, 0x30, 0x12, 0x6c, 0x32, 0x55, 0x25,
|
||||||
0x6c, 0x85, 0xac, 0x00, 0xf4, 0x19, 0x54, 0x66, 0x3c, 0x8a, 0xd8, 0x84, 0x53, 0x5d, 0x76, 0xa0,
|
0x5b, 0x25, 0x0b, 0x00, 0x7d, 0x01, 0xd5, 0x09, 0x8f, 0x22, 0x76, 0xc3, 0xa9, 0x2e, 0x3b, 0x50,
|
||||||
0x18, 0xe5, 0x18, 0xec, 0xa8, 0xea, 0xfb, 0x0c, 0x12, 0x19, 0x88, 0x49, 0x79, 0x4d, 0x8a, 0x41,
|
0x8c, 0x4a, 0x0c, 0xb6, 0x55, 0xf5, 0x7d, 0x01, 0x89, 0x0c, 0xc4, 0xa4, 0xbc, 0x26, 0xc5, 0xa0,
|
||||||
0x4d, 0xda, 0x54, 0x60, 0xc1, 0xe2, 0xea, 0x4e, 0x2b, 0xb0, 0x60, 0xe8, 0x05, 0x3c, 0xd2, 0x12,
|
0x26, 0xad, 0x2a, 0xb0, 0x60, 0x71, 0x75, 0xa7, 0x15, 0x58, 0x30, 0xf4, 0x02, 0x76, 0xb4, 0x84,
|
||||||
0xe2, 0xfa, 0xee, 0x6c, 0x31, 0xd3, 0x52, 0x52, 0x50, 0x21, 0x57, 0x95, 0x94, 0x68, 0x5c, 0x29,
|
0xb8, 0xbe, 0x3b, 0x99, 0x4d, 0xb4, 0x94, 0x14, 0xd4, 0x96, 0xb7, 0x95, 0x94, 0x68, 0x5c, 0x29,
|
||||||
0xca, 0x53, 0xd8, 0xbb, 0x66, 0x11, 0x97, 0xe2, 0x1f, 0x97, 0x7a, 0x41, 0x8e, 0x3b, 0x9c, 0x4b,
|
0xca, 0x53, 0x28, 0x5e, 0xb1, 0x88, 0x4b, 0xf1, 0x8f, 0x4b, 0xbd, 0x20, 0xc7, 0x6d, 0xce, 0xa5,
|
||||||
0x93, 0x7c, 0x12, 0x42, 0x29, 0x62, 0x45, 0x6d, 0xba, 0xe1, 0x9c, 0xc8, 0x73, 0x5c, 0xae, 0xc0,
|
0x49, 0x3e, 0x09, 0xa1, 0x14, 0xb1, 0x92, 0x36, 0x5d, 0x73, 0x4e, 0x64, 0x1c, 0xe7, 0x2b, 0xb0,
|
||||||
0xde, 0xad, 0x56, 0x28, 0xa5, 0x56, 0xd0, 0xb8, 0x5a, 0xe1, 0x05, 0x3c, 0xe2, 0xef, 0x44, 0xc8,
|
0x0f, 0x8b, 0x15, 0xca, 0xa9, 0x15, 0x34, 0xae, 0x56, 0x78, 0x01, 0x3b, 0xfc, 0x83, 0x08, 0x19,
|
||||||
0x68, 0x30, 0x67, 0x3f, 0x2f, 0x38, 0x1d, 0x33, 0xc1, 0xcc, 0xb2, 0x3a, 0xdc, 0xaa, 0x32, 0x38,
|
0x0d, 0xa6, 0xec, 0xfd, 0x8c, 0xd3, 0x11, 0x13, 0xcc, 0xac, 0xa8, 0xe0, 0x6e, 0x2b, 0x83, 0xa3,
|
||||||
0x0a, 0x6f, 0x33, 0xc1, 0xea, 0xcf, 0xa0, 0x46, 0x78, 0xc4, 0x45, 0xcf, 0x8d, 0x22, 0x37, 0xf0,
|
0xf0, 0x16, 0x13, 0xac, 0xfe, 0x0c, 0x6a, 0x84, 0x47, 0x5c, 0x74, 0xdd, 0x28, 0x72, 0x03, 0xbf,
|
||||||
0x5b, 0x81, 0x2f, 0xc2, 0xc0, 0x8b, 0xdf, 0x90, 0xfa, 0x09, 0x1c, 0x6f, 0xb5, 0xea, 0x47, 0x40,
|
0x19, 0xf8, 0x22, 0x0c, 0xbc, 0xf8, 0x0d, 0xa9, 0x1f, 0xc1, 0xe1, 0x5a, 0xab, 0x7e, 0x04, 0xe4,
|
||||||
0x4e, 0xfe, 0x61, 0xc1, 0xc3, 0xfb, 0xed, 0x93, 0xef, 0xe1, 0x78, 0xab, 0x35, 0x7e, 0x41, 0xbe,
|
0xe4, 0x1f, 0x67, 0x3c, 0xbc, 0x5f, 0x3f, 0xf9, 0x1e, 0x0e, 0xd7, 0x5a, 0xe3, 0x17, 0xe4, 0x6b,
|
||||||
0x84, 0xbc, 0x1f, 0x8c, 0x79, 0x64, 0x66, 0x54, 0x57, 0x72, 0x94, 0x92, 0x6b, 0x3b, 0x18, 0xf3,
|
0xc8, 0xfb, 0xc1, 0x88, 0x47, 0x66, 0x46, 0x75, 0x25, 0xfb, 0x29, 0xb9, 0xb6, 0x83, 0x11, 0xbf,
|
||||||
0x4b, 0x37, 0x12, 0x41, 0x78, 0x4f, 0x34, 0x49, 0xb2, 0xe7, 0xcc, 0x0d, 0x23, 0x73, 0xe7, 0x01,
|
0x70, 0x23, 0x11, 0x84, 0xf7, 0x44, 0x93, 0x24, 0x7b, 0xca, 0xdc, 0x30, 0x32, 0x37, 0x1e, 0xb0,
|
||||||
0xfb, 0x8a, 0xb9, 0xe1, 0x92, 0xad, 0x48, 0xf5, 0x3f, 0x67, 0xa0, 0x94, 0x72, 0x82, 0x8e, 0x60,
|
0x2f, 0x99, 0x1b, 0xce, 0xd9, 0x8a, 0x54, 0xff, 0x73, 0x06, 0xca, 0x29, 0x27, 0x68, 0x1f, 0x36,
|
||||||
0x77, 0xbe, 0xb8, 0x4e, 0x1a, 0x96, 0x32, 0x89, 0x47, 0xe8, 0x39, 0xec, 0x7b, 0x2c, 0x12, 0x54,
|
0xa7, 0xb3, 0xab, 0xa4, 0x61, 0xa9, 0x90, 0x78, 0x84, 0x9e, 0xc3, 0x96, 0xc7, 0x22, 0x41, 0xa5,
|
||||||
0x6a, 0x2d, 0x95, 0x29, 0x8d, 0x1f, 0xd8, 0x0d, 0x14, 0x9d, 0x03, 0x0a, 0xc4, 0x94, 0x87, 0x34,
|
0xd6, 0x52, 0x99, 0xd2, 0xf8, 0x81, 0x5d, 0x41, 0xd1, 0x29, 0xa0, 0x40, 0x8c, 0x79, 0x48, 0xa3,
|
||||||
0x5a, 0x8c, 0x46, 0x3c, 0x8a, 0xe8, 0x3c, 0x0c, 0xae, 0xd5, 0x9d, 0xdc, 0x21, 0x5b, 0x2c, 0xaf,
|
0xd9, 0x70, 0xc8, 0xa3, 0x88, 0x4e, 0xc3, 0xe0, 0x4a, 0xdd, 0xc9, 0x0d, 0xb2, 0xc6, 0xf2, 0x3a,
|
||||||
0x72, 0x7b, 0x39, 0x23, 0x5f, 0xff, 0x47, 0x06, 0x4a, 0xa9, 0xe0, 0xe4, 0xad, 0x95, 0x9b, 0xa1,
|
0x57, 0xcc, 0x19, 0xf9, 0xfa, 0xbf, 0x33, 0x50, 0x4e, 0x6d, 0x4e, 0xde, 0x5a, 0x79, 0x18, 0x7a,
|
||||||
0x37, 0x61, 0x30, 0x4b, 0x6a, 0x61, 0x09, 0x20, 0x13, 0x0a, 0x6a, 0x20, 0x82, 0xb8, 0x10, 0x92,
|
0x1d, 0x06, 0x93, 0xa4, 0x16, 0xe6, 0x00, 0x32, 0xa1, 0xa0, 0x06, 0x22, 0x88, 0x0b, 0x21, 0x19,
|
||||||
0xe1, 0x96, 0x28, 0xb3, 0x5b, 0xa3, 0x6c, 0xc0, 0xe1, 0xcc, 0xf5, 0xe9, 0x9c, 0xfb, 0xcc, 0x73,
|
0x2e, 0xdf, 0xf6, 0xac, 0xda, 0x60, 0xea, 0xb6, 0x9f, 0xc3, 0xde, 0xc4, 0xf5, 0xe9, 0x94, 0xfb,
|
||||||
0xff, 0xc8, 0x69, 0xd2, 0x93, 0xe4, 0x14, 0x7b, 0xab, 0x0d, 0xd5, 0xa1, 0xbc, 0xb6, 0xa7, 0xbc,
|
0xcc, 0x73, 0xff, 0xc8, 0x69, 0xd2, 0x89, 0xe4, 0x14, 0x71, 0xad, 0x0d, 0xd5, 0xa1, 0xb2, 0x74,
|
||||||
0xda, 0xd3, 0x1a, 0xf6, 0xe2, 0xaf, 0x19, 0x28, 0xa7, 0xbb, 0x2b, 0x54, 0x81, 0xa2, 0x65, 0xd3,
|
0x92, 0xbc, 0x3a, 0xc9, 0x12, 0x86, 0x5e, 0xc2, 0x81, 0x8a, 0x02, 0x13, 0x82, 0x4f, 0xa6, 0x22,
|
||||||
0x4e, 0xd7, 0xfa, 0xfe, 0x72, 0x60, 0x7c, 0x24, 0x87, 0xfd, 0x61, 0xab, 0x85, 0x71, 0x1b, 0xb7,
|
0x39, 0xe0, 0xf5, 0xcc, 0x53, 0x35, 0x50, 0x24, 0x8f, 0x99, 0x5f, 0xfc, 0x35, 0x03, 0x95, 0x74,
|
||||||
0x8d, 0x0c, 0x42, 0xb0, 0x2f, 0x85, 0x01, 0xb7, 0xe9, 0xc0, 0xea, 0x61, 0x67, 0x28, 0xdf, 0x94,
|
0x37, 0x86, 0xaa, 0x50, 0xb2, 0x6c, 0xda, 0xee, 0x58, 0x3f, 0x5c, 0xf4, 0x8d, 0x4f, 0xe4, 0xb0,
|
||||||
0x03, 0xa8, 0xc6, 0x98, 0xed, 0x50, 0xe2, 0x0c, 0x07, 0xd8, 0xc8, 0x22, 0x03, 0xca, 0x31, 0x88,
|
0x37, 0x68, 0x36, 0x31, 0x6e, 0xe1, 0x96, 0x91, 0x41, 0x08, 0xb6, 0xa4, 0x90, 0xe0, 0x16, 0xed,
|
||||||
0x09, 0x71, 0x88, 0x91, 0x93, 0x42, 0x18, 0x23, 0x0f, 0xdf, 0xa7, 0xe4, 0xf9, 0xca, 0x37, 0xfe,
|
0x5b, 0x5d, 0xec, 0x0c, 0xe4, 0x1b, 0xb4, 0x0b, 0xdb, 0x31, 0x66, 0x3b, 0x94, 0x38, 0x83, 0x3e,
|
||||||
0x92, 0x83, 0x5d, 0xd5, 0x8d, 0x84, 0xe8, 0x12, 0x4a, 0xa9, 0x16, 0x16, 0x9d, 0x7c, 0xb0, 0xb5,
|
0x36, 0xb2, 0xc8, 0x80, 0x4a, 0x0c, 0x62, 0x42, 0x1c, 0x62, 0xe4, 0xa4, 0x70, 0xc6, 0xc8, 0xc3,
|
||||||
0xad, 0x99, 0xdb, 0xdb, 0xc5, 0x45, 0xf4, 0x55, 0x06, 0xbd, 0x82, 0x72, 0xba, 0x49, 0x45, 0xe9,
|
0xf7, 0x2c, 0x79, 0xee, 0xf2, 0xe7, 0x7f, 0xc9, 0xc1, 0xa6, 0xea, 0x5e, 0x42, 0x74, 0x01, 0xe5,
|
||||||
0xe6, 0x63, 0x4b, 0xf7, 0xfa, 0x41, 0x5f, 0xaf, 0xc1, 0xc0, 0x91, 0x70, 0x67, 0xb2, 0xd9, 0x88,
|
0x54, 0xcb, 0x8b, 0x8e, 0x3e, 0xda, 0x0a, 0xd7, 0xcc, 0xf5, 0xed, 0xe5, 0x2c, 0xfa, 0x26, 0x83,
|
||||||
0xdb, 0x3f, 0x54, 0x4b, 0xf1, 0x37, 0x7a, 0xca, 0xda, 0xf1, 0x56, 0x5b, 0x5c, 0x27, 0x5d, 0xbd,
|
0x5e, 0x43, 0x25, 0xdd, 0xd4, 0xa2, 0x74, 0xb3, 0xb2, 0xa6, 0xdb, 0xfd, 0xa8, 0xaf, 0x37, 0x60,
|
||||||
0xc5, 0xb8, 0x01, 0x7b, 0xb0, 0xc5, 0xf5, 0xae, 0xaf, 0xf6, 0xf1, 0xfb, 0xcc, 0xb1, 0xb7, 0x31,
|
0xe0, 0x48, 0xb8, 0x13, 0xd9, 0x9c, 0xc4, 0xed, 0x22, 0xaa, 0xa5, 0xf8, 0x2b, 0x3d, 0x68, 0xed,
|
||||||
0x1c, 0x6c, 0xa9, 0x68, 0xf4, 0x8b, 0x74, 0x04, 0xef, 0xd5, 0x83, 0xda, 0xf3, 0xff, 0x45, 0x5b,
|
0x70, 0xad, 0x2d, 0xae, 0xab, 0x8e, 0x3e, 0x62, 0xdc, 0xb0, 0x3d, 0x38, 0xe2, 0x72, 0x97, 0x58,
|
||||||
0xad, 0xb2, 0xa5, 0xf4, 0xd7, 0x56, 0x79, 0xbf, 0x70, 0xac, 0xad, 0xf2, 0x01, 0x05, 0x79, 0xf9,
|
0xfb, 0xf4, 0x31, 0x73, 0xec, 0x6d, 0x04, 0xbb, 0x6b, 0x14, 0x00, 0xfd, 0x22, 0xbd, 0x83, 0x47,
|
||||||
0xab, 0xdf, 0x5d, 0x4c, 0x5c, 0x31, 0x5d, 0x5c, 0x9f, 0x8f, 0x82, 0xd9, 0x85, 0xe7, 0x4e, 0xa6,
|
0xf5, 0xa3, 0xf6, 0xfc, 0x7f, 0xd1, 0x16, 0xab, 0xac, 0x91, 0x8a, 0xa5, 0x55, 0x1e, 0x17, 0x9a,
|
||||||
0xc2, 0x77, 0xfd, 0x89, 0xcf, 0xc5, 0x1f, 0x82, 0xf0, 0xf6, 0xc2, 0xf3, 0xc7, 0x17, 0xaa, 0xa1,
|
0xa5, 0x55, 0x3e, 0xa2, 0x38, 0xaf, 0x7e, 0xf5, 0xbb, 0xb3, 0x1b, 0x57, 0x8c, 0x67, 0x57, 0xa7,
|
||||||
0xbd, 0x58, 0xba, 0xbb, 0xde, 0x55, 0xff, 0x48, 0x7f, 0xfd, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff,
|
0xc3, 0x60, 0x72, 0xe6, 0xb9, 0x37, 0x63, 0xe1, 0xbb, 0xfe, 0x8d, 0xcf, 0xc5, 0x1f, 0x82, 0xf0,
|
||||||
0x54, 0xde, 0xd4, 0xfd, 0xc1, 0x0e, 0x00, 0x00,
|
0xf6, 0xcc, 0xf3, 0x47, 0x67, 0xaa, 0x01, 0x3e, 0x9b, 0xbb, 0xbb, 0xda, 0x54, 0xff, 0x60, 0x7f,
|
||||||
|
0xfd, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5, 0x11, 0xe6, 0x51, 0xf1, 0x0e, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -364,14 +364,17 @@ message PairHistory {
|
|||||||
/// The destination node pubkey of the pair.
|
/// The destination node pubkey of the pair.
|
||||||
bytes node_to = 2 [json_name="node_to"];
|
bytes node_to = 2 [json_name="node_to"];
|
||||||
|
|
||||||
/// Time stamp of last failure.
|
/// Time stamp of last result.
|
||||||
int64 last_fail_time = 3 [json_name = "last_fail_time"];
|
int64 timestamp = 3 [json_name = "timestamp"];
|
||||||
|
|
||||||
/// Minimum penalization amount.
|
/// 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.
|
/// Estimation of success probability for this pair.
|
||||||
float success_prob = 5 [json_name = "success_prob"];
|
float success_prob = 5 [json_name = "success_prob"];
|
||||||
|
|
||||||
|
/// Whether the last payment attempt through this pair was successful.
|
||||||
|
bool last_attempt_successful = 6 [json_name = "last_attempt_successful"];
|
||||||
}
|
}
|
||||||
|
|
||||||
service Router {
|
service Router {
|
||||||
|
@ -475,11 +475,12 @@ func (s *Server) QueryMissionControl(ctx context.Context,
|
|||||||
rpcPair := PairHistory{
|
rpcPair := PairHistory{
|
||||||
NodeFrom: pair.Pair.From[:],
|
NodeFrom: pair.Pair.From[:],
|
||||||
NodeTo: pair.Pair.To[:],
|
NodeTo: pair.Pair.To[:],
|
||||||
LastFailTime: pair.LastFail.Unix(),
|
Timestamp: pair.Timestamp.Unix(),
|
||||||
MinPenalizeAmtSat: int64(
|
MinPenalizeAmtSat: int64(
|
||||||
pair.MinPenalizeAmt.ToSatoshis(),
|
pair.MinPenalizeAmt.ToSatoshis(),
|
||||||
),
|
),
|
||||||
SuccessProb: float32(pair.SuccessProb),
|
SuccessProb: float32(pair.SuccessProb),
|
||||||
|
LastAttemptSuccessful: pair.LastAttemptSuccessful,
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcPairs = append(rpcPairs, &rpcPair)
|
rpcPairs = append(rpcPairs, &rpcPair)
|
||||||
|
@ -43,6 +43,10 @@ const (
|
|||||||
|
|
||||||
// DefaultMaxMcHistory is the default maximum history size.
|
// DefaultMaxMcHistory is the default maximum history size.
|
||||||
DefaultMaxMcHistory = 1000
|
DefaultMaxMcHistory = 1000
|
||||||
|
|
||||||
|
// prevSuccessProbability is the assumed probability for node pairs that
|
||||||
|
// successfully relayed the previous attempt.
|
||||||
|
prevSuccessProbability = 0.95
|
||||||
)
|
)
|
||||||
|
|
||||||
// MissionControl contains state which summarizes the past attempts of HTLC
|
// MissionControl contains state which summarizes the past attempts of HTLC
|
||||||
@ -55,8 +59,8 @@ 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 {
|
||||||
// lastPairFailure tracks the last payment failure per node pair.
|
// lastPairResult tracks the last payment result per node pair.
|
||||||
lastPairFailure map[DirectedNodePair]pairFailure
|
lastPairResult map[DirectedNodePair]timedPairResult
|
||||||
|
|
||||||
// lastNodeFailure tracks the last node level failure per node.
|
// lastNodeFailure tracks the last node level failure per node.
|
||||||
lastNodeFailure map[route.Vertex]time.Time
|
lastNodeFailure map[route.Vertex]time.Time
|
||||||
@ -97,14 +101,12 @@ type MissionControlConfig struct {
|
|||||||
MaxMcHistory int
|
MaxMcHistory int
|
||||||
}
|
}
|
||||||
|
|
||||||
// pairFailure describes a payment failure for a node pair.
|
// timedPairResult describes a timestamped pair result.
|
||||||
type pairFailure struct {
|
type timedPairResult struct {
|
||||||
// timestamp is the time when this failure result was obtained.
|
// timestamp is the time when this result was obtained.
|
||||||
timestamp time.Time
|
timestamp time.Time
|
||||||
|
|
||||||
// minPenalizeAmt is the minimum amount for which to take this failure
|
pairResult
|
||||||
// into account.
|
|
||||||
minPenalizeAmt lnwire.MilliSatoshi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MissionControlSnapshot contains a snapshot of the current state of mission
|
// MissionControlSnapshot contains a snapshot of the current state of mission
|
||||||
@ -138,8 +140,8 @@ type MissionControlPairSnapshot struct {
|
|||||||
// Pair is the node pair of which the state is described.
|
// Pair is the node pair of which the state is described.
|
||||||
Pair DirectedNodePair
|
Pair DirectedNodePair
|
||||||
|
|
||||||
// LastFail is the time of last failure.
|
// Timestamp is the time of last result.
|
||||||
LastFail time.Time
|
Timestamp time.Time
|
||||||
|
|
||||||
// MinPenalizeAmt is the minimum amount for which the channel will be
|
// MinPenalizeAmt is the minimum amount for which the channel will be
|
||||||
// penalized.
|
// penalized.
|
||||||
@ -147,6 +149,10 @@ type MissionControlPairSnapshot struct {
|
|||||||
|
|
||||||
// SuccessProb is the success probability estimation for this channel.
|
// SuccessProb is the success probability estimation for this channel.
|
||||||
SuccessProb float64
|
SuccessProb float64
|
||||||
|
|
||||||
|
// LastAttemptSuccessful indicates whether the last payment attempt
|
||||||
|
// through this pair was successful.
|
||||||
|
LastAttemptSuccessful bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// paymentResult is the information that becomes available when a payment
|
// paymentResult is the information that becomes available when a payment
|
||||||
@ -174,7 +180,7 @@ func NewMissionControl(db *bbolt.DB, cfg *MissionControlConfig) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
mc := &MissionControl{
|
mc := &MissionControl{
|
||||||
lastPairFailure: make(map[DirectedNodePair]pairFailure),
|
lastPairResult: make(map[DirectedNodePair]timedPairResult),
|
||||||
lastNodeFailure: make(map[route.Vertex]time.Time),
|
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,
|
||||||
@ -220,7 +226,7 @@ func (m *MissionControl) ResetHistory() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.lastPairFailure = make(map[DirectedNodePair]pairFailure)
|
m.lastPairResult = make(map[DirectedNodePair]timedPairResult)
|
||||||
m.lastNodeFailure = make(map[route.Vertex]time.Time)
|
m.lastNodeFailure = make(map[route.Vertex]time.Time)
|
||||||
m.lastSecondChance = make(map[DirectedNodePair]time.Time)
|
m.lastSecondChance = make(map[DirectedNodePair]time.Time)
|
||||||
|
|
||||||
@ -271,12 +277,16 @@ func (m *MissionControl) getPairProbability(fromNode,
|
|||||||
|
|
||||||
// Retrieve the last pair outcome.
|
// Retrieve the last pair outcome.
|
||||||
pair := NewDirectedNodePair(fromNode, toNode)
|
pair := NewDirectedNodePair(fromNode, toNode)
|
||||||
lastPairResult, ok := m.lastPairFailure[pair]
|
lastPairResult, ok := m.lastPairResult[pair]
|
||||||
|
|
||||||
// Only look at the last pair outcome if it happened after the last node
|
// 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
|
// level failure. Otherwise the node level failure is the most recent
|
||||||
// and used as the basis for calculation of the probability.
|
// and used as the basis for calculation of the probability.
|
||||||
if ok && lastPairResult.timestamp.After(lastFail) {
|
if ok && lastPairResult.timestamp.After(lastFail) {
|
||||||
|
if lastPairResult.success {
|
||||||
|
return prevSuccessProbability
|
||||||
|
}
|
||||||
|
|
||||||
// Take into account a minimum penalize amount. For balance
|
// Take into account a minimum penalize amount. For balance
|
||||||
// errors, a failure may be reported with such a minimum to
|
// errors, a failure may be reported with such a minimum to
|
||||||
// prevent too aggresive penalization. We only take into account
|
// prevent too aggresive penalization. We only take into account
|
||||||
@ -330,7 +340,7 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
|||||||
|
|
||||||
log.Debugf("Requesting history snapshot from mission control: "+
|
log.Debugf("Requesting history snapshot from mission control: "+
|
||||||
"node_failure_count=%v, pair_result_count=%v",
|
"node_failure_count=%v, pair_result_count=%v",
|
||||||
len(m.lastNodeFailure), len(m.lastPairFailure))
|
len(m.lastNodeFailure), len(m.lastPairResult))
|
||||||
|
|
||||||
nodes := make([]MissionControlNodeSnapshot, 0, len(m.lastNodeFailure))
|
nodes := make([]MissionControlNodeSnapshot, 0, len(m.lastNodeFailure))
|
||||||
for v, h := range m.lastNodeFailure {
|
for v, h := range m.lastNodeFailure {
|
||||||
@ -343,9 +353,9 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairFailure))
|
pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairResult))
|
||||||
|
|
||||||
for v, h := range m.lastPairFailure {
|
for v, h := range m.lastPairResult {
|
||||||
// Show probability assuming amount meets min
|
// Show probability assuming amount meets min
|
||||||
// penalization amount.
|
// penalization amount.
|
||||||
prob := m.getPairProbability(v.From, v.To, h.minPenalizeAmt)
|
prob := m.getPairProbability(v.From, v.To, h.minPenalizeAmt)
|
||||||
@ -353,8 +363,9 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
|||||||
pair := MissionControlPairSnapshot{
|
pair := MissionControlPairSnapshot{
|
||||||
Pair: v,
|
Pair: v,
|
||||||
MinPenalizeAmt: h.minPenalizeAmt,
|
MinPenalizeAmt: h.minPenalizeAmt,
|
||||||
LastFail: h.timestamp,
|
Timestamp: h.timestamp,
|
||||||
SuccessProb: prob,
|
SuccessProb: prob,
|
||||||
|
LastAttemptSuccessful: h.success,
|
||||||
}
|
}
|
||||||
|
|
||||||
pairs = append(pairs, pair)
|
pairs = append(pairs, pair)
|
||||||
@ -379,7 +390,6 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
|
|||||||
|
|
||||||
timestamp := m.now()
|
timestamp := m.now()
|
||||||
|
|
||||||
// TODO(joostjager): Use actual payment initiation time for timeFwd.
|
|
||||||
result := &paymentResult{
|
result := &paymentResult{
|
||||||
success: false,
|
success: false,
|
||||||
timeFwd: timestamp,
|
timeFwd: timestamp,
|
||||||
@ -390,6 +400,33 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
|
|||||||
route: rt,
|
route: rt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return m.processPaymentResult(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportPaymentSuccess reports a successful payment to mission control as input
|
||||||
|
// for future probability estimates.
|
||||||
|
func (m *MissionControl) ReportPaymentSuccess(paymentID uint64,
|
||||||
|
rt *route.Route) error {
|
||||||
|
|
||||||
|
timestamp := m.now()
|
||||||
|
|
||||||
|
result := &paymentResult{
|
||||||
|
timeFwd: timestamp,
|
||||||
|
timeReply: timestamp,
|
||||||
|
id: paymentID,
|
||||||
|
success: true,
|
||||||
|
route: rt,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := m.processPaymentResult(result)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// processPaymentResult stores a payment result in the mission control store and
|
||||||
|
// updates mission control's in-memory state.
|
||||||
|
func (m *MissionControl) processPaymentResult(result *paymentResult) (
|
||||||
|
*channeldb.FailureReason, error) {
|
||||||
|
|
||||||
// Store complete result in database.
|
// Store complete result in database.
|
||||||
if err := m.store.AddResult(result); err != nil {
|
if err := m.store.AddResult(result); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -409,7 +446,8 @@ func (m *MissionControl) applyPaymentResult(
|
|||||||
|
|
||||||
// Interpret result.
|
// Interpret result.
|
||||||
i := interpretResult(
|
i := interpretResult(
|
||||||
result.route, result.failureSourceIdx, result.failure,
|
result.route, result.success, result.failureSourceIdx,
|
||||||
|
result.failure,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update mission control state using the interpretation.
|
// Update mission control state using the interpretation.
|
||||||
@ -432,13 +470,19 @@ func (m *MissionControl) applyPaymentResult(
|
|||||||
m.lastNodeFailure[*i.nodeFailure] = result.timeReply
|
m.lastNodeFailure[*i.nodeFailure] = result.timeReply
|
||||||
}
|
}
|
||||||
|
|
||||||
for pair, minPenalizeAmt := range i.pairResults {
|
for pair, pairResult := range i.pairResults {
|
||||||
log.Debugf("Reporting pair failure to Mission Control: "+
|
if pairResult.success {
|
||||||
"pair=%v, minPenalizeAmt=%v", pair, minPenalizeAmt)
|
log.Debugf("Reporting pair success to Mission "+
|
||||||
|
"Control: pair=%v", pair)
|
||||||
|
} else {
|
||||||
|
log.Debugf("Reporting pair failure to Mission "+
|
||||||
|
"Control: pair=%v, minPenalizeAmt=%v",
|
||||||
|
pair, pairResult.minPenalizeAmt)
|
||||||
|
}
|
||||||
|
|
||||||
m.lastPairFailure[pair] = pairFailure{
|
m.lastPairResult[pair] = timedPairResult{
|
||||||
minPenalizeAmt: minPenalizeAmt,
|
|
||||||
timestamp: result.timeReply,
|
timestamp: result.timeReply,
|
||||||
|
pairResult: pairResult,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +114,16 @@ func (ctx *mcTestContext) reportFailure(amt lnwire.MilliSatoshi,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reportSuccess reports a success by using a test route.
|
||||||
|
func (ctx *mcTestContext) reportSuccess() {
|
||||||
|
err := ctx.mc.ReportPaymentSuccess(ctx.pid, mcTestRoute)
|
||||||
|
if err != nil {
|
||||||
|
ctx.t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.pid++
|
||||||
|
}
|
||||||
|
|
||||||
// TestMissionControl tests mission control probability estimation.
|
// TestMissionControl tests mission control probability estimation.
|
||||||
func TestMissionControl(t *testing.T) {
|
func TestMissionControl(t *testing.T) {
|
||||||
ctx := createMcTestContext(t)
|
ctx := createMcTestContext(t)
|
||||||
@ -164,9 +174,12 @@ func TestMissionControl(t *testing.T) {
|
|||||||
len(history.Nodes))
|
len(history.Nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(history.Pairs) != 1 {
|
if len(history.Pairs) != 2 {
|
||||||
t.Fatal("unexpected number of channels")
|
t.Fatalf("expected 2 pairs, but got %v", len(history.Pairs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test reporting a success.
|
||||||
|
ctx.reportSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestMissionControlChannelUpdate tests that the first channel update is not
|
// TestMissionControlChannelUpdate tests that the first channel update is not
|
||||||
|
@ -105,6 +105,12 @@ func (m *mockMissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockMissionControl) ReportPaymentSuccess(paymentID uint64,
|
||||||
|
rt *route.Route) error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,
|
func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,
|
||||||
amt lnwire.MilliSatoshi) float64 {
|
amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ var (
|
|||||||
|
|
||||||
// DefaultAprioriHopProbability is the default a priori probability for
|
// DefaultAprioriHopProbability is the default a priori probability for
|
||||||
// a hop.
|
// a hop.
|
||||||
DefaultAprioriHopProbability = float64(0.95)
|
DefaultAprioriHopProbability = float64(0.6)
|
||||||
)
|
)
|
||||||
|
|
||||||
// edgePolicyWithSource is a helper struct to keep track of the source node
|
// edgePolicyWithSource is a helper struct to keep track of the source node
|
||||||
|
@ -161,6 +161,15 @@ func (p *paymentLifecycle) resumePayment() ([32]byte, *route.Route, error) {
|
|||||||
log.Debugf("Payment %x succeeded with pid=%v",
|
log.Debugf("Payment %x succeeded with pid=%v",
|
||||||
p.payment.PaymentHash, p.attempt.PaymentID)
|
p.payment.PaymentHash, p.attempt.PaymentID)
|
||||||
|
|
||||||
|
// Report success to mission control.
|
||||||
|
err = p.router.cfg.MissionControl.ReportPaymentSuccess(
|
||||||
|
p.attempt.PaymentID, &p.attempt.Route,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error reporting payment success to mc: %v",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
// In case of success we atomically store the db payment and
|
// In case of success we atomically store the db payment and
|
||||||
// move the payment to the success state.
|
// move the payment to the success state.
|
||||||
err = p.router.cfg.Control.Success(p.payment.PaymentHash, result.Preimage)
|
err = p.router.cfg.Control.Success(p.payment.PaymentHash, result.Preimage)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
@ -12,17 +14,36 @@ var (
|
|||||||
reasonIncorrectDetails = channeldb.FailureReasonIncorrectPaymentDetails
|
reasonIncorrectDetails = channeldb.FailureReasonIncorrectPaymentDetails
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// pairResult contains the result of the interpretation of a payment attempt for
|
||||||
|
// a specific node pair.
|
||||||
|
type pairResult struct {
|
||||||
|
// minPenalizeAmt is the minimum amount for which a penalty should be
|
||||||
|
// applied based on this result. Only applies to fail results.
|
||||||
|
minPenalizeAmt lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// success indicates whether the payment attempt was successful through
|
||||||
|
// this pair.
|
||||||
|
success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the human-readable representation of a pair result.
|
||||||
|
func (p pairResult) String() string {
|
||||||
|
if p.success {
|
||||||
|
return "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("failed (minPenalizeAmt=%v)", p.minPenalizeAmt)
|
||||||
|
}
|
||||||
|
|
||||||
// interpretedResult contains the result of the interpretation of a payment
|
// interpretedResult contains the result of the interpretation of a payment
|
||||||
// outcome.
|
// attempt.
|
||||||
type interpretedResult struct {
|
type interpretedResult struct {
|
||||||
// nodeFailure points to a node pubkey if all channels of that node are
|
// nodeFailure points to a node pubkey if all channels of that node are
|
||||||
// responsible for the result.
|
// responsible for the result.
|
||||||
nodeFailure *route.Vertex
|
nodeFailure *route.Vertex
|
||||||
|
|
||||||
// pairResults contains a map of node pairs that could be responsible
|
// pairResults contains a map of node pairs for which we have a result.
|
||||||
// for the failure. The map values are the minimum amounts for which a
|
pairResults map[DirectedNodePair]pairResult
|
||||||
// future penalty should be applied.
|
|
||||||
pairResults map[DirectedNodePair]lnwire.MilliSatoshi
|
|
||||||
|
|
||||||
// finalFailureReason is set to a non-nil value if it makes no more
|
// finalFailureReason is set to a non-nil value if it makes no more
|
||||||
// sense to start another payment attempt. It will contain the reason
|
// sense to start another payment attempt. It will contain the reason
|
||||||
@ -37,18 +58,28 @@ type interpretedResult struct {
|
|||||||
|
|
||||||
// interpretResult interprets a payment outcome and returns an object that
|
// interpretResult interprets a payment outcome and returns an object that
|
||||||
// contains information required to update mission control.
|
// contains information required to update mission control.
|
||||||
func interpretResult(rt *route.Route, failureSrcIdx *int,
|
func interpretResult(rt *route.Route, success bool, failureSrcIdx *int,
|
||||||
failure lnwire.FailureMessage) *interpretedResult {
|
failure lnwire.FailureMessage) *interpretedResult {
|
||||||
|
|
||||||
i := &interpretedResult{
|
i := &interpretedResult{
|
||||||
pairResults: make(map[DirectedNodePair]lnwire.MilliSatoshi),
|
pairResults: make(map[DirectedNodePair]pairResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if success {
|
||||||
|
i.processSuccess(rt)
|
||||||
|
} else {
|
||||||
i.processFail(rt, failureSrcIdx, failure)
|
i.processFail(rt, failureSrcIdx, failure)
|
||||||
|
}
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processSuccess processes a successful payment attempt.
|
||||||
|
func (i *interpretedResult) processSuccess(route *route.Route) {
|
||||||
|
// For successes, all nodes must have acted in the right way. Therefore
|
||||||
|
// we mark all of them with a success result.
|
||||||
|
i.successPairRange(route, 0, len(route.Hops)-1)
|
||||||
|
}
|
||||||
|
|
||||||
// processFail processes a failed payment attempt.
|
// processFail processes a failed payment attempt.
|
||||||
func (i *interpretedResult) processFail(
|
func (i *interpretedResult) processFail(
|
||||||
rt *route.Route, errSourceIdx *int,
|
rt *route.Route, errSourceIdx *int,
|
||||||
@ -141,10 +172,20 @@ func (i *interpretedResult) processPaymentOutcomeFinal(
|
|||||||
// from its predecessor.
|
// from its predecessor.
|
||||||
i.failPair(route, n-1)
|
i.failPair(route, n-1)
|
||||||
|
|
||||||
|
// The other hops relayed corectly, so assign those pairs a
|
||||||
|
// success result.
|
||||||
|
if n > 2 {
|
||||||
|
i.successPairRange(route, 0, n-2)
|
||||||
|
}
|
||||||
|
|
||||||
// We are using wrong payment hash or amount, fail the payment.
|
// We are using wrong payment hash or amount, fail the payment.
|
||||||
case *lnwire.FailIncorrectPaymentAmount,
|
case *lnwire.FailIncorrectPaymentAmount,
|
||||||
*lnwire.FailIncorrectDetails:
|
*lnwire.FailIncorrectDetails:
|
||||||
|
|
||||||
|
// Assign all pairs a success result, as the payment reached the
|
||||||
|
// destination correctly.
|
||||||
|
i.successPairRange(route, 0, n-1)
|
||||||
|
|
||||||
i.finalFailureReason = &reasonIncorrectDetails
|
i.finalFailureReason = &reasonIncorrectDetails
|
||||||
|
|
||||||
// The HTLC that was extended to the final hop expires too soon. Fail
|
// The HTLC that was extended to the final hop expires too soon. Fail
|
||||||
@ -162,6 +203,12 @@ func (i *interpretedResult) processPaymentOutcomeFinal(
|
|||||||
// final hop. They indicate that something is wrong at the
|
// final hop. They indicate that something is wrong at the
|
||||||
// recipient, so we do apply a penalty.
|
// recipient, so we do apply a penalty.
|
||||||
i.failNode(route, n)
|
i.failNode(route, n)
|
||||||
|
|
||||||
|
// Other channels in the route forwarded correctly.
|
||||||
|
if n > 2 {
|
||||||
|
i.successPairRange(route, 0, n-2)
|
||||||
|
}
|
||||||
|
|
||||||
i.finalFailureReason = &reasonError
|
i.finalFailureReason = &reasonError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,6 +229,10 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(
|
|||||||
i.failPairBalance(
|
i.failPairBalance(
|
||||||
route, errorSourceIdx,
|
route, errorSourceIdx,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// All nodes up to the failing pair must have forwarded
|
||||||
|
// successfully.
|
||||||
|
i.successPairRange(route, 0, errorSourceIdx-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
reportIncoming := func() {
|
reportIncoming := func() {
|
||||||
@ -197,6 +248,12 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(
|
|||||||
i.failPair(
|
i.failPair(
|
||||||
route, errorSourceIdx-1,
|
route, errorSourceIdx-1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// All nodes up to the failing pair must have forwarded
|
||||||
|
// successfully.
|
||||||
|
if errorSourceIdx > 2 {
|
||||||
|
i.successPairRange(route, 0, errorSourceIdx-2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reportAll := func() {
|
reportAll := func() {
|
||||||
@ -330,8 +387,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] = 0
|
i.pairResults[pair] = pairResult{}
|
||||||
i.pairResults[pair.Reverse()] = 0
|
i.pairResults[pair.Reverse()] = pairResult{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// failPairBalance marks a pair as failed with a minimum penalization amount.
|
// failPairBalance marks a pair as failed with a minimum penalization amount.
|
||||||
@ -340,7 +397,23 @@ func (i *interpretedResult) failPairBalance(
|
|||||||
|
|
||||||
pair, amt := getPair(rt, channelIdx)
|
pair, amt := getPair(rt, channelIdx)
|
||||||
|
|
||||||
i.pairResults[pair] = amt
|
i.pairResults[pair] = pairResult{
|
||||||
|
minPenalizeAmt: amt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// successPairRange marks the node pairs from node fromIdx to node toIdx as
|
||||||
|
// succeeded.
|
||||||
|
func (i *interpretedResult) successPairRange(
|
||||||
|
rt *route.Route, fromIdx, toIdx int) {
|
||||||
|
|
||||||
|
for idx := fromIdx; idx <= toIdx; idx++ {
|
||||||
|
pair, _ := getPair(rt, idx)
|
||||||
|
|
||||||
|
i.pairResults[pair] = pairResult{
|
||||||
|
success: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPair returns a node pair from the route and the amount passed between that
|
// getPair returns a node pair from the route and the amount passed between that
|
||||||
|
@ -50,6 +50,7 @@ func getTestPair(from, to int) DirectedNodePair {
|
|||||||
type resultTestCase struct {
|
type resultTestCase struct {
|
||||||
name string
|
name string
|
||||||
route *route.Route
|
route *route.Route
|
||||||
|
success bool
|
||||||
failureSrcIdx int
|
failureSrcIdx int
|
||||||
failure lnwire.FailureMessage
|
failure lnwire.FailureMessage
|
||||||
|
|
||||||
@ -66,8 +67,13 @@ var resultTestCases = []resultTestCase{
|
|||||||
failure: lnwire.NewTemporaryChannelFailure(nil),
|
failure: lnwire.NewTemporaryChannelFailure(nil),
|
||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]lnwire.MilliSatoshi{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(1, 2): 99,
|
getTestPair(0, 1): {
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
getTestPair(1, 2): {
|
||||||
|
minPenalizeAmt: 99,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -80,13 +86,67 @@ var resultTestCases = []resultTestCase{
|
|||||||
failure: lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}),
|
failure: lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}),
|
||||||
|
|
||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]lnwire.MilliSatoshi{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): 0,
|
getTestPair(0, 1): {},
|
||||||
getTestPair(1, 0): 0,
|
getTestPair(1, 0): {},
|
||||||
getTestPair(1, 2): 0,
|
getTestPair(1, 2): {},
|
||||||
getTestPair(2, 1): 0,
|
getTestPair(2, 1): {},
|
||||||
getTestPair(2, 3): 0,
|
getTestPair(2, 3): {},
|
||||||
getTestPair(3, 2): 0,
|
getTestPair(3, 2): {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Tests an incorrect payment details result. This should be a final
|
||||||
|
// failure, but mark all pairs along the route as successful.
|
||||||
|
{
|
||||||
|
name: "fail incorrect details",
|
||||||
|
route: &routeTwoHop,
|
||||||
|
failureSrcIdx: 2,
|
||||||
|
failure: lnwire.NewFailIncorrectDetails(97),
|
||||||
|
|
||||||
|
expectedResult: &interpretedResult{
|
||||||
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
|
getTestPair(0, 1): {
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
getTestPair(1, 2): {
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
finalFailureReason: &reasonIncorrectDetails,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Tests a successful direct payment.
|
||||||
|
{
|
||||||
|
name: "success direct",
|
||||||
|
route: &routeOneHop,
|
||||||
|
success: true,
|
||||||
|
|
||||||
|
expectedResult: &interpretedResult{
|
||||||
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
|
getTestPair(0, 1): {
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Tests a successful two hop payment.
|
||||||
|
{
|
||||||
|
name: "success",
|
||||||
|
route: &routeTwoHop,
|
||||||
|
success: true,
|
||||||
|
|
||||||
|
expectedResult: &interpretedResult{
|
||||||
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
|
getTestPair(0, 1): {
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
getTestPair(1, 2): {
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -121,13 +181,13 @@ var resultTestCases = []resultTestCase{
|
|||||||
// TestResultInterpretation executes a list of test cases that test the result
|
// TestResultInterpretation executes a list of test cases that test the result
|
||||||
// interpretation logic.
|
// interpretation logic.
|
||||||
func TestResultInterpretation(t *testing.T) {
|
func TestResultInterpretation(t *testing.T) {
|
||||||
emptyResults := make(map[DirectedNodePair]lnwire.MilliSatoshi)
|
emptyResults := make(map[DirectedNodePair]pairResult)
|
||||||
|
|
||||||
for _, testCase := range resultTestCases {
|
for _, testCase := range resultTestCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
i := interpretResult(
|
i := interpretResult(
|
||||||
testCase.route, &testCase.failureSrcIdx,
|
testCase.route, testCase.success,
|
||||||
testCase.failure,
|
&testCase.failureSrcIdx, testCase.failure,
|
||||||
)
|
)
|
||||||
|
|
||||||
expected := testCase.expectedResult
|
expected := testCase.expectedResult
|
||||||
|
@ -183,6 +183,10 @@ type MissionController interface {
|
|||||||
failureSourceIdx *int, failure lnwire.FailureMessage) (
|
failureSourceIdx *int, failure lnwire.FailureMessage) (
|
||||||
*channeldb.FailureReason, error)
|
*channeldb.FailureReason, error)
|
||||||
|
|
||||||
|
// ReportPaymentSuccess reports a successful payment to mission control as input
|
||||||
|
// for future probability estimates.
|
||||||
|
ReportPaymentSuccess(paymentID uint64, rt *route.Route) error
|
||||||
|
|
||||||
// GetProbability is expected to return the success probability of a
|
// GetProbability is expected to return the success probability of a
|
||||||
// payment from fromNode along edge.
|
// payment from fromNode along edge.
|
||||||
GetProbability(fromNode, toNode route.Vertex,
|
GetProbability(fromNode, toNode route.Vertex,
|
||||||
|
Loading…
Reference in New Issue
Block a user