mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-23 22:46:40 +01:00
Merge pull request #1734 from joostjager/edgesets
routing: prune based on channel sets instead of channels
This commit is contained in:
commit
d134e0362e
19 changed files with 1183 additions and 1026 deletions
|
@ -32,24 +32,43 @@ func queryMissionControl(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type displayNodeHistory struct {
|
type displayNodeHistory struct {
|
||||||
Pubkey string
|
Pubkey string
|
||||||
LastFailTime int64
|
LastFailTime int64
|
||||||
OtherChanSuccessProb float32
|
OtherSuccessProb float32
|
||||||
Channels []*routerrpc.ChannelHistory
|
}
|
||||||
|
|
||||||
|
type displayPairHistory struct {
|
||||||
|
NodeFrom, NodeTo string
|
||||||
|
LastFailTime int64
|
||||||
|
SuccessProb float32
|
||||||
|
MinPenalizeAmtSat int64
|
||||||
}
|
}
|
||||||
|
|
||||||
displayResp := struct {
|
displayResp := struct {
|
||||||
Nodes []displayNodeHistory
|
Nodes []displayNodeHistory
|
||||||
|
Pairs []displayPairHistory
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
for _, n := range snapshot.Nodes {
|
for _, n := range snapshot.Nodes {
|
||||||
displayResp.Nodes = append(
|
displayResp.Nodes = append(
|
||||||
displayResp.Nodes,
|
displayResp.Nodes,
|
||||||
displayNodeHistory{
|
displayNodeHistory{
|
||||||
Pubkey: hex.EncodeToString(n.Pubkey),
|
Pubkey: hex.EncodeToString(n.Pubkey),
|
||||||
LastFailTime: n.LastFailTime,
|
LastFailTime: n.LastFailTime,
|
||||||
OtherChanSuccessProb: n.OtherChanSuccessProb,
|
OtherSuccessProb: n.OtherSuccessProb,
|
||||||
Channels: n.Channels,
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range snapshot.Pairs {
|
||||||
|
displayResp.Pairs = append(
|
||||||
|
displayResp.Pairs,
|
||||||
|
displayPairHistory{
|
||||||
|
NodeFrom: hex.EncodeToString(n.NodeFrom),
|
||||||
|
NodeTo: hex.EncodeToString(n.NodeTo),
|
||||||
|
LastFailTime: n.LastFailTime,
|
||||||
|
SuccessProb: n.SuccessProb,
|
||||||
|
MinPenalizeAmtSat: n.MinPenalizeAmtSat,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -979,9 +979,12 @@ func (m *QueryMissionControlRequest) XXX_DiscardUnknown() {
|
||||||
|
|
||||||
var xxx_messageInfo_QueryMissionControlRequest proto.InternalMessageInfo
|
var xxx_messageInfo_QueryMissionControlRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
/// QueryMissionControlResponse contains mission control state per node.
|
/// QueryMissionControlResponse contains mission control state.
|
||||||
type QueryMissionControlResponse struct {
|
type QueryMissionControlResponse struct {
|
||||||
Nodes []*NodeHistory `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
|
/// 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:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
@ -1019,19 +1022,26 @@ func (m *QueryMissionControlResponse) GetNodes() []*NodeHistory {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *QueryMissionControlResponse) GetPairs() []*PairHistory {
|
||||||
|
if m != nil {
|
||||||
|
return m.Pairs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
/// NodeHistory contains the mission control state for a particular node.
|
/// NodeHistory contains the mission control state for a particular node.
|
||||||
type NodeHistory struct {
|
type NodeHistory struct {
|
||||||
/// Node pubkey
|
/// Node pubkey
|
||||||
Pubkey []byte `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
|
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.
|
/// 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"`
|
LastFailTime int64 `protobuf:"varint,2,opt,name=last_fail_time,proto3" json:"last_fail_time,omitempty"`
|
||||||
/// Estimation of success probability for channels not in the channel array.
|
//*
|
||||||
OtherChanSuccessProb float32 `protobuf:"fixed32,3,opt,name=other_chan_success_prob,proto3" json:"other_chan_success_prob,omitempty"`
|
//Estimation of success probability of forwarding towards peers of this node
|
||||||
/// Historical information of particular channels.
|
//for which no specific history is available.
|
||||||
Channels []*ChannelHistory `protobuf:"bytes,4,rep,name=channels,proto3" json:"channels,omitempty"`
|
OtherSuccessProb float32 `protobuf:"fixed32,3,opt,name=other_success_prob,proto3" json:"other_success_prob,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:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NodeHistory) Reset() { *m = NodeHistory{} }
|
func (m *NodeHistory) Reset() { *m = NodeHistory{} }
|
||||||
|
@ -1073,82 +1083,84 @@ func (m *NodeHistory) GetLastFailTime() int64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NodeHistory) GetOtherChanSuccessProb() float32 {
|
func (m *NodeHistory) GetOtherSuccessProb() float32 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.OtherChanSuccessProb
|
return m.OtherSuccessProb
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NodeHistory) GetChannels() []*ChannelHistory {
|
/// PairHistory contains the mission control state for a particular node pair.
|
||||||
if m != nil {
|
type PairHistory struct {
|
||||||
return m.Channels
|
/// The source node pubkey of the pair.
|
||||||
}
|
NodeFrom []byte `protobuf:"bytes,1,opt,name=node_from,proto3" json:"node_from,omitempty"`
|
||||||
return nil
|
/// The destination node pubkey of the pair.
|
||||||
}
|
NodeTo []byte `protobuf:"bytes,2,opt,name=node_to,proto3" json:"node_to,omitempty"`
|
||||||
|
|
||||||
/// NodeHistory contains the mission control state for a particular channel.
|
|
||||||
type ChannelHistory struct {
|
|
||||||
/// Short channel id
|
|
||||||
ChannelId uint64 `protobuf:"varint,1,opt,name=channel_id,proto3" json:"channel_id,omitempty"`
|
|
||||||
/// Time stamp of last failure.
|
/// Time stamp of last failure.
|
||||||
LastFailTime int64 `protobuf:"varint,2,opt,name=last_fail_time,proto3" json:"last_fail_time,omitempty"`
|
LastFailTime int64 `protobuf:"varint,3,opt,name=last_fail_time,proto3" json:"last_fail_time,omitempty"`
|
||||||
/// Minimum penalization amount.
|
/// Minimum penalization amount.
|
||||||
MinPenalizeAmtSat int64 `protobuf:"varint,3,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 channel.
|
/// Estimation of success probability for this pair.
|
||||||
SuccessProb float32 `protobuf:"fixed32,4,opt,name=success_prob,proto3" json:"success_prob,omitempty"`
|
SuccessProb float32 `protobuf:"fixed32,5,opt,name=success_prob,proto3" json:"success_prob,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:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ChannelHistory) Reset() { *m = ChannelHistory{} }
|
func (m *PairHistory) Reset() { *m = PairHistory{} }
|
||||||
func (m *ChannelHistory) String() string { return proto.CompactTextString(m) }
|
func (m *PairHistory) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ChannelHistory) ProtoMessage() {}
|
func (*PairHistory) ProtoMessage() {}
|
||||||
func (*ChannelHistory) Descriptor() ([]byte, []int) {
|
func (*PairHistory) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_7a0613f69d37b0a5, []int{14}
|
return fileDescriptor_7a0613f69d37b0a5, []int{14}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ChannelHistory) XXX_Unmarshal(b []byte) error {
|
func (m *PairHistory) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ChannelHistory.Unmarshal(m, b)
|
return xxx_messageInfo_PairHistory.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *ChannelHistory) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *PairHistory) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_ChannelHistory.Marshal(b, m, deterministic)
|
return xxx_messageInfo_PairHistory.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (m *ChannelHistory) XXX_Merge(src proto.Message) {
|
func (m *PairHistory) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_ChannelHistory.Merge(m, src)
|
xxx_messageInfo_PairHistory.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *ChannelHistory) XXX_Size() int {
|
func (m *PairHistory) XXX_Size() int {
|
||||||
return xxx_messageInfo_ChannelHistory.Size(m)
|
return xxx_messageInfo_PairHistory.Size(m)
|
||||||
}
|
}
|
||||||
func (m *ChannelHistory) XXX_DiscardUnknown() {
|
func (m *PairHistory) XXX_DiscardUnknown() {
|
||||||
xxx_messageInfo_ChannelHistory.DiscardUnknown(m)
|
xxx_messageInfo_PairHistory.DiscardUnknown(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
var xxx_messageInfo_ChannelHistory proto.InternalMessageInfo
|
var xxx_messageInfo_PairHistory proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *ChannelHistory) GetChannelId() uint64 {
|
func (m *PairHistory) GetNodeFrom() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.ChannelId
|
return m.NodeFrom
|
||||||
}
|
}
|
||||||
return 0
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ChannelHistory) GetLastFailTime() int64 {
|
func (m *PairHistory) GetNodeTo() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.NodeTo
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PairHistory) GetLastFailTime() int64 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.LastFailTime
|
return m.LastFailTime
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ChannelHistory) GetMinPenalizeAmtSat() int64 {
|
func (m *PairHistory) GetMinPenalizeAmtSat() int64 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.MinPenalizeAmtSat
|
return m.MinPenalizeAmtSat
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ChannelHistory) GetSuccessProb() float32 {
|
func (m *PairHistory) GetSuccessProb() float32 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.SuccessProb
|
return m.SuccessProb
|
||||||
}
|
}
|
||||||
|
@ -1172,117 +1184,118 @@ func init() {
|
||||||
proto.RegisterType((*QueryMissionControlRequest)(nil), "routerrpc.QueryMissionControlRequest")
|
proto.RegisterType((*QueryMissionControlRequest)(nil), "routerrpc.QueryMissionControlRequest")
|
||||||
proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse")
|
proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse")
|
||||||
proto.RegisterType((*NodeHistory)(nil), "routerrpc.NodeHistory")
|
proto.RegisterType((*NodeHistory)(nil), "routerrpc.NodeHistory")
|
||||||
proto.RegisterType((*ChannelHistory)(nil), "routerrpc.ChannelHistory")
|
proto.RegisterType((*PairHistory)(nil), "routerrpc.PairHistory")
|
||||||
}
|
}
|
||||||
|
|
||||||
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{
|
||||||
// 1663 bytes of a gzipped FileDescriptorProto
|
// 1677 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x77, 0x22, 0xc7,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x77, 0x22, 0xc7,
|
||||||
0x11, 0x37, 0x02, 0x84, 0x28, 0xfe, 0x8d, 0x5a, 0x5a, 0x89, 0x45, 0xab, 0xb5, 0x8c, 0x9d, 0xb5,
|
0x11, 0x37, 0x02, 0x84, 0x28, 0xfe, 0xcd, 0xb6, 0xb4, 0xda, 0x59, 0xb4, 0xb2, 0xe5, 0xb1, 0xb3,
|
||||||
0xde, 0x3e, 0x47, 0x72, 0xc8, 0x5b, 0x3f, 0x9f, 0x92, 0xc7, 0x42, 0x63, 0x8d, 0x17, 0x66, 0xe4,
|
0xd6, 0xdb, 0xe7, 0x48, 0x0e, 0x79, 0xf6, 0xcb, 0x29, 0x79, 0x2c, 0x34, 0xd6, 0xec, 0xc2, 0x8c,
|
||||||
0x06, 0xd6, 0xde, 0xe4, 0xd0, 0xaf, 0xc5, 0xb4, 0x60, 0x9e, 0x60, 0x06, 0xcf, 0x34, 0xce, 0x2a,
|
0xdc, 0xc0, 0xda, 0x9b, 0x1c, 0xfa, 0xb5, 0xa0, 0x05, 0xf3, 0x04, 0x33, 0x78, 0xa6, 0x71, 0x56,
|
||||||
0x87, 0xdc, 0x72, 0xcc, 0x3d, 0xdf, 0x22, 0xf9, 0x04, 0xb9, 0xe7, 0x8b, 0x24, 0xdf, 0x21, 0xa7,
|
0x39, 0xe4, 0x92, 0x97, 0x63, 0xee, 0xf9, 0x16, 0xf9, 0x14, 0x39, 0xe4, 0x8b, 0x24, 0xdf, 0x21,
|
||||||
0xbc, 0xee, 0x9e, 0x81, 0x01, 0xa1, 0xb5, 0x4f, 0x62, 0x7e, 0xbf, 0x5f, 0x57, 0x75, 0x57, 0x75,
|
0xa7, 0xbc, 0xee, 0x9e, 0x81, 0x01, 0xb1, 0x1b, 0x9f, 0x98, 0xfe, 0xd5, 0xaf, 0xab, 0xaa, 0xab,
|
||||||
0x55, 0x97, 0xe0, 0x28, 0xf0, 0x17, 0x82, 0x07, 0xc1, 0x7c, 0x74, 0xa9, 0x7f, 0x5d, 0xcc, 0x03,
|
0xba, 0xaa, 0x0b, 0x38, 0x0e, 0x83, 0xa5, 0xe0, 0x61, 0xb8, 0x18, 0x5d, 0xea, 0xaf, 0x8b, 0x45,
|
||||||
0x5f, 0xf8, 0x28, 0xbf, 0xc4, 0x6b, 0xf9, 0x60, 0x3e, 0xd2, 0x68, 0xfd, 0x7f, 0x3b, 0x80, 0xfa,
|
0x18, 0x88, 0x00, 0x15, 0x57, 0x78, 0xbd, 0x18, 0x2e, 0x46, 0x1a, 0xb5, 0xfe, 0xbb, 0x07, 0xa8,
|
||||||
0xdc, 0x73, 0xae, 0xd9, 0xfd, 0x8c, 0x7b, 0x82, 0xf0, 0x1f, 0x17, 0x3c, 0x14, 0x08, 0x41, 0xc6,
|
0xcf, 0xfd, 0xf1, 0x35, 0xbb, 0x9f, 0x73, 0x5f, 0x10, 0xfe, 0xe3, 0x92, 0x47, 0x02, 0x21, 0xc8,
|
||||||
0xe1, 0xa1, 0xa8, 0xa6, 0xce, 0x52, 0xe7, 0x45, 0xa2, 0x7e, 0x23, 0x03, 0xd2, 0x6c, 0x26, 0xaa,
|
0x8d, 0x79, 0x24, 0xcc, 0xcc, 0x59, 0xe6, 0xbc, 0x4c, 0xd4, 0x37, 0x32, 0x20, 0xcb, 0xe6, 0xc2,
|
||||||
0x3b, 0x67, 0xa9, 0xf3, 0x34, 0x91, 0x3f, 0xd1, 0x27, 0x50, 0x9c, 0xeb, 0x75, 0x74, 0xc2, 0xc2,
|
0xdc, 0x3b, 0xcb, 0x9c, 0x67, 0x89, 0xfc, 0x44, 0x9f, 0x42, 0x79, 0xa1, 0xf7, 0xd1, 0x29, 0x8b,
|
||||||
0x49, 0x35, 0xad, 0xd4, 0x85, 0x08, 0xbb, 0x62, 0xe1, 0x04, 0x9d, 0x83, 0x71, 0xeb, 0x7a, 0x6c,
|
0xa6, 0x66, 0x56, 0xb1, 0x4b, 0x31, 0x76, 0xc5, 0xa2, 0x29, 0x3a, 0x07, 0xe3, 0xd6, 0xf3, 0xd9,
|
||||||
0x4a, 0x47, 0x53, 0xf1, 0x13, 0x75, 0xf8, 0x54, 0xb0, 0x6a, 0xe6, 0x2c, 0x75, 0x9e, 0x25, 0x65,
|
0x8c, 0x8e, 0x66, 0xe2, 0x27, 0x3a, 0xe6, 0x33, 0xc1, 0xcc, 0xdc, 0x59, 0xe6, 0x3c, 0x4f, 0xaa,
|
||||||
0x85, 0xb7, 0xa6, 0xe2, 0xa7, 0xb6, 0x44, 0xd1, 0xe7, 0x50, 0x89, 0x8d, 0x05, 0x7a, 0x17, 0xd5,
|
0x0a, 0x6f, 0xcd, 0xc4, 0x4f, 0x6d, 0x89, 0xa2, 0x2f, 0xa0, 0x96, 0x28, 0x0b, 0xb5, 0x17, 0x66,
|
||||||
0xec, 0x59, 0xea, 0x3c, 0x4f, 0xca, 0xf3, 0xf5, 0xbd, 0x7d, 0x0e, 0x15, 0xe1, 0xce, 0xb8, 0xbf,
|
0xfe, 0x2c, 0x73, 0x5e, 0x24, 0xd5, 0xc5, 0xa6, 0x6f, 0x5f, 0x40, 0x4d, 0x78, 0x73, 0x1e, 0x2c,
|
||||||
0x10, 0x34, 0xe4, 0x23, 0xdf, 0x73, 0xc2, 0xea, 0xae, 0xb6, 0x18, 0xc1, 0x7d, 0x8d, 0xa2, 0x3a,
|
0x05, 0x8d, 0xf8, 0x28, 0xf0, 0xc7, 0x91, 0xb9, 0xaf, 0x35, 0xc6, 0x70, 0x5f, 0xa3, 0xc8, 0x82,
|
||||||
0x94, 0x6e, 0x39, 0xa7, 0x53, 0x77, 0xe6, 0x0a, 0x1a, 0x32, 0x51, 0xcd, 0xa9, 0xad, 0x17, 0x6e,
|
0xca, 0x2d, 0xe7, 0x74, 0xe6, 0xcd, 0x3d, 0x41, 0x23, 0x26, 0xcc, 0x82, 0x72, 0xbd, 0x74, 0xcb,
|
||||||
0x39, 0xef, 0x4a, 0xac, 0xcf, 0x84, 0xdc, 0x9f, 0xbf, 0x10, 0x63, 0xdf, 0xf5, 0xc6, 0x74, 0x34,
|
0x79, 0x57, 0x62, 0x7d, 0x26, 0xa4, 0x7f, 0xc1, 0x52, 0x4c, 0x02, 0xcf, 0x9f, 0xd0, 0xd1, 0x94,
|
||||||
0x61, 0x1e, 0x75, 0x9d, 0xea, 0xde, 0x59, 0xea, 0x3c, 0x43, 0xca, 0x31, 0xde, 0x9a, 0x30, 0xcf,
|
0xf9, 0xd4, 0x1b, 0x9b, 0x07, 0x67, 0x99, 0xf3, 0x1c, 0xa9, 0x26, 0x78, 0x6b, 0xca, 0x7c, 0x7b,
|
||||||
0x74, 0xd0, 0x29, 0x80, 0x3a, 0x83, 0x32, 0x57, 0xcd, 0x2b, 0x8f, 0x79, 0x89, 0x28, 0x5b, 0xa8,
|
0x8c, 0x4e, 0x01, 0xd4, 0x19, 0x94, 0x3a, 0xb3, 0xa8, 0x2c, 0x16, 0x25, 0xa2, 0x74, 0xa1, 0x06,
|
||||||
0x01, 0x05, 0x15, 0x60, 0x3a, 0x71, 0x3d, 0x11, 0x56, 0xe1, 0x2c, 0x7d, 0x5e, 0x68, 0x18, 0x17,
|
0x94, 0x54, 0x80, 0xe9, 0xd4, 0xf3, 0x45, 0x64, 0xc2, 0x59, 0xf6, 0xbc, 0xd4, 0x30, 0x2e, 0x66,
|
||||||
0x53, 0x4f, 0xc6, 0x9a, 0x48, 0xe6, 0xca, 0xf5, 0x04, 0x49, 0x8a, 0xea, 0x5f, 0xc3, 0xc1, 0x20,
|
0xbe, 0x8c, 0x35, 0x91, 0x92, 0x2b, 0xcf, 0x17, 0x24, 0x4d, 0xb2, 0x7e, 0x03, 0x87, 0x83, 0x90,
|
||||||
0x60, 0xa3, 0xbb, 0x8d, 0xe0, 0x6f, 0x86, 0x35, 0xf5, 0x20, 0xac, 0xf5, 0xbf, 0x40, 0x29, 0x5a,
|
0x8d, 0xee, 0xb6, 0x82, 0xbf, 0x1d, 0xd6, 0xcc, 0x83, 0xb0, 0x5a, 0x7f, 0x86, 0x4a, 0xbc, 0xa9,
|
||||||
0xd4, 0x17, 0x4c, 0x2c, 0x42, 0xf4, 0x6b, 0xc8, 0x86, 0x82, 0x09, 0xae, 0xc4, 0xe5, 0xc6, 0xf1,
|
0x2f, 0x98, 0x58, 0x46, 0xe8, 0x97, 0x90, 0x8f, 0x04, 0x13, 0x5c, 0x91, 0xab, 0x8d, 0x27, 0x17,
|
||||||
0xc5, 0x32, 0xdb, 0x17, 0x09, 0x21, 0x27, 0x5a, 0x85, 0x6a, 0xb0, 0x37, 0x0f, 0xb8, 0x3b, 0x63,
|
0xab, 0x6c, 0x5f, 0xa4, 0x88, 0x9c, 0x68, 0x16, 0xaa, 0xc3, 0xc1, 0x22, 0xe4, 0xde, 0x9c, 0x4d,
|
||||||
0x63, 0xae, 0x12, 0x5a, 0x24, 0xcb, 0x6f, 0x54, 0x87, 0xac, 0x5a, 0xac, 0xd2, 0x59, 0x68, 0x14,
|
0xb8, 0x4a, 0x68, 0x99, 0xac, 0xd6, 0xc8, 0x82, 0xbc, 0xda, 0xac, 0xd2, 0x59, 0x6a, 0x94, 0xd3,
|
||||||
0x93, 0x67, 0x20, 0x9a, 0xaa, 0xff, 0x0e, 0x2a, 0xea, 0xbb, 0xc3, 0xf9, 0x87, 0xae, 0xcc, 0x31,
|
0x67, 0x20, 0x5a, 0x64, 0xfd, 0x16, 0x6a, 0x6a, 0xdd, 0xe1, 0xfc, 0x43, 0x57, 0xe6, 0x09, 0x14,
|
||||||
0xe4, 0xd8, 0x4c, 0xc7, 0x5e, 0x5f, 0x9b, 0x5d, 0x36, 0x93, 0x61, 0xaf, 0x3b, 0x60, 0xac, 0xd6,
|
0xd8, 0x5c, 0xc7, 0x5e, 0x5f, 0x9b, 0x7d, 0x36, 0x97, 0x61, 0xb7, 0xc6, 0x60, 0xac, 0xf7, 0x47,
|
||||||
0x87, 0x73, 0xdf, 0x0b, 0xb9, 0x4c, 0x85, 0x34, 0x2e, 0x33, 0x21, 0xd3, 0x36, 0x93, 0xab, 0x52,
|
0x8b, 0xc0, 0x8f, 0xb8, 0x4c, 0x85, 0x54, 0x2e, 0x33, 0x21, 0xd3, 0x36, 0x97, 0xbb, 0x32, 0x6a,
|
||||||
0x6a, 0x55, 0x39, 0xc2, 0x3b, 0x9c, 0xf7, 0x42, 0x26, 0xd0, 0x0b, 0x7d, 0x03, 0xe8, 0xd4, 0x1f,
|
0x57, 0x35, 0xc6, 0x3b, 0x9c, 0xf7, 0x22, 0x26, 0xd0, 0x73, 0x7d, 0x03, 0xe8, 0x2c, 0x18, 0xdd,
|
||||||
0xdd, 0xc9, 0x3b, 0xc5, 0xee, 0x23, 0xf3, 0x25, 0x09, 0x77, 0xfd, 0xd1, 0x5d, 0x5b, 0x82, 0xf5,
|
0xc9, 0x3b, 0xc5, 0xee, 0x63, 0xf5, 0x15, 0x09, 0x77, 0x83, 0xd1, 0x5d, 0x5b, 0x82, 0xd6, 0x1f,
|
||||||
0x3f, 0xea, 0xbb, 0x3d, 0xf0, 0xf5, 0xde, 0x7f, 0x71, 0x78, 0x57, 0x21, 0xd8, 0x79, 0x3c, 0x04,
|
0xf4, 0xdd, 0x1e, 0x04, 0xda, 0xf7, 0x9f, 0x1d, 0xde, 0x75, 0x08, 0xf6, 0xde, 0x1f, 0x02, 0x0a,
|
||||||
0x14, 0x0e, 0xd6, 0x8c, 0x47, 0xa7, 0x48, 0x46, 0x36, 0xb5, 0x11, 0xd9, 0x2f, 0x20, 0x77, 0xcb,
|
0x87, 0x1b, 0xca, 0xe3, 0x53, 0xa4, 0x23, 0x9b, 0xd9, 0x8a, 0xec, 0x97, 0x50, 0xb8, 0x65, 0xde,
|
||||||
0xdc, 0xe9, 0x22, 0x88, 0x0d, 0xa3, 0x44, 0x9a, 0x3a, 0x9a, 0x21, 0xb1, 0xa4, 0xfe, 0xef, 0x1c,
|
0x6c, 0x19, 0x26, 0x8a, 0x51, 0x2a, 0x4d, 0x1d, 0x2d, 0x21, 0x09, 0xc5, 0xfa, 0x57, 0x01, 0x0a,
|
||||||
0xe4, 0x22, 0x10, 0x35, 0x20, 0x33, 0xf2, 0x9d, 0x38, 0xbb, 0xcf, 0x1f, 0x2e, 0x8b, 0xff, 0xb6,
|
0x31, 0x88, 0x1a, 0x90, 0x1b, 0x05, 0xe3, 0x24, 0xbb, 0x1f, 0x3f, 0xdc, 0x96, 0xfc, 0xb6, 0x82,
|
||||||
0x7c, 0x87, 0x13, 0xa5, 0x45, 0xbf, 0x87, 0xb2, 0xbc, 0xd1, 0x1e, 0x9f, 0xd2, 0xc5, 0xdc, 0x61,
|
0x31, 0x27, 0x8a, 0x8b, 0x7e, 0x07, 0x55, 0x79, 0xa3, 0x7d, 0x3e, 0xa3, 0xcb, 0xc5, 0x98, 0xad,
|
||||||
0xcb, 0x84, 0x56, 0x13, 0xab, 0x5b, 0x5a, 0x30, 0x54, 0x3c, 0x29, 0x8d, 0x92, 0x9f, 0xe8, 0x04,
|
0x12, 0x6a, 0xa6, 0x76, 0xb7, 0x34, 0x61, 0xa8, 0xe4, 0xa4, 0x32, 0x4a, 0x2f, 0xd1, 0x09, 0x14,
|
||||||
0xf2, 0x13, 0x31, 0x1d, 0xe9, 0x4c, 0x64, 0x54, 0x51, 0xec, 0x49, 0x40, 0xe5, 0xa0, 0x0e, 0x25,
|
0xa7, 0x62, 0x36, 0xd2, 0x99, 0xc8, 0xa9, 0xa2, 0x38, 0x90, 0x80, 0xca, 0x81, 0x05, 0x95, 0xc0,
|
||||||
0xdf, 0x73, 0x7d, 0x8f, 0x86, 0x13, 0x46, 0x1b, 0xaf, 0xbe, 0x52, 0xc5, 0x5a, 0x24, 0x05, 0x05,
|
0xf7, 0x02, 0x9f, 0x46, 0x53, 0x46, 0x1b, 0x5f, 0x7f, 0xa3, 0x8a, 0xb5, 0x4c, 0x4a, 0x0a, 0xec,
|
||||||
0xf6, 0x27, 0xac, 0xf1, 0xea, 0x2b, 0xf4, 0x31, 0x14, 0x54, 0xc9, 0xf0, 0xf7, 0x73, 0x37, 0xb8,
|
0x4f, 0x59, 0xe3, 0xeb, 0x6f, 0xd0, 0x27, 0x50, 0x52, 0x25, 0xc3, 0xdf, 0x2d, 0xbc, 0xf0, 0x5e,
|
||||||
0x57, 0x55, 0x5a, 0x22, 0xaa, 0x8a, 0xb0, 0x42, 0xd0, 0x21, 0x64, 0x6f, 0xa7, 0x6c, 0x1c, 0xaa,
|
0x55, 0x69, 0x85, 0xa8, 0x2a, 0xc2, 0x0a, 0x41, 0x47, 0x90, 0xbf, 0x9d, 0xb1, 0x49, 0xa4, 0x2a,
|
||||||
0xca, 0x2c, 0x11, 0xfd, 0x81, 0xbe, 0x84, 0xc3, 0x28, 0x06, 0x34, 0xf4, 0x17, 0xc1, 0x88, 0x53,
|
0xb3, 0x42, 0xf4, 0x02, 0x7d, 0x05, 0x47, 0x71, 0x0c, 0x68, 0x14, 0x2c, 0xc3, 0x11, 0xa7, 0x9e,
|
||||||
0xd7, 0x73, 0xf8, 0x7b, 0x55, 0x97, 0x25, 0x82, 0x22, 0xae, 0xaf, 0x28, 0x53, 0x32, 0xf5, 0xbf,
|
0x3f, 0xe6, 0xef, 0x54, 0x5d, 0x56, 0x08, 0x8a, 0x65, 0x7d, 0x25, 0xb2, 0xa5, 0xc4, 0xfa, 0x7b,
|
||||||
0x67, 0xa1, 0x90, 0x08, 0x00, 0x2a, 0xc2, 0x1e, 0xc1, 0x7d, 0x4c, 0xde, 0xe2, 0xb6, 0xf1, 0x11,
|
0x1e, 0x4a, 0xa9, 0x00, 0xa0, 0x32, 0x1c, 0x10, 0xdc, 0xc7, 0xe4, 0x0d, 0x6e, 0x1b, 0x1f, 0xa1,
|
||||||
0x3a, 0x87, 0xcf, 0x4c, 0xab, 0x65, 0x13, 0x82, 0x5b, 0x03, 0x6a, 0x13, 0x3a, 0xb4, 0xde, 0x58,
|
0x73, 0xf8, 0xdc, 0x76, 0x5a, 0x2e, 0x21, 0xb8, 0x35, 0xa0, 0x2e, 0xa1, 0x43, 0xe7, 0xb5, 0xe3,
|
||||||
0xf6, 0xf7, 0x16, 0xbd, 0x6e, 0xbe, 0xeb, 0x61, 0x6b, 0x40, 0xdb, 0x78, 0xd0, 0x34, 0xbb, 0x7d,
|
0x7e, 0xef, 0xd0, 0xeb, 0xe6, 0xdb, 0x1e, 0x76, 0x06, 0xb4, 0x8d, 0x07, 0x4d, 0xbb, 0xdb, 0x37,
|
||||||
0x23, 0x85, 0x9e, 0x41, 0x75, 0xa5, 0x8c, 0xe9, 0x66, 0xcf, 0x1e, 0x5a, 0x03, 0x63, 0x07, 0x7d,
|
0x32, 0xe8, 0x19, 0x98, 0x6b, 0x66, 0x22, 0x6e, 0xf6, 0xdc, 0xa1, 0x33, 0x30, 0xf6, 0xd0, 0x27,
|
||||||
0x0c, 0x27, 0x1d, 0xd3, 0x6a, 0x76, 0xe9, 0x4a, 0xd3, 0xea, 0x0e, 0xde, 0x52, 0xfc, 0xc3, 0xb5,
|
0x70, 0xd2, 0xb1, 0x9d, 0x66, 0x97, 0xae, 0x39, 0xad, 0xee, 0xe0, 0x0d, 0xc5, 0x3f, 0x5c, 0xdb,
|
||||||
0x49, 0xde, 0x19, 0xe9, 0x6d, 0x82, 0xab, 0x41, 0xb7, 0x15, 0x5b, 0xc8, 0xa0, 0xa7, 0xf0, 0x44,
|
0xe4, 0xad, 0x91, 0xdd, 0x45, 0xb8, 0x1a, 0x74, 0x5b, 0x89, 0x86, 0x1c, 0x7a, 0x0a, 0x8f, 0x35,
|
||||||
0x0b, 0xf4, 0x12, 0x3a, 0xb0, 0x6d, 0xda, 0xb7, 0x6d, 0xcb, 0xc8, 0xa2, 0x7d, 0x28, 0x99, 0xd6,
|
0x41, 0x6f, 0xa1, 0x03, 0xd7, 0xa5, 0x7d, 0xd7, 0x75, 0x8c, 0x3c, 0x7a, 0x04, 0x15, 0xdb, 0x79,
|
||||||
0xdb, 0x66, 0xd7, 0x6c, 0x53, 0x82, 0x9b, 0xdd, 0x9e, 0xb1, 0x8b, 0x0e, 0xa0, 0xb2, 0xa9, 0xcb,
|
0xd3, 0xec, 0xda, 0x6d, 0x4a, 0x70, 0xb3, 0xdb, 0x33, 0xf6, 0xd1, 0x21, 0xd4, 0xb6, 0x79, 0x05,
|
||||||
0x49, 0x13, 0xb1, 0xce, 0xb6, 0x4c, 0xdb, 0xa2, 0x6f, 0x31, 0xe9, 0x9b, 0xb6, 0x65, 0xec, 0xa1,
|
0xa9, 0x22, 0xe1, 0xb9, 0x8e, 0xed, 0x3a, 0xf4, 0x0d, 0x26, 0x7d, 0xdb, 0x75, 0x8c, 0x03, 0x74,
|
||||||
0x23, 0x40, 0xeb, 0xd4, 0x55, 0xaf, 0xd9, 0x32, 0xf2, 0xe8, 0x09, 0xec, 0xaf, 0xe3, 0x6f, 0xf0,
|
0x0c, 0x68, 0x53, 0x74, 0xd5, 0x6b, 0xb6, 0x8c, 0x22, 0x7a, 0x0c, 0x8f, 0x36, 0xf1, 0xd7, 0xf8,
|
||||||
0x3b, 0x03, 0x50, 0x15, 0x0e, 0xf5, 0xc6, 0xe8, 0x6b, 0xdc, 0xb5, 0xbf, 0xa7, 0x3d, 0xd3, 0x32,
|
0xad, 0x01, 0xc8, 0x84, 0x23, 0xed, 0x18, 0x7d, 0x89, 0xbb, 0xee, 0xf7, 0xb4, 0x67, 0x3b, 0x76,
|
||||||
0x7b, 0xc3, 0x9e, 0x51, 0x40, 0x87, 0x60, 0x74, 0x30, 0xa6, 0xa6, 0xd5, 0x1f, 0x76, 0x3a, 0x66,
|
0x6f, 0xd8, 0x33, 0x4a, 0xe8, 0x08, 0x8c, 0x0e, 0xc6, 0xd4, 0x76, 0xfa, 0xc3, 0x4e, 0xc7, 0x6e,
|
||||||
0xcb, 0xc4, 0xd6, 0xc0, 0x28, 0x6a, 0xcf, 0xdb, 0x0e, 0x5e, 0x92, 0x0b, 0x5a, 0x57, 0x4d, 0xcb,
|
0xd9, 0xd8, 0x19, 0x18, 0x65, 0x6d, 0x79, 0xd7, 0xc1, 0x2b, 0x72, 0x43, 0xeb, 0xaa, 0xe9, 0x38,
|
||||||
0xc2, 0x5d, 0xda, 0x36, 0xfb, 0xcd, 0xd7, 0x5d, 0xdc, 0x36, 0xca, 0xe8, 0x14, 0x9e, 0x0e, 0x70,
|
0xb8, 0x4b, 0xdb, 0x76, 0xbf, 0xf9, 0xb2, 0x8b, 0xdb, 0x46, 0x15, 0x9d, 0xc2, 0xd3, 0x01, 0xee,
|
||||||
0xef, 0xda, 0x26, 0x4d, 0xf2, 0x8e, 0xc6, 0x7c, 0xa7, 0x69, 0x76, 0x87, 0x04, 0x1b, 0x15, 0xf4,
|
0x5d, 0xbb, 0xa4, 0x49, 0xde, 0xd2, 0x44, 0xde, 0x69, 0xda, 0xdd, 0x21, 0xc1, 0x46, 0x0d, 0x7d,
|
||||||
0x09, 0x9c, 0x12, 0xfc, 0xdd, 0xd0, 0x24, 0xb8, 0x4d, 0x2d, 0xbb, 0x8d, 0x69, 0x07, 0x37, 0x07,
|
0x0a, 0xa7, 0x04, 0x7f, 0x37, 0xb4, 0x09, 0x6e, 0x53, 0xc7, 0x6d, 0x63, 0xda, 0xc1, 0xcd, 0xc1,
|
||||||
0x43, 0x82, 0x69, 0xcf, 0xec, 0xf7, 0x4d, 0xeb, 0x1b, 0xc3, 0x40, 0x9f, 0xc1, 0xd9, 0x52, 0xb2,
|
0x90, 0x60, 0xda, 0xb3, 0xfb, 0x7d, 0xdb, 0xf9, 0xd6, 0x30, 0xd0, 0xe7, 0x70, 0xb6, 0xa2, 0xac,
|
||||||
0x34, 0xb0, 0xa1, 0xda, 0x97, 0xe7, 0x8b, 0x53, 0x6a, 0xe1, 0x1f, 0x06, 0xf4, 0x1a, 0x63, 0x62,
|
0x14, 0x6c, 0xb1, 0x1e, 0xc9, 0xf3, 0x25, 0x29, 0x75, 0xf0, 0x0f, 0x03, 0x7a, 0x8d, 0x31, 0x31,
|
||||||
0x20, 0x54, 0x83, 0xa3, 0x95, 0x7b, 0xed, 0x20, 0xf2, 0x7d, 0x20, 0xb9, 0x6b, 0x4c, 0x7a, 0x4d,
|
0x10, 0xaa, 0xc3, 0xf1, 0xda, 0xbc, 0x36, 0x10, 0xdb, 0x3e, 0x94, 0xb2, 0x6b, 0x4c, 0x7a, 0x4d,
|
||||||
0x4b, 0x26, 0x78, 0x8d, 0x3b, 0x94, 0xdb, 0x5e, 0x71, 0x9b, 0xdb, 0x7e, 0x82, 0x0e, 0xa1, 0x12,
|
0x47, 0x26, 0x78, 0x43, 0x76, 0x24, 0xdd, 0x5e, 0xcb, 0xb6, 0xdd, 0x7e, 0x8c, 0x8e, 0xa0, 0x96,
|
||||||
0x7b, 0x8b, 0xc1, 0xff, 0xe4, 0xd0, 0x31, 0xa0, 0xa1, 0x45, 0x70, 0xb3, 0x2d, 0x0f, 0xbf, 0x24,
|
0x58, 0x4b, 0xc0, 0x7f, 0x17, 0xd0, 0x13, 0x40, 0x43, 0x87, 0xe0, 0x66, 0x5b, 0x1e, 0x7e, 0x25,
|
||||||
0xfe, 0x9b, 0xfb, 0x36, 0xb3, 0xb7, 0x63, 0xa4, 0xeb, 0xff, 0x48, 0x43, 0x69, 0xad, 0xd6, 0xd0,
|
0xf8, 0x4f, 0xe1, 0x55, 0xee, 0x60, 0xcf, 0xc8, 0x5a, 0xff, 0xc8, 0x42, 0x65, 0xa3, 0xd6, 0xd0,
|
||||||
0x33, 0xc8, 0x87, 0xee, 0xd8, 0x63, 0x42, 0x76, 0x03, 0xdd, 0x28, 0x56, 0x80, 0x7a, 0x6c, 0x26,
|
0x33, 0x28, 0x46, 0xde, 0xc4, 0x67, 0x42, 0x76, 0x03, 0xdd, 0x28, 0xd6, 0x80, 0x7a, 0x6c, 0xa6,
|
||||||
0xcc, 0xf5, 0x74, 0x87, 0xd2, 0x1d, 0x3a, 0xaf, 0x10, 0xd5, 0x9f, 0x8e, 0x21, 0x17, 0x3f, 0x56,
|
0xcc, 0xf3, 0x75, 0x87, 0xd2, 0x1d, 0xba, 0xa8, 0x10, 0xd5, 0x9f, 0x9e, 0x40, 0x21, 0x79, 0xac,
|
||||||
0x69, 0x55, 0x97, 0xbb, 0x23, 0xfd, 0x48, 0x3d, 0x83, 0xbc, 0x6c, 0x81, 0xa1, 0x60, 0xb3, 0xb9,
|
0xb2, 0xaa, 0x2e, 0xf7, 0x47, 0xfa, 0x91, 0x7a, 0x06, 0x45, 0xd9, 0x02, 0x23, 0xc1, 0xe6, 0x0b,
|
||||||
0x2a, 0xd9, 0x12, 0x59, 0x01, 0xe8, 0x53, 0x28, 0xcd, 0x78, 0x18, 0xb2, 0x31, 0xa7, 0xba, 0xec,
|
0x55, 0xb2, 0x15, 0xb2, 0x06, 0xd0, 0x67, 0x50, 0x99, 0xf3, 0x28, 0x62, 0x13, 0x4e, 0x75, 0xd9,
|
||||||
0x40, 0x29, 0x8a, 0x11, 0xd8, 0x51, 0xd5, 0xf7, 0x29, 0xc4, 0x6d, 0x20, 0x12, 0x65, 0xb5, 0x28,
|
0x81, 0x62, 0x94, 0x63, 0xb0, 0xa3, 0xaa, 0xef, 0x33, 0x48, 0xda, 0x40, 0x4c, 0xca, 0x6b, 0x52,
|
||||||
0x02, 0xb5, 0x68, 0xb3, 0x03, 0x0b, 0x16, 0x55, 0x77, 0xb2, 0x03, 0x0b, 0x86, 0x5e, 0xc2, 0xbe,
|
0x0c, 0x6a, 0xd2, 0x76, 0x07, 0x16, 0x2c, 0xae, 0xee, 0x74, 0x07, 0x16, 0x0c, 0xbd, 0x80, 0x47,
|
||||||
0x6e, 0x21, 0xae, 0xe7, 0xce, 0x16, 0x33, 0xdd, 0x4a, 0x72, 0x6a, 0xcb, 0x15, 0xd5, 0x4a, 0x34,
|
0xba, 0x85, 0x78, 0xbe, 0x37, 0x5f, 0xce, 0x75, 0x2b, 0x29, 0x28, 0x97, 0x6b, 0xaa, 0x95, 0x68,
|
||||||
0xae, 0x3a, 0xca, 0x53, 0xd8, 0xbb, 0x61, 0x21, 0x97, 0xcd, 0x3f, 0x2a, 0xf5, 0x9c, 0xfc, 0xee,
|
0x5c, 0x75, 0x94, 0xa7, 0x70, 0x70, 0xc3, 0x22, 0x2e, 0x9b, 0x7f, 0x5c, 0xea, 0x05, 0xb9, 0xee,
|
||||||
0x70, 0x2e, 0x29, 0xf9, 0x24, 0x04, 0xb2, 0x89, 0xe5, 0x35, 0x75, 0xcb, 0x39, 0x91, 0x71, 0x5c,
|
0x70, 0x2e, 0x45, 0xf2, 0x49, 0x08, 0x65, 0x13, 0x2b, 0x6a, 0xd1, 0x2d, 0xe7, 0x44, 0xc6, 0x71,
|
||||||
0x7a, 0x60, 0xef, 0x57, 0x1e, 0x0a, 0x09, 0x0f, 0x1a, 0x57, 0x1e, 0x5e, 0xc2, 0x3e, 0x7f, 0x2f,
|
0x65, 0x81, 0xbd, 0x5b, 0x5b, 0x28, 0xa5, 0x2c, 0x68, 0x5c, 0x59, 0x78, 0x01, 0x8f, 0xf8, 0x3b,
|
||||||
0x02, 0x46, 0xfd, 0x39, 0xfb, 0x71, 0xc1, 0xa9, 0xc3, 0x04, 0xab, 0x16, 0x55, 0x70, 0x2b, 0x8a,
|
0x11, 0x32, 0x1a, 0x2c, 0xd8, 0x8f, 0x4b, 0x4e, 0xc7, 0x4c, 0x30, 0xb3, 0xac, 0x82, 0x5b, 0x53,
|
||||||
0xb0, 0x15, 0xde, 0x66, 0x82, 0xd5, 0x9f, 0x41, 0x8d, 0xf0, 0x90, 0x8b, 0x9e, 0x1b, 0x86, 0xae,
|
0x02, 0x57, 0xe1, 0x6d, 0x26, 0x98, 0xf5, 0x0c, 0xea, 0x84, 0x47, 0x5c, 0xf4, 0xbc, 0x28, 0xf2,
|
||||||
0xef, 0xb5, 0x7c, 0x4f, 0x04, 0xfe, 0x34, 0x7a, 0x43, 0xea, 0xa7, 0x70, 0xb2, 0x95, 0xd5, 0x8f,
|
0x02, 0xbf, 0x15, 0xf8, 0x22, 0x0c, 0x66, 0xf1, 0x1b, 0x62, 0x9d, 0xc2, 0xc9, 0x4e, 0xa9, 0x7e,
|
||||||
0x80, 0x5c, 0xfc, 0xdd, 0x82, 0x07, 0xf7, 0xdb, 0x17, 0xbf, 0x81, 0x93, 0xad, 0x6c, 0xf4, 0x82,
|
0x04, 0xe4, 0xe6, 0xef, 0x96, 0x3c, 0xbc, 0xdf, 0xbd, 0xf9, 0x1e, 0x4e, 0x76, 0x4a, 0xe3, 0x17,
|
||||||
0x7c, 0x01, 0x59, 0xcf, 0x77, 0x78, 0x58, 0x4d, 0xa9, 0x19, 0xe2, 0x28, 0xd1, 0xae, 0x2d, 0xdf,
|
0xe4, 0x4b, 0xc8, 0xfb, 0xc1, 0x98, 0x47, 0x66, 0x46, 0xcd, 0x10, 0xc7, 0xa9, 0x76, 0xed, 0x04,
|
||||||
0xe1, 0x57, 0x6e, 0x28, 0xfc, 0xe0, 0x9e, 0x68, 0x51, 0xfd, 0x5f, 0x29, 0x28, 0x24, 0x60, 0x74,
|
0x63, 0x7e, 0xe5, 0x45, 0x22, 0x08, 0xef, 0x89, 0x26, 0x49, 0xf6, 0x82, 0x79, 0x61, 0x64, 0xee,
|
||||||
0x04, 0xbb, 0xf3, 0xc5, 0xcd, 0x1d, 0xbf, 0x8f, 0x2e, 0x55, 0xf4, 0x85, 0x5e, 0x40, 0x79, 0xca,
|
0x3d, 0x60, 0x5f, 0x33, 0x2f, 0x5c, 0xb1, 0x15, 0xc9, 0xfa, 0x4b, 0x06, 0x4a, 0x29, 0x25, 0xe8,
|
||||||
0x42, 0x41, 0x65, 0xf7, 0xa4, 0x32, 0x49, 0xd1, 0x93, 0xb9, 0x81, 0xa2, 0xaf, 0xe1, 0xd8, 0x17,
|
0x18, 0xf6, 0x17, 0xcb, 0x9b, 0x3b, 0x7e, 0x1f, 0x5f, 0xc1, 0x78, 0x85, 0x9e, 0x43, 0x75, 0xc6,
|
||||||
0x13, 0x1e, 0xe8, 0x69, 0x28, 0x5c, 0x8c, 0x46, 0x3c, 0x0c, 0xe9, 0x3c, 0xf0, 0x6f, 0xd4, 0x55,
|
0x22, 0x41, 0x65, 0xaf, 0xa5, 0x32, 0xa5, 0xf1, 0x03, 0xbb, 0x85, 0xa2, 0x0b, 0x40, 0x81, 0x98,
|
||||||
0xdb, 0x21, 0x8f, 0xd1, 0xe8, 0x15, 0xec, 0x45, 0x77, 0x24, 0xac, 0x66, 0xd4, 0xd6, 0x9f, 0x3e,
|
0xf2, 0x90, 0x46, 0xcb, 0xd1, 0x88, 0x47, 0x11, 0x5d, 0x84, 0xc1, 0x8d, 0xba, 0x93, 0x7b, 0x64,
|
||||||
0x7c, 0x69, 0xe2, 0xdd, 0x2f, 0xa5, 0xf5, 0x7f, 0xa6, 0xa0, 0xbc, 0x4e, 0xa2, 0xe7, 0xea, 0xf6,
|
0x87, 0xe4, 0x55, 0xee, 0x20, 0x67, 0xe4, 0xad, 0x7f, 0x66, 0xa0, 0x94, 0x72, 0x4e, 0xde, 0x5a,
|
||||||
0xab, 0x2b, 0xe8, 0x3a, 0xea, 0x1c, 0x19, 0x92, 0x40, 0x7e, 0xf1, 0x59, 0x1a, 0x70, 0x38, 0x73,
|
0x79, 0x18, 0x7a, 0x1b, 0x06, 0xf3, 0xa4, 0x16, 0x56, 0x00, 0x32, 0xa1, 0xa0, 0x16, 0x22, 0x88,
|
||||||
0x3d, 0x3a, 0xe7, 0x1e, 0x9b, 0xba, 0x7f, 0xe6, 0x34, 0x9e, 0x45, 0xd2, 0x4a, 0xbd, 0x95, 0x43,
|
0x0b, 0x21, 0x59, 0xee, 0xf0, 0x32, 0xbb, 0xd3, 0xcb, 0x06, 0x1c, 0xcd, 0x3d, 0x9f, 0x2e, 0xb8,
|
||||||
0x75, 0x28, 0xae, 0x1d, 0x3a, 0xa3, 0x0e, 0xbd, 0x86, 0xbd, 0xfc, 0x5b, 0x0a, 0x8a, 0xc9, 0xa9,
|
0xcf, 0x66, 0xde, 0x9f, 0x38, 0x4d, 0x66, 0x92, 0x9c, 0x62, 0xef, 0x94, 0x21, 0x0b, 0xca, 0x1b,
|
||||||
0x0a, 0x95, 0x20, 0x6f, 0x5a, 0xb4, 0xd3, 0x35, 0xbf, 0xb9, 0x1a, 0x18, 0x1f, 0xc9, 0xcf, 0xfe,
|
0x67, 0xca, 0xab, 0x33, 0x6d, 0x60, 0x2f, 0xfe, 0x96, 0x81, 0x72, 0x7a, 0xba, 0x42, 0x15, 0x28,
|
||||||
0xb0, 0xd5, 0xc2, 0xb8, 0x8d, 0xdb, 0x46, 0x0a, 0x21, 0x28, 0xcb, 0x86, 0x80, 0xdb, 0x74, 0x60,
|
0xda, 0x0e, 0xed, 0x74, 0xed, 0x6f, 0xaf, 0x06, 0xc6, 0x47, 0x72, 0xd9, 0x1f, 0xb6, 0x5a, 0x18,
|
||||||
0xf6, 0xb0, 0x3d, 0x94, 0x6f, 0xc9, 0x01, 0x54, 0x22, 0xcc, 0xb2, 0x29, 0xb1, 0x87, 0x03, 0x6c,
|
0xb7, 0x71, 0xdb, 0xc8, 0x20, 0x04, 0x55, 0xd9, 0x18, 0x70, 0x9b, 0x0e, 0xec, 0x1e, 0x76, 0x87,
|
||||||
0xa4, 0x91, 0x01, 0xc5, 0x08, 0xc4, 0x84, 0xd8, 0xc4, 0xc8, 0xc8, 0x06, 0x18, 0x21, 0x0f, 0xdf,
|
0xf2, 0x4d, 0x39, 0x84, 0x5a, 0x8c, 0x39, 0x2e, 0x25, 0xee, 0x70, 0x80, 0x8d, 0x2c, 0x32, 0xa0,
|
||||||
0xa5, 0xf8, 0xd9, 0xca, 0x36, 0xfe, 0x9a, 0x81, 0x5d, 0x35, 0x85, 0x04, 0xe8, 0x0a, 0x0a, 0x89,
|
0x1c, 0x83, 0x98, 0x10, 0x97, 0x18, 0x39, 0xd9, 0x08, 0x63, 0xe4, 0xe1, 0xfb, 0x94, 0x3c, 0x5f,
|
||||||
0x71, 0x1e, 0x9d, 0x26, 0x32, 0xf0, 0x70, 0xcc, 0xaf, 0x55, 0xb7, 0x8f, 0x89, 0x8b, 0xf0, 0xcb,
|
0xf9, 0xc6, 0x5f, 0x73, 0xb0, 0xaf, 0xa6, 0x91, 0x10, 0x5d, 0x41, 0x29, 0x35, 0xd6, 0xa3, 0xd3,
|
||||||
0x14, 0xfa, 0x16, 0x8a, 0xc9, 0xe1, 0x14, 0x25, 0x87, 0x8e, 0x2d, 0x53, 0xeb, 0x07, 0x6d, 0xbd,
|
0xd4, 0xb5, 0x78, 0x38, 0xee, 0xd7, 0xcd, 0xdd, 0xe3, 0xe2, 0x32, 0xfa, 0x2a, 0x83, 0x5e, 0x41,
|
||||||
0x01, 0x03, 0x87, 0xc2, 0x9d, 0xc9, 0x21, 0x23, 0x1a, 0xfb, 0x50, 0x2d, 0xa1, 0xdf, 0x98, 0x25,
|
0x39, 0x3d, 0xa4, 0xa2, 0xf4, 0xf0, 0xb1, 0x63, 0x7a, 0xfd, 0xa0, 0xae, 0xd7, 0x60, 0xe0, 0x48,
|
||||||
0x6b, 0x27, 0x5b, 0xb9, 0xa8, 0x3e, 0xba, 0xfa, 0x88, 0xd1, 0xe0, 0xf5, 0xe0, 0x88, 0xeb, 0xd3,
|
0x78, 0x73, 0x39, 0x6c, 0xc4, 0xe3, 0x1f, 0xaa, 0xa7, 0xf8, 0x5b, 0x33, 0x65, 0xfd, 0x64, 0xa7,
|
||||||
0x5e, 0xed, 0xf9, 0x63, 0x74, 0x64, 0xcd, 0x81, 0x83, 0x2d, 0x95, 0x8c, 0x7e, 0x95, 0xdc, 0xc1,
|
0x2c, 0xae, 0x93, 0xae, 0x3e, 0x62, 0x3c, 0x80, 0x3d, 0x38, 0xe2, 0xe6, 0xd4, 0x57, 0xff, 0xf8,
|
||||||
0xa3, 0x7d, 0xa0, 0xf6, 0xe2, 0xe7, 0x64, 0x2b, 0x2f, 0x5b, 0x4a, 0x7e, 0xcd, 0xcb, 0xe3, 0x0d,
|
0x7d, 0xe2, 0x58, 0xdb, 0x18, 0x0e, 0x77, 0x54, 0x34, 0xfa, 0x45, 0xda, 0x83, 0xf7, 0xf6, 0x83,
|
||||||
0x63, 0xcd, 0xcb, 0x07, 0x3a, 0xc7, 0xeb, 0xdf, 0xfc, 0xe1, 0x72, 0xec, 0x8a, 0xc9, 0xe2, 0xe6,
|
0xfa, 0xf3, 0xff, 0x47, 0x5b, 0x5b, 0xd9, 0x51, 0xfa, 0x1b, 0x56, 0xde, 0xdf, 0x38, 0x36, 0xac,
|
||||||
0x62, 0xe4, 0xcf, 0x2e, 0xa7, 0xee, 0x78, 0x22, 0x3c, 0xd7, 0x1b, 0x7b, 0x5c, 0xfc, 0xc9, 0x0f,
|
0x7c, 0xa0, 0x83, 0xbc, 0xfc, 0xd5, 0xef, 0x2f, 0x27, 0x9e, 0x98, 0x2e, 0x6f, 0x2e, 0x46, 0xc1,
|
||||||
0xee, 0x2e, 0xa7, 0x9e, 0x73, 0xa9, 0x06, 0xd9, 0xcb, 0xa5, 0xb9, 0x9b, 0x5d, 0xf5, 0x6f, 0xe0,
|
0xfc, 0x72, 0xe6, 0x4d, 0xa6, 0xc2, 0xf7, 0xfc, 0x89, 0xcf, 0xc5, 0x1f, 0x83, 0xf0, 0xee, 0x72,
|
||||||
0x6f, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x58, 0x80, 0x4d, 0xdb, 0x36, 0x0e, 0x00, 0x00,
|
0xe6, 0x8f, 0x2f, 0xd5, 0x40, 0x7b, 0xb9, 0x52, 0x77, 0xb3, 0xaf, 0xfe, 0x0e, 0xfe, 0xfa, 0x7f,
|
||||||
|
0x01, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xa6, 0xd9, 0x94, 0x3e, 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.
|
||||||
|
|
|
@ -323,9 +323,13 @@ message ResetMissionControlResponse{}
|
||||||
|
|
||||||
message QueryMissionControlRequest {}
|
message QueryMissionControlRequest {}
|
||||||
|
|
||||||
/// QueryMissionControlResponse contains mission control state per node.
|
/// QueryMissionControlResponse contains mission control state.
|
||||||
message QueryMissionControlResponse {
|
message QueryMissionControlResponse {
|
||||||
|
/// Node-level mission control state.
|
||||||
repeated NodeHistory nodes = 1;
|
repeated NodeHistory nodes = 1;
|
||||||
|
|
||||||
|
/// Node pair-level mission control state.
|
||||||
|
repeated PairHistory pairs = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NodeHistory contains the mission control state for a particular node.
|
/// NodeHistory contains the mission control state for a particular node.
|
||||||
|
@ -336,26 +340,31 @@ message NodeHistory {
|
||||||
/// Time stamp of last failure. Set to zero if no failure happened yet.
|
/// Time stamp of last failure. Set to zero if no failure happened yet.
|
||||||
int64 last_fail_time = 2 [json_name = "last_fail_time"];
|
int64 last_fail_time = 2 [json_name = "last_fail_time"];
|
||||||
|
|
||||||
/// Estimation of success probability for channels not in the channel array.
|
/**
|
||||||
float other_chan_success_prob = 3 [json_name = "other_chan_success_prob"];
|
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"];
|
||||||
|
|
||||||
/// Historical information of particular channels.
|
reserved 4;
|
||||||
repeated ChannelHistory channels = 4 [json_name = "channels"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NodeHistory contains the mission control state for a particular channel.
|
/// PairHistory contains the mission control state for a particular node pair.
|
||||||
message ChannelHistory {
|
message PairHistory {
|
||||||
/// Short channel id
|
/// The source node pubkey of the pair.
|
||||||
uint64 channel_id = 1 [json_name = "channel_id"];
|
bytes node_from = 1 [json_name="node_from"];
|
||||||
|
|
||||||
|
/// The destination node pubkey of the pair.
|
||||||
|
bytes node_to = 2 [json_name="node_to"];
|
||||||
|
|
||||||
/// Time stamp of last failure.
|
/// Time stamp of last failure.
|
||||||
int64 last_fail_time = 2 [json_name = "last_fail_time"];
|
int64 last_fail_time = 3 [json_name = "last_fail_time"];
|
||||||
|
|
||||||
/// Minimum penalization amount.
|
/// Minimum penalization amount.
|
||||||
int64 min_penalize_amt_sat = 3 [json_name = "min_penalize_amt_sat"];
|
int64 min_penalize_amt_sat = 4 [json_name = "min_penalize_amt_sat"];
|
||||||
|
|
||||||
/// Estimation of success probability for this channel.
|
/// Estimation of success probability for this pair.
|
||||||
float success_prob = 4 [json_name = "success_prob"];
|
float success_prob = 5 [json_name = "success_prob"];
|
||||||
}
|
}
|
||||||
|
|
||||||
service Router {
|
service Router {
|
||||||
|
|
|
@ -57,17 +57,17 @@ type RouterBackend struct {
|
||||||
|
|
||||||
// MissionControl defines the mission control dependencies of routerrpc.
|
// MissionControl defines the mission control dependencies of routerrpc.
|
||||||
type MissionControl interface {
|
type MissionControl interface {
|
||||||
// GetEdgeProbability is expected to return the success probability of a payment
|
// GetProbability is expected to return the success probability of a
|
||||||
// from fromNode along edge.
|
// payment from fromNode to toNode.
|
||||||
GetEdgeProbability(fromNode route.Vertex,
|
GetProbability(fromNode, toNode route.Vertex,
|
||||||
edge routing.EdgeLocator, amt lnwire.MilliSatoshi) float64
|
amt lnwire.MilliSatoshi) float64
|
||||||
|
|
||||||
// ResetHistory resets the history of MissionControl returning it to a state as
|
// ResetHistory resets the history of MissionControl returning it to a
|
||||||
// if no payment attempts have been made.
|
// state as if no payment attempts have been made.
|
||||||
ResetHistory() error
|
ResetHistory() error
|
||||||
|
|
||||||
// GetHistorySnapshot takes a snapshot from the current mission control state
|
// GetHistorySnapshot takes a snapshot from the current mission control
|
||||||
// and actual probability estimates.
|
// state and actual probability estimates.
|
||||||
GetHistorySnapshot() *routing.MissionControlSnapshot
|
GetHistorySnapshot() *routing.MissionControlSnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,15 +89,7 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
||||||
return route.Vertex{}, err
|
return route.Vertex{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pubKeyBytes) != 33 {
|
return route.NewVertexFromBytes(pubKeyBytes)
|
||||||
return route.Vertex{},
|
|
||||||
errors.New("invalid key length")
|
|
||||||
}
|
|
||||||
|
|
||||||
var v route.Vertex
|
|
||||||
copy(v[:], pubKeyBytes)
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the hex-encoded source and target public keys into full public
|
// Parse the hex-encoded source and target public keys into full public
|
||||||
|
@ -134,36 +126,57 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
||||||
|
|
||||||
ignoredNodes := make(map[route.Vertex]struct{})
|
ignoredNodes := make(map[route.Vertex]struct{})
|
||||||
for _, ignorePubKey := range in.IgnoredNodes {
|
for _, ignorePubKey := range in.IgnoredNodes {
|
||||||
if len(ignorePubKey) != 33 {
|
ignoreVertex, err := route.NewVertexFromBytes(ignorePubKey)
|
||||||
return nil, fmt.Errorf("invalid ignore node pubkey")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
var ignoreVertex route.Vertex
|
|
||||||
copy(ignoreVertex[:], ignorePubKey)
|
|
||||||
ignoredNodes[ignoreVertex] = struct{}{}
|
ignoredNodes[ignoreVertex] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoredEdges := make(map[routing.EdgeLocator]struct{})
|
ignoredPairs := make(map[routing.DirectedNodePair]struct{})
|
||||||
|
|
||||||
|
// Convert deprecated ignoredEdges to pairs.
|
||||||
for _, ignoredEdge := range in.IgnoredEdges {
|
for _, ignoredEdge := range in.IgnoredEdges {
|
||||||
locator := routing.EdgeLocator{
|
pair, err := r.rpcEdgeToPair(ignoredEdge)
|
||||||
ChannelID: ignoredEdge.ChannelId,
|
if err != nil {
|
||||||
|
log.Warnf("Ignore channel %v skipped: %v",
|
||||||
|
ignoredEdge.ChannelId, err)
|
||||||
|
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if ignoredEdge.DirectionReverse {
|
ignoredPairs[pair] = struct{}{}
|
||||||
locator.Direction = 1
|
}
|
||||||
|
|
||||||
|
// Add ignored pairs to set.
|
||||||
|
for _, ignorePair := range in.IgnoredPairs {
|
||||||
|
from, err := route.NewVertexFromBytes(ignorePair.From)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
ignoredEdges[locator] = struct{}{}
|
|
||||||
|
to, err := route.NewVertexFromBytes(ignorePair.To)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pair := routing.NewDirectedNodePair(from, to)
|
||||||
|
ignoredPairs[pair] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
restrictions := &routing.RestrictParams{
|
restrictions := &routing.RestrictParams{
|
||||||
FeeLimit: feeLimit,
|
FeeLimit: feeLimit,
|
||||||
ProbabilitySource: func(node route.Vertex,
|
ProbabilitySource: func(fromNode, toNode route.Vertex,
|
||||||
edge routing.EdgeLocator,
|
|
||||||
amt lnwire.MilliSatoshi) float64 {
|
amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
if _, ok := ignoredNodes[node]; ok {
|
if _, ok := ignoredNodes[fromNode]; ok {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := ignoredEdges[edge]; ok {
|
pair := routing.DirectedNodePair{
|
||||||
|
From: fromNode,
|
||||||
|
To: toNode,
|
||||||
|
}
|
||||||
|
if _, ok := ignoredPairs[pair]; ok {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,8 +184,8 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.MissionControl.GetEdgeProbability(
|
return r.MissionControl.GetProbability(
|
||||||
node, edge, amt,
|
fromNode, toNode, amt,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -211,6 +224,26 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
||||||
return routeResp, nil
|
return routeResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rpcEdgeToPair looks up the provided channel and returns the channel endpoints
|
||||||
|
// as a directed pair.
|
||||||
|
func (r *RouterBackend) rpcEdgeToPair(e *lnrpc.EdgeLocator) (
|
||||||
|
routing.DirectedNodePair, error) {
|
||||||
|
|
||||||
|
a, b, err := r.FetchChannelEndpoints(e.ChannelId)
|
||||||
|
if err != nil {
|
||||||
|
return routing.DirectedNodePair{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pair routing.DirectedNodePair
|
||||||
|
if e.DirectionReverse {
|
||||||
|
pair.From, pair.To = b, a
|
||||||
|
} else {
|
||||||
|
pair.From, pair.To = a, b
|
||||||
|
}
|
||||||
|
|
||||||
|
return pair, nil
|
||||||
|
}
|
||||||
|
|
||||||
// calculateFeeLimit returns the fee limit in millisatoshis. If a percentage
|
// calculateFeeLimit returns the fee limit in millisatoshis. If a percentage
|
||||||
// based fee limit has been requested, we'll factor in the ratio provided with
|
// based fee limit has been requested, we'll factor in the ratio provided with
|
||||||
// the amount of the payment.
|
// the amount of the payment.
|
||||||
|
@ -484,12 +517,11 @@ func (r *RouterBackend) extractIntentFromSendRequest(
|
||||||
// from the other fields.
|
// from the other fields.
|
||||||
|
|
||||||
// Payment destination.
|
// Payment destination.
|
||||||
if len(rpcPayReq.Dest) != 33 {
|
target, err := route.NewVertexFromBytes(rpcPayReq.Dest)
|
||||||
return nil, errors.New("invalid key length")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
pubBytes := rpcPayReq.Dest
|
payIntent.Target = target
|
||||||
copy(payIntent.Target[:], pubBytes)
|
|
||||||
|
|
||||||
// Final payment CLTV delta.
|
// Final payment CLTV delta.
|
||||||
if rpcPayReq.FinalCltvDelta != 0 {
|
if rpcPayReq.FinalCltvDelta != 0 {
|
||||||
|
|
|
@ -23,6 +23,10 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sourceKey = route.Vertex{1, 2, 3}
|
sourceKey = route.Vertex{1, 2, 3}
|
||||||
|
|
||||||
|
node1 = route.Vertex{10}
|
||||||
|
|
||||||
|
node2 = route.Vertex{11}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestQueryRoutes asserts that query routes rpc parameters are properly parsed
|
// TestQueryRoutes asserts that query routes rpc parameters are properly parsed
|
||||||
|
@ -50,11 +54,6 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoredEdge := routing.EdgeLocator{
|
|
||||||
ChannelID: 555,
|
|
||||||
Direction: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
request := &lnrpc.QueryRoutesRequest{
|
request := &lnrpc.QueryRoutesRequest{
|
||||||
PubKey: destKey,
|
PubKey: destKey,
|
||||||
Amt: 100000,
|
Amt: 100000,
|
||||||
|
@ -69,6 +68,10 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
|
||||||
ChannelId: 555,
|
ChannelId: 555,
|
||||||
DirectionReverse: true,
|
DirectionReverse: true,
|
||||||
}},
|
}},
|
||||||
|
IgnoredPairs: []*lnrpc.NodePair{{
|
||||||
|
From: node1[:],
|
||||||
|
To: node2[:],
|
||||||
|
}},
|
||||||
UseMissionControl: useMissionControl,
|
UseMissionControl: useMissionControl,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,24 +95,28 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
|
||||||
t.Fatal("unexpected fee limit")
|
t.Fatal("unexpected fee limit")
|
||||||
}
|
}
|
||||||
|
|
||||||
if restrictions.ProbabilitySource(route.Vertex{},
|
if restrictions.ProbabilitySource(route.Vertex{2},
|
||||||
ignoredEdge, 0,
|
route.Vertex{1}, 0,
|
||||||
) != 0 {
|
) != 0 {
|
||||||
t.Fatal("expecting 0% probability for ignored edge")
|
t.Fatal("expecting 0% probability for ignored edge")
|
||||||
}
|
}
|
||||||
|
|
||||||
if restrictions.ProbabilitySource(ignoreNodeVertex,
|
if restrictions.ProbabilitySource(ignoreNodeVertex,
|
||||||
routing.EdgeLocator{}, 0,
|
route.Vertex{6}, 0,
|
||||||
) != 0 {
|
) != 0 {
|
||||||
t.Fatal("expecting 0% probability for ignored node")
|
t.Fatal("expecting 0% probability for ignored node")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if restrictions.ProbabilitySource(node1, node2, 0) != 0 {
|
||||||
|
t.Fatal("expecting 0% probability for ignored pair")
|
||||||
|
}
|
||||||
|
|
||||||
expectedProb := 1.0
|
expectedProb := 1.0
|
||||||
if useMissionControl {
|
if useMissionControl {
|
||||||
expectedProb = testMissionControlProb
|
expectedProb = testMissionControlProb
|
||||||
}
|
}
|
||||||
if restrictions.ProbabilitySource(route.Vertex{},
|
if restrictions.ProbabilitySource(route.Vertex{4},
|
||||||
routing.EdgeLocator{}, 0,
|
route.Vertex{5}, 0,
|
||||||
) != expectedProb {
|
) != expectedProb {
|
||||||
t.Fatal("expecting 100% probability")
|
t.Fatal("expecting 100% probability")
|
||||||
}
|
}
|
||||||
|
@ -128,6 +135,16 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
|
||||||
return 1, nil
|
return 1, nil
|
||||||
},
|
},
|
||||||
MissionControl: &mockMissionControl{},
|
MissionControl: &mockMissionControl{},
|
||||||
|
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
|
||||||
|
route.Vertex, error) {
|
||||||
|
|
||||||
|
if chanID != 555 {
|
||||||
|
t.Fatal("expected endpoints to be fetched for "+
|
||||||
|
"channel 555, but got %v instead",
|
||||||
|
chanID)
|
||||||
|
}
|
||||||
|
return route.Vertex{1}, route.Vertex{2}, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := backend.QueryRoutes(context.Background(), request)
|
resp, err := backend.QueryRoutes(context.Background(), request)
|
||||||
|
@ -142,8 +159,9 @@ func testQueryRoutes(t *testing.T, useMissionControl bool) {
|
||||||
type mockMissionControl struct {
|
type mockMissionControl struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockMissionControl) GetEdgeProbability(fromNode route.Vertex,
|
func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,
|
||||||
edge routing.EdgeLocator, amt lnwire.MilliSatoshi) float64 {
|
amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
return testMissionControlProb
|
return testMissionControlProb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,40 +451,43 @@ func (s *Server) QueryMissionControl(ctx context.Context,
|
||||||
|
|
||||||
snapshot := s.cfg.RouterBackend.MissionControl.GetHistorySnapshot()
|
snapshot := s.cfg.RouterBackend.MissionControl.GetHistorySnapshot()
|
||||||
|
|
||||||
rpcNodes := make([]*NodeHistory, len(snapshot.Nodes))
|
rpcNodes := make([]*NodeHistory, 0, len(snapshot.Nodes))
|
||||||
for i, n := range snapshot.Nodes {
|
for _, n := range snapshot.Nodes {
|
||||||
// Copy node struct to prevent loop variable binding bugs.
|
// Copy node struct to prevent loop variable binding bugs.
|
||||||
node := n
|
node := n
|
||||||
|
|
||||||
channels := make([]*ChannelHistory, len(node.Channels))
|
rpcNode := NodeHistory{
|
||||||
for j, channel := range node.Channels {
|
|
||||||
channels[j] = &ChannelHistory{
|
|
||||||
ChannelId: channel.ChannelID,
|
|
||||||
LastFailTime: channel.LastFail.Unix(),
|
|
||||||
MinPenalizeAmtSat: int64(
|
|
||||||
channel.MinPenalizeAmt.ToSatoshis(),
|
|
||||||
),
|
|
||||||
SuccessProb: float32(channel.SuccessProb),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastFail int64
|
|
||||||
if node.LastFail != nil {
|
|
||||||
lastFail = node.LastFail.Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcNodes[i] = &NodeHistory{
|
|
||||||
Pubkey: node.Node[:],
|
Pubkey: node.Node[:],
|
||||||
LastFailTime: lastFail,
|
LastFailTime: node.LastFail.Unix(),
|
||||||
OtherChanSuccessProb: float32(
|
OtherSuccessProb: float32(
|
||||||
node.OtherChanSuccessProb,
|
node.OtherSuccessProb,
|
||||||
),
|
),
|
||||||
Channels: channels,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpcNodes = append(rpcNodes, &rpcNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcPairs := make([]*PairHistory, 0, len(snapshot.Pairs))
|
||||||
|
for _, p := range snapshot.Pairs {
|
||||||
|
// Prevent binding to loop variable.
|
||||||
|
pair := p
|
||||||
|
|
||||||
|
rpcPair := PairHistory{
|
||||||
|
NodeFrom: pair.Pair.From[:],
|
||||||
|
NodeTo: pair.Pair.To[:],
|
||||||
|
LastFailTime: pair.LastFail.Unix(),
|
||||||
|
MinPenalizeAmtSat: int64(
|
||||||
|
pair.MinPenalizeAmt.ToSatoshis(),
|
||||||
|
),
|
||||||
|
SuccessProb: float32(pair.SuccessProb),
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcPairs = append(rpcPairs, &rpcPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
response := QueryMissionControlResponse{
|
response := QueryMissionControlResponse{
|
||||||
Nodes: rpcNodes,
|
Nodes: rpcNodes,
|
||||||
|
Pairs: rpcPairs,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &response, nil
|
return &response, nil
|
||||||
|
|
1192
lnrpc/rpc.pb.go
1192
lnrpc/rpc.pb.go
File diff suppressed because it is too large
Load diff
|
@ -1645,9 +1645,9 @@ message QueryRoutesRequest {
|
||||||
repeated bytes ignored_nodes = 6;
|
repeated bytes ignored_nodes = 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A list of edges to ignore during path finding.
|
Deprecated. A list of edges to ignore during path finding.
|
||||||
*/
|
*/
|
||||||
repeated EdgeLocator ignored_edges = 7;
|
repeated EdgeLocator ignored_edges = 7 [deprecated = true];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The source node where the request route should originated from. If empty,
|
The source node where the request route should originated from. If empty,
|
||||||
|
@ -1660,6 +1660,19 @@ message QueryRoutesRequest {
|
||||||
the optimal route.
|
the optimal route.
|
||||||
*/
|
*/
|
||||||
bool use_mission_control = 9;
|
bool use_mission_control = 9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A list of directed node pairs that will be ignored during path finding.
|
||||||
|
*/
|
||||||
|
repeated NodePair ignored_pairs = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NodePair {
|
||||||
|
/// The sending node of the pair.
|
||||||
|
bytes from = 1;
|
||||||
|
|
||||||
|
/// The receiving node of the pair.
|
||||||
|
bytes to = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EdgeLocator {
|
message EdgeLocator {
|
||||||
|
|
|
@ -9543,6 +9543,17 @@ func testBidirectionalAsyncPayments(net *lntest.NetworkHarness, t *harnessTest)
|
||||||
"timeout: %v", err)
|
"timeout: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset mission control to prevent previous payment results from
|
||||||
|
// interfering with this test. A new channel has been opened, but
|
||||||
|
// mission control operates on node pairs.
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
_, err = net.Alice.RouterClient.ResetMissionControl(
|
||||||
|
ctxt, &routerrpc.ResetMissionControlRequest{},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to reset mc for alice: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Open up a payment streams to Alice and to Bob, that we'll use to
|
// Open up a payment streams to Alice and to Bob, that we'll use to
|
||||||
// send payment between nodes.
|
// send payment between nodes.
|
||||||
ctx, cancel := context.WithTimeout(ctxb, lntest.AsyncBenchmarkTimeout)
|
ctx, cancel := context.WithTimeout(ctxb, lntest.AsyncBenchmarkTimeout)
|
||||||
|
|
|
@ -55,7 +55,11 @@ const (
|
||||||
// since the last failure is used to estimate a success probability that is fed
|
// since the last failure is used to estimate a success probability that is fed
|
||||||
// into the path finding process for subsequent payment attempts.
|
// into the path finding process for subsequent payment attempts.
|
||||||
type MissionControl struct {
|
type MissionControl struct {
|
||||||
history map[route.Vertex]*nodeHistory
|
// lastPairFailure tracks the last payment failure per node pair.
|
||||||
|
lastPairFailure map[DirectedNodePair]pairFailure
|
||||||
|
|
||||||
|
// lastNodeFailure tracks the last node level failure per node.
|
||||||
|
lastNodeFailure map[route.Vertex]time.Time
|
||||||
|
|
||||||
// lastSecondChance tracks the last time a second chance was granted for
|
// lastSecondChance tracks the last time a second chance was granted for
|
||||||
// a directed node pair.
|
// a directed node pair.
|
||||||
|
@ -93,22 +97,10 @@ type MissionControlConfig struct {
|
||||||
MaxMcHistory int
|
MaxMcHistory int
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeHistory contains a summary of payment attempt outcomes involving a
|
// pairFailure describes a payment failure for a node pair.
|
||||||
// particular node.
|
type pairFailure struct {
|
||||||
type nodeHistory struct {
|
// timestamp is the time when this failure result was obtained.
|
||||||
// lastFail is the last time a node level failure occurred, if any.
|
timestamp time.Time
|
||||||
lastFail *time.Time
|
|
||||||
|
|
||||||
// channelLastFail tracks history per channel, if available for that
|
|
||||||
// channel.
|
|
||||||
channelLastFail map[uint64]*channelHistory
|
|
||||||
}
|
|
||||||
|
|
||||||
// channelHistory contains a summary of payment attempt outcomes involving a
|
|
||||||
// particular channel.
|
|
||||||
type channelHistory struct {
|
|
||||||
// lastFail is the last time a channel level failure occurred.
|
|
||||||
lastFail time.Time
|
|
||||||
|
|
||||||
// minPenalizeAmt is the minimum amount for which to take this failure
|
// minPenalizeAmt is the minimum amount for which to take this failure
|
||||||
// into account.
|
// into account.
|
||||||
|
@ -120,6 +112,10 @@ type channelHistory struct {
|
||||||
type MissionControlSnapshot struct {
|
type MissionControlSnapshot struct {
|
||||||
// Nodes contains the per node information of this snapshot.
|
// Nodes contains the per node information of this snapshot.
|
||||||
Nodes []MissionControlNodeSnapshot
|
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
|
// MissionControlNodeSnapshot contains a snapshot of the current node state in
|
||||||
|
@ -128,23 +124,19 @@ type MissionControlNodeSnapshot struct {
|
||||||
// Node pubkey.
|
// Node pubkey.
|
||||||
Node route.Vertex
|
Node route.Vertex
|
||||||
|
|
||||||
// Lastfail is the time of last failure, if any.
|
// LastFail is the time of last failure.
|
||||||
LastFail *time.Time
|
LastFail time.Time
|
||||||
|
|
||||||
// Channels is a list of channels for which specific information is
|
// OtherSuccessProb is the success probability for pairs not in
|
||||||
// logged.
|
// the Pairs slice.
|
||||||
Channels []MissionControlChannelSnapshot
|
OtherSuccessProb float64
|
||||||
|
|
||||||
// OtherChanSuccessProb is the success probability for channels not in
|
|
||||||
// the Channels slice.
|
|
||||||
OtherChanSuccessProb float64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MissionControlChannelSnapshot contains a snapshot of the current channel
|
// MissionControlPairSnapshot contains a snapshot of the current node pair
|
||||||
// state in mission control.
|
// state in mission control.
|
||||||
type MissionControlChannelSnapshot struct {
|
type MissionControlPairSnapshot struct {
|
||||||
// ChannelID is the short channel id of the snapshot.
|
// Pair is the node pair of which the state is described.
|
||||||
ChannelID uint64
|
Pair DirectedNodePair
|
||||||
|
|
||||||
// LastFail is the time of last failure.
|
// LastFail is the time of last failure.
|
||||||
LastFail time.Time
|
LastFail time.Time
|
||||||
|
@ -182,7 +174,8 @@ func NewMissionControl(db *bbolt.DB, cfg *MissionControlConfig) (
|
||||||
}
|
}
|
||||||
|
|
||||||
mc := &MissionControl{
|
mc := &MissionControl{
|
||||||
history: make(map[route.Vertex]*nodeHistory),
|
lastPairFailure: make(map[DirectedNodePair]pairFailure),
|
||||||
|
lastNodeFailure: make(map[route.Vertex]time.Time),
|
||||||
lastSecondChance: make(map[DirectedNodePair]time.Time),
|
lastSecondChance: make(map[DirectedNodePair]time.Time),
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
@ -227,7 +220,8 @@ func (m *MissionControl) ResetHistory() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.history = make(map[route.Vertex]*nodeHistory)
|
m.lastPairFailure = make(map[DirectedNodePair]pairFailure)
|
||||||
|
m.lastNodeFailure = make(map[route.Vertex]time.Time)
|
||||||
m.lastSecondChance = make(map[DirectedNodePair]time.Time)
|
m.lastSecondChance = make(map[DirectedNodePair]time.Time)
|
||||||
|
|
||||||
log.Debugf("Mission control history cleared")
|
log.Debugf("Mission control history cleared")
|
||||||
|
@ -235,59 +229,24 @@ func (m *MissionControl) ResetHistory() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEdgeProbability is expected to return the success probability of a payment
|
// GetProbability is expected to return the success probability of a payment
|
||||||
// from fromNode along edge.
|
// from fromNode along edge.
|
||||||
func (m *MissionControl) GetEdgeProbability(fromNode route.Vertex,
|
func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex,
|
||||||
edge EdgeLocator, amt lnwire.MilliSatoshi) float64 {
|
amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
// Get the history for this node. If there is no history available,
|
return m.getPairProbability(fromNode, toNode, amt)
|
||||||
// assume that it's success probability is a constant a priori
|
|
||||||
// probability. After the attempt new information becomes available to
|
|
||||||
// adjust this probability.
|
|
||||||
nodeHistory, ok := m.history[fromNode]
|
|
||||||
if !ok {
|
|
||||||
return m.cfg.AprioriHopProbability
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.getEdgeProbabilityForNode(nodeHistory, edge.ChannelID, amt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getEdgeProbabilityForNode estimates the probability of successfully
|
// getProbAfterFail returns a probability estimate based on a last failure time.
|
||||||
// traversing a channel based on the node history.
|
func (m *MissionControl) getProbAfterFail(lastFailure time.Time) float64 {
|
||||||
func (m *MissionControl) getEdgeProbabilityForNode(nodeHistory *nodeHistory,
|
if lastFailure.IsZero() {
|
||||||
channelID uint64, amt lnwire.MilliSatoshi) float64 {
|
|
||||||
|
|
||||||
// Calculate the last failure of the given edge. 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.
|
|
||||||
lastFailure := nodeHistory.lastFail
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
channelHistory, ok := nodeHistory.channelLastFail[channelID]
|
|
||||||
if ok && channelHistory.minPenalizeAmt <= amt {
|
|
||||||
|
|
||||||
// If there is both a node level failure recorded and a channel
|
|
||||||
// level failure is applicable too, we take the most recent of
|
|
||||||
// the two.
|
|
||||||
if lastFailure == nil ||
|
|
||||||
channelHistory.lastFail.After(*lastFailure) {
|
|
||||||
|
|
||||||
lastFailure = &channelHistory.lastFail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastFailure == nil {
|
|
||||||
return m.cfg.AprioriHopProbability
|
return m.cfg.AprioriHopProbability
|
||||||
}
|
}
|
||||||
|
|
||||||
timeSinceLastFailure := m.now().Sub(*lastFailure)
|
timeSinceLastFailure := m.now().Sub(lastFailure)
|
||||||
|
|
||||||
// Calculate success probability. It is an exponential curve that brings
|
// Calculate success probability. It is an exponential curve that brings
|
||||||
// the probability down to zero when a failure occurs. From there it
|
// the probability down to zero when a failure occurs. From there it
|
||||||
|
@ -299,6 +258,39 @@ func (m *MissionControl) getEdgeProbabilityForNode(nodeHistory *nodeHistory,
|
||||||
return probability
|
return probability
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
|
||||||
|
// 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.lastPairFailure[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) {
|
||||||
|
// Take into account a minimum penalize amount. For balance
|
||||||
|
// errors, a failure may be reported with such a minimum to
|
||||||
|
// prevent too aggresive penalization. We only take into account
|
||||||
|
// a previous failure if the amount that we currently get the
|
||||||
|
// probability for is greater or equal than the minPenalizeAmt
|
||||||
|
// of the previous failure.
|
||||||
|
if amt >= lastPairResult.minPenalizeAmt {
|
||||||
|
lastFail = lastPairResult.timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.getProbAfterFail(lastFail)
|
||||||
|
}
|
||||||
|
|
||||||
// requestSecondChance checks whether the node fromNode can have a second chance
|
// requestSecondChance checks whether the node fromNode can have a second chance
|
||||||
// at providing a channel update for its channel with toNode.
|
// at providing a channel update for its channel with toNode.
|
||||||
func (m *MissionControl) requestSecondChance(timestamp time.Time,
|
func (m *MissionControl) requestSecondChance(timestamp time.Time,
|
||||||
|
@ -330,21 +322,6 @@ func (m *MissionControl) requestSecondChance(timestamp time.Time,
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// createHistoryIfNotExists returns the history for the given node. If the node
|
|
||||||
// is yet unknown, it will create an empty history structure.
|
|
||||||
func (m *MissionControl) createHistoryIfNotExists(vertex route.Vertex) *nodeHistory {
|
|
||||||
if node, ok := m.history[vertex]; ok {
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
node := &nodeHistory{
|
|
||||||
channelLastFail: make(map[uint64]*channelHistory),
|
|
||||||
}
|
|
||||||
m.history[vertex] = node
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
// reportVertexFailure reports a node level failure.
|
// reportVertexFailure reports a node level failure.
|
||||||
func (m *MissionControl) reportVertexFailure(timestamp time.Time,
|
func (m *MissionControl) reportVertexFailure(timestamp time.Time,
|
||||||
v route.Vertex) {
|
v route.Vertex) {
|
||||||
|
@ -354,13 +331,12 @@ func (m *MissionControl) reportVertexFailure(timestamp time.Time,
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
history := m.createHistoryIfNotExists(v)
|
m.lastNodeFailure[v] = timestamp
|
||||||
history.lastFail = ×tamp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reportEdgePolicyFailure reports a policy related failure.
|
// reportPairPolicyFailure reports a policy related failure.
|
||||||
func (m *MissionControl) reportEdgePolicyFailure(timestamp time.Time,
|
func (m *MissionControl) reportPairPolicyFailure(timestamp time.Time,
|
||||||
failedEdge edge) {
|
failedPair DirectedNodePair) {
|
||||||
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
@ -369,31 +345,29 @@ func (m *MissionControl) reportEdgePolicyFailure(timestamp time.Time,
|
||||||
// immediately. If some time has passed since the last policy failure,
|
// immediately. If some time has passed since the last policy failure,
|
||||||
// we grant the node a second chance at forwarding the payment.
|
// we grant the node a second chance at forwarding the payment.
|
||||||
if m.requestSecondChance(
|
if m.requestSecondChance(
|
||||||
timestamp, failedEdge.from, failedEdge.to,
|
timestamp, failedPair.From, failedPair.To,
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
history := m.createHistoryIfNotExists(failedEdge.from)
|
m.lastNodeFailure[failedPair.From] = timestamp
|
||||||
history.lastFail = ×tamp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reportEdgeFailure reports a channel level failure.
|
// reportPairFailure reports a pair level failure.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): also add value attempted to send and capacity of channel
|
// TODO(roasbeef): also add value attempted to send and capacity of channel
|
||||||
func (m *MissionControl) reportEdgeFailure(timestamp time.Time, failedEdge edge,
|
func (m *MissionControl) reportPairFailure(timestamp time.Time,
|
||||||
minPenalizeAmt lnwire.MilliSatoshi) {
|
failedPair DirectedNodePair, minPenalizeAmt lnwire.MilliSatoshi) {
|
||||||
|
|
||||||
log.Debugf("Reporting channel %v failure to Mission Control",
|
log.Debugf("Reporting pair %v failure to Mission Control", failedPair)
|
||||||
failedEdge.channel)
|
|
||||||
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
history := m.createHistoryIfNotExists(failedEdge.from)
|
pair := NewDirectedNodePair(failedPair.From, failedPair.To)
|
||||||
history.channelLastFail[failedEdge.channel] = &channelHistory{
|
m.lastPairFailure[pair] = pairFailure{
|
||||||
lastFail: timestamp,
|
|
||||||
minPenalizeAmt: minPenalizeAmt,
|
minPenalizeAmt: minPenalizeAmt,
|
||||||
|
timestamp: timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,46 +378,40 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
log.Debugf("Requesting history snapshot from mission control: "+
|
log.Debugf("Requesting history snapshot from mission control: "+
|
||||||
"node_count=%v", len(m.history))
|
"node_failure_count=%v, pair_result_count=%v",
|
||||||
|
len(m.lastNodeFailure), len(m.lastPairFailure))
|
||||||
|
|
||||||
nodes := make([]MissionControlNodeSnapshot, 0, len(m.history))
|
nodes := make([]MissionControlNodeSnapshot, 0, len(m.lastNodeFailure))
|
||||||
|
for v, h := range m.lastNodeFailure {
|
||||||
|
otherProb := m.getPairProbability(v, route.Vertex{}, 0)
|
||||||
|
|
||||||
for v, h := range m.history {
|
nodes = append(nodes, MissionControlNodeSnapshot{
|
||||||
channelSnapshot := make([]MissionControlChannelSnapshot, 0,
|
Node: v,
|
||||||
len(h.channelLastFail),
|
LastFail: h,
|
||||||
)
|
OtherSuccessProb: otherProb,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for id, lastFail := range h.channelLastFail {
|
pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairFailure))
|
||||||
// Show probability assuming amount meets min
|
|
||||||
// penalization amount.
|
|
||||||
prob := m.getEdgeProbabilityForNode(
|
|
||||||
h, id, lastFail.minPenalizeAmt,
|
|
||||||
)
|
|
||||||
|
|
||||||
channelSnapshot = append(channelSnapshot,
|
for v, h := range m.lastPairFailure {
|
||||||
MissionControlChannelSnapshot{
|
// Show probability assuming amount meets min
|
||||||
ChannelID: id,
|
// penalization amount.
|
||||||
LastFail: lastFail.lastFail,
|
prob := m.getPairProbability(v.From, v.To, h.minPenalizeAmt)
|
||||||
MinPenalizeAmt: lastFail.minPenalizeAmt,
|
|
||||||
SuccessProb: prob,
|
pair := MissionControlPairSnapshot{
|
||||||
},
|
Pair: v,
|
||||||
)
|
MinPenalizeAmt: h.minPenalizeAmt,
|
||||||
|
LastFail: h.timestamp,
|
||||||
|
SuccessProb: prob,
|
||||||
}
|
}
|
||||||
|
|
||||||
otherProb := m.getEdgeProbabilityForNode(h, 0, 0)
|
pairs = append(pairs, pair)
|
||||||
|
|
||||||
nodes = append(nodes,
|
|
||||||
MissionControlNodeSnapshot{
|
|
||||||
Node: v,
|
|
||||||
LastFail: h.lastFail,
|
|
||||||
OtherChanSuccessProb: otherProb,
|
|
||||||
Channels: channelSnapshot,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot := MissionControlSnapshot{
|
snapshot := MissionControlSnapshot{
|
||||||
Nodes: nodes,
|
Nodes: nodes,
|
||||||
|
Pairs: pairs,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &snapshot
|
return &snapshot
|
||||||
|
@ -518,7 +486,7 @@ func (m *MissionControl) applyPaymentResult(result *paymentResult) (
|
||||||
|
|
||||||
// Always determine chan id ourselves, because a channel update with id
|
// Always determine chan id ourselves, because a channel update with id
|
||||||
// may not be available.
|
// may not be available.
|
||||||
failedEdge, failedAmt := getFailedEdge(
|
failedPair, failedAmt := getFailedPair(
|
||||||
result.route, failureSourceIdxInt,
|
result.route, failureSourceIdxInt,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -598,35 +566,35 @@ func (m *MissionControl) applyPaymentResult(result *paymentResult) (
|
||||||
// amount, we'll apply the new minimum amount and retry
|
// amount, we'll apply the new minimum amount and retry
|
||||||
// routing.
|
// routing.
|
||||||
case *lnwire.FailAmountBelowMinimum:
|
case *lnwire.FailAmountBelowMinimum:
|
||||||
m.reportEdgePolicyFailure(result.timeReply, failedEdge)
|
m.reportPairPolicyFailure(result.timeReply, failedPair)
|
||||||
return false, 0
|
return false, 0
|
||||||
|
|
||||||
// If we get a failure due to a fee, we'll apply the
|
// If we get a failure due to a fee, we'll apply the
|
||||||
// new fee update, and retry our attempt using the
|
// new fee update, and retry our attempt using the
|
||||||
// newly updated fees.
|
// newly updated fees.
|
||||||
case *lnwire.FailFeeInsufficient:
|
case *lnwire.FailFeeInsufficient:
|
||||||
m.reportEdgePolicyFailure(result.timeReply, failedEdge)
|
m.reportPairPolicyFailure(result.timeReply, failedPair)
|
||||||
return false, 0
|
return false, 0
|
||||||
|
|
||||||
// If we get the failure for an intermediate node that
|
// If we get the failure for an intermediate node that
|
||||||
// disagrees with our time lock values, then we'll
|
// disagrees with our time lock values, then we'll
|
||||||
// apply the new delta value and try it once more.
|
// apply the new delta value and try it once more.
|
||||||
case *lnwire.FailIncorrectCltvExpiry:
|
case *lnwire.FailIncorrectCltvExpiry:
|
||||||
m.reportEdgePolicyFailure(result.timeReply, failedEdge)
|
m.reportPairPolicyFailure(result.timeReply, failedPair)
|
||||||
return false, 0
|
return false, 0
|
||||||
|
|
||||||
// The outgoing channel that this node was meant to
|
// The outgoing channel that this node was meant to
|
||||||
// forward one is currently disabled, so we'll apply
|
// forward one is currently disabled, so we'll apply
|
||||||
// the update and continue.
|
// the update and continue.
|
||||||
case *lnwire.FailChannelDisabled:
|
case *lnwire.FailChannelDisabled:
|
||||||
m.reportEdgeFailure(result.timeReply, failedEdge, 0)
|
m.reportPairFailure(result.timeReply, failedPair, 0)
|
||||||
return false, 0
|
return false, 0
|
||||||
|
|
||||||
// It's likely that the outgoing channel didn't have
|
// It's likely that the outgoing channel didn't have
|
||||||
// sufficient capacity, so we'll prune this edge for
|
// sufficient capacity, so we'll prune this pair for
|
||||||
// now, and continue onwards with our path finding.
|
// now, and continue onwards with our path finding.
|
||||||
case *lnwire.FailTemporaryChannelFailure:
|
case *lnwire.FailTemporaryChannelFailure:
|
||||||
m.reportEdgeFailure(result.timeReply, failedEdge, failedAmt)
|
m.reportPairFailure(result.timeReply, failedPair, failedAmt)
|
||||||
return false, 0
|
return false, 0
|
||||||
|
|
||||||
// If the send fail due to a node not having the
|
// If the send fail due to a node not having the
|
||||||
|
@ -651,7 +619,7 @@ func (m *MissionControl) applyPaymentResult(result *paymentResult) (
|
||||||
// returning errors in order to attempt to black list
|
// returning errors in order to attempt to black list
|
||||||
// another node.
|
// another node.
|
||||||
case *lnwire.FailUnknownNextPeer:
|
case *lnwire.FailUnknownNextPeer:
|
||||||
m.reportEdgeFailure(result.timeReply, failedEdge, 0)
|
m.reportPairFailure(result.timeReply, failedPair, 0)
|
||||||
return false, 0
|
return false, 0
|
||||||
|
|
||||||
// If the node wasn't able to forward for which ever
|
// If the node wasn't able to forward for which ever
|
||||||
|
@ -682,12 +650,10 @@ func (m *MissionControl) applyPaymentResult(result *paymentResult) (
|
||||||
// we'll prune the channel in both directions and
|
// we'll prune the channel in both directions and
|
||||||
// continue with the rest of the routes.
|
// continue with the rest of the routes.
|
||||||
case *lnwire.FailPermanentChannelFailure:
|
case *lnwire.FailPermanentChannelFailure:
|
||||||
m.reportEdgeFailure(result.timeReply, failedEdge, 0)
|
m.reportPairFailure(result.timeReply, failedPair, 0)
|
||||||
m.reportEdgeFailure(result.timeReply, edge{
|
m.reportPairFailure(
|
||||||
from: failedEdge.to,
|
result.timeReply, failedPair.Reverse(), 0,
|
||||||
to: failedEdge.from,
|
)
|
||||||
channel: failedEdge.channel,
|
|
||||||
}, 0)
|
|
||||||
return false, 0
|
return false, 0
|
||||||
|
|
||||||
// Any other failure or an empty failure will get the node pruned.
|
// Any other failure or an empty failure will get the node pruned.
|
||||||
|
@ -697,11 +663,11 @@ func (m *MissionControl) applyPaymentResult(result *paymentResult) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFailedEdge tries to locate the failing channel given a route and the
|
// getFailedPair tries to locate the failing pair given a route and the pubkey
|
||||||
// pubkey of the node that sent the failure. It will assume that the failure is
|
// of the node that sent the failure. It will assume that the failure is
|
||||||
// associated with the outgoing channel of the failing node. As a second result,
|
// associated with the outgoing channel set of the failing node. As a second
|
||||||
// it returns the amount sent over the edge.
|
// result, it returns the amount sent between the pair.
|
||||||
func getFailedEdge(route *route.Route, failureSource int) (edge,
|
func getFailedPair(route *route.Route, failureSource int) (DirectedNodePair,
|
||||||
lnwire.MilliSatoshi) {
|
lnwire.MilliSatoshi) {
|
||||||
|
|
||||||
// Determine if we have a failure from the final hop. If it is, we
|
// Determine if we have a failure from the final hop. If it is, we
|
||||||
|
@ -718,16 +684,14 @@ func getFailedEdge(route *route.Route, failureSource int) (edge,
|
||||||
// this HTLC (for w/e reason), we'll return the _outgoing_ channel that
|
// this HTLC (for w/e reason), we'll return the _outgoing_ channel that
|
||||||
// the source of the failure was meant to pass the HTLC along to.
|
// the source of the failure was meant to pass the HTLC along to.
|
||||||
if failureSource == 0 {
|
if failureSource == 0 {
|
||||||
return edge{
|
return NewDirectedNodePair(
|
||||||
from: route.SourcePubKey,
|
route.SourcePubKey,
|
||||||
to: route.Hops[0].PubKeyBytes,
|
route.Hops[0].PubKeyBytes,
|
||||||
channel: route.Hops[0].ChannelID,
|
), route.TotalAmount
|
||||||
}, route.TotalAmount
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return edge{
|
return NewDirectedNodePair(
|
||||||
from: route.Hops[failureSource-1].PubKeyBytes,
|
route.Hops[failureSource-1].PubKeyBytes,
|
||||||
to: route.Hops[failureSource].PubKeyBytes,
|
route.Hops[failureSource].PubKeyBytes,
|
||||||
channel: route.Hops[failureSource].ChannelID,
|
), route.Hops[failureSource-1].AmtToForward
|
||||||
}, route.Hops[failureSource-1].AmtToForward
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mcTestEdge = EdgeLocator{
|
|
||||||
ChannelID: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
mcTestRoute = &route.Route{
|
mcTestRoute = &route.Route{
|
||||||
SourcePubKey: route.Vertex{10},
|
SourcePubKey: route.Vertex{10},
|
||||||
Hops: []*route.Hop{
|
Hops: []*route.Hop{
|
||||||
|
@ -94,14 +90,13 @@ func (ctx *mcTestContext) cleanup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert that mission control returns a probability for an edge.
|
// Assert that mission control returns a probability for an edge.
|
||||||
func (ctx *mcTestContext) expectP(amt lnwire.MilliSatoshi,
|
func (ctx *mcTestContext) expectP(amt lnwire.MilliSatoshi, expected float64) {
|
||||||
expected float64) {
|
|
||||||
|
|
||||||
ctx.t.Helper()
|
ctx.t.Helper()
|
||||||
|
|
||||||
p := ctx.mc.GetEdgeProbability(mcTestNode1, mcTestEdge, amt)
|
p := ctx.mc.GetProbability(mcTestNode1, mcTestNode2, amt)
|
||||||
if p != expected {
|
if p != expected {
|
||||||
ctx.t.Fatalf("unexpected probability %v", p)
|
ctx.t.Fatalf("expected probability %v but got %v", expected, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +170,7 @@ func TestMissionControl(t *testing.T) {
|
||||||
t.Fatal("unexpected number of nodes")
|
t.Fatal("unexpected number of nodes")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(history.Nodes[0].Channels) != 1 {
|
if len(history.Pairs) != 1 {
|
||||||
t.Fatal("unexpected number of channels")
|
t.Fatal("unexpected number of channels")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +179,6 @@ func TestMissionControl(t *testing.T) {
|
||||||
// penalizing the channel yet.
|
// penalizing the channel yet.
|
||||||
func TestMissionControlChannelUpdate(t *testing.T) {
|
func TestMissionControlChannelUpdate(t *testing.T) {
|
||||||
ctx := createMcTestContext(t)
|
ctx := createMcTestContext(t)
|
||||||
defer ctx.cleanup()
|
|
||||||
|
|
||||||
// Report a policy related failure. Because it is the first, we don't
|
// Report a policy related failure. Because it is the first, we don't
|
||||||
// expect a penalty.
|
// expect a penalty.
|
||||||
|
|
|
@ -105,15 +105,7 @@ func (m *mockMissionControl) ReportPaymentFail(paymentID uint64,
|
||||||
return false, 0, nil
|
return false, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockMissionControl) ReportEdgeFailure(failedEdge edge,
|
func (m *mockMissionControl) GetProbability(fromNode, toNode route.Vertex,
|
||||||
minPenalizeAmt lnwire.MilliSatoshi) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockMissionControl) ReportEdgePolicyFailure(failedEdge edge) {}
|
|
||||||
|
|
||||||
func (m *mockMissionControl) ReportVertexFailure(v route.Vertex) {}
|
|
||||||
|
|
||||||
func (m *mockMissionControl) GetEdgeProbability(fromNode route.Vertex, edge EdgeLocator,
|
|
||||||
amt lnwire.MilliSatoshi) float64 {
|
amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
@ -138,12 +130,6 @@ func (m *mockPaymentSession) RequestRoute(payment *LightningPayment,
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockPaymentSession) ReportVertexFailure(v route.Vertex) {}
|
|
||||||
|
|
||||||
func (m *mockPaymentSession) ReportEdgeFailure(failedEdge edge, minPenalizeAmt lnwire.MilliSatoshi) {}
|
|
||||||
|
|
||||||
func (m *mockPaymentSession) ReportEdgePolicyFailure(failedEdge edge) {}
|
|
||||||
|
|
||||||
type mockPayer struct {
|
type mockPayer struct {
|
||||||
sendResult chan error
|
sendResult chan error
|
||||||
paymentResultErr chan error
|
paymentResultErr chan error
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,3 +10,21 @@ import (
|
||||||
type DirectedNodePair struct {
|
type DirectedNodePair struct {
|
||||||
From, To route.Vertex
|
From, To route.Vertex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDirectedNodePair instantiates a new DirectedNodePair struct.
|
||||||
|
func NewDirectedNodePair(from, to route.Vertex) DirectedNodePair {
|
||||||
|
return DirectedNodePair{
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts a node pair to its human readable representation.
|
||||||
|
func (d DirectedNodePair) String() string {
|
||||||
|
return fmt.Sprintf("%v -> %v", d.From, d.To)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse returns a reversed copy of the pair.
|
||||||
|
func (d DirectedNodePair) Reverse() DirectedNodePair {
|
||||||
|
return DirectedNodePair{From: d.To, To: d.From}
|
||||||
|
}
|
||||||
|
|
|
@ -246,7 +246,7 @@ type graphParams struct {
|
||||||
type RestrictParams struct {
|
type RestrictParams struct {
|
||||||
// ProbabilitySource is a callback that is expected to return the
|
// ProbabilitySource is a callback that is expected to return the
|
||||||
// success probability of traversing the channel from the node.
|
// success probability of traversing the channel from the node.
|
||||||
ProbabilitySource func(route.Vertex, EdgeLocator,
|
ProbabilitySource func(route.Vertex, route.Vertex,
|
||||||
lnwire.MilliSatoshi) float64
|
lnwire.MilliSatoshi) float64
|
||||||
|
|
||||||
// FeeLimit is a maximum fee amount allowed to be used on the path from
|
// FeeLimit is a maximum fee amount allowed to be used on the path from
|
||||||
|
@ -401,14 +401,12 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
||||||
amountToSend := toNodeDist.amountToReceive
|
amountToSend := toNodeDist.amountToReceive
|
||||||
|
|
||||||
// Request the success probability for this edge.
|
// Request the success probability for this edge.
|
||||||
locator := newEdgeLocator(edge)
|
|
||||||
edgeProbability := r.ProbabilitySource(
|
edgeProbability := r.ProbabilitySource(
|
||||||
fromVertex, *locator, amountToSend,
|
fromVertex, toNode, amountToSend,
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Tracef("path finding probability: fromnode=%v, chanid=%v, "+
|
log.Tracef("path finding probability: fromnode=%v, tonode=%v, "+
|
||||||
"probability=%v", fromVertex, locator.ChannelID,
|
"probability=%v", fromVertex, toNode, edgeProbability)
|
||||||
edgeProbability)
|
|
||||||
|
|
||||||
// If the probability is zero, there is no point in trying.
|
// If the probability is zero, there is no point in trying.
|
||||||
if edgeProbability == 0 {
|
if edgeProbability == 0 {
|
||||||
|
|
|
@ -77,7 +77,7 @@ var (
|
||||||
|
|
||||||
// noProbabilitySource is used in testing to return the same probability 1 for
|
// noProbabilitySource is used in testing to return the same probability 1 for
|
||||||
// all edges.
|
// all edges.
|
||||||
func noProbabilitySource(route.Vertex, EdgeLocator, lnwire.MilliSatoshi) float64 {
|
func noProbabilitySource(route.Vertex, route.Vertex, lnwire.MilliSatoshi) float64 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2156,6 +2156,8 @@ func testProbabilityRouting(t *testing.T, p10, p11, p20, minProbability float64,
|
||||||
}
|
}
|
||||||
defer testGraphInstance.cleanUp()
|
defer testGraphInstance.cleanUp()
|
||||||
|
|
||||||
|
alias := testGraphInstance.aliasMap
|
||||||
|
|
||||||
sourceNode, err := testGraphInstance.graph.SourceNode()
|
sourceNode, err := testGraphInstance.graph.SourceNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to fetch source node: %v", err)
|
t.Fatalf("unable to fetch source node: %v", err)
|
||||||
|
@ -2166,19 +2168,19 @@ func testProbabilityRouting(t *testing.T, p10, p11, p20, minProbability float64,
|
||||||
target := testGraphInstance.aliasMap["target"]
|
target := testGraphInstance.aliasMap["target"]
|
||||||
|
|
||||||
// Configure a probability source with the test parameters.
|
// Configure a probability source with the test parameters.
|
||||||
probabilitySource := func(node route.Vertex, edge EdgeLocator,
|
probabilitySource := func(fromNode, toNode route.Vertex,
|
||||||
amt lnwire.MilliSatoshi) float64 {
|
amt lnwire.MilliSatoshi) float64 {
|
||||||
|
|
||||||
if amt == 0 {
|
if amt == 0 {
|
||||||
t.Fatal("expected non-zero amount")
|
t.Fatal("expected non-zero amount")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch edge.ChannelID {
|
switch {
|
||||||
case 10:
|
case fromNode == alias["a1"] && toNode == alias["a2"]:
|
||||||
return p10
|
return p10
|
||||||
case 11:
|
case fromNode == alias["a2"] && toNode == alias["target"]:
|
||||||
return p11
|
return p11
|
||||||
case 20:
|
case fromNode == alias["b"] && toNode == alias["target"]:
|
||||||
return p20
|
return p20
|
||||||
default:
|
default:
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -91,7 +91,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
||||||
ss := p.sessionSource
|
ss := p.sessionSource
|
||||||
|
|
||||||
restrictions := &RestrictParams{
|
restrictions := &RestrictParams{
|
||||||
ProbabilitySource: ss.MissionControl.GetEdgeProbability,
|
ProbabilitySource: ss.MissionControl.GetProbability,
|
||||||
FeeLimit: payment.FeeLimit,
|
FeeLimit: payment.FeeLimit,
|
||||||
OutgoingChannelID: payment.OutgoingChannelID,
|
OutgoingChannelID: payment.OutgoingChannelID,
|
||||||
CltvLimit: cltvLimit,
|
CltvLimit: cltvLimit,
|
||||||
|
|
|
@ -11,13 +11,16 @@ import (
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// VertexSize is the size of the array to store a vertex.
|
||||||
|
const VertexSize = 33
|
||||||
|
|
||||||
// ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
|
// ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
|
||||||
// sphinx packet, but provides an empty set of hops for each route.
|
// sphinx packet, but provides an empty set of hops for each route.
|
||||||
var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")
|
var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")
|
||||||
|
|
||||||
// Vertex is a simple alias for the serialization of a compressed Bitcoin
|
// Vertex is a simple alias for the serialization of a compressed Bitcoin
|
||||||
// public key.
|
// public key.
|
||||||
type Vertex [33]byte
|
type Vertex [VertexSize]byte
|
||||||
|
|
||||||
// NewVertex returns a new Vertex given a public key.
|
// NewVertex returns a new Vertex given a public key.
|
||||||
func NewVertex(pub *btcec.PublicKey) Vertex {
|
func NewVertex(pub *btcec.PublicKey) Vertex {
|
||||||
|
@ -26,6 +29,20 @@ func NewVertex(pub *btcec.PublicKey) Vertex {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewVertexFromBytes returns a new Vertex based on a serialized pubkey in a
|
||||||
|
// byte slice.
|
||||||
|
func NewVertexFromBytes(b []byte) (Vertex, error) {
|
||||||
|
vertexLen := len(b)
|
||||||
|
if vertexLen != VertexSize {
|
||||||
|
return Vertex{}, fmt.Errorf("invalid vertex length of %v, "+
|
||||||
|
"want %v", vertexLen, VertexSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
var v Vertex
|
||||||
|
copy(v[:], b)
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
// String returns a human readable version of the Vertex which is the
|
// String returns a human readable version of the Vertex which is the
|
||||||
// hex-encoding of the serialized compressed public key.
|
// hex-encoding of the serialized compressed public key.
|
||||||
func (v Vertex) String() string {
|
func (v Vertex) String() string {
|
||||||
|
|
|
@ -182,9 +182,9 @@ type MissionController interface {
|
||||||
failureSourceIdx *int, failure lnwire.FailureMessage) (bool,
|
failureSourceIdx *int, failure lnwire.FailureMessage) (bool,
|
||||||
channeldb.FailureReason, error)
|
channeldb.FailureReason, error)
|
||||||
|
|
||||||
// GetEdgeProbability 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.
|
||||||
GetEdgeProbability(fromNode route.Vertex, edge EdgeLocator,
|
GetProbability(fromNode, toNode route.Vertex,
|
||||||
amt lnwire.MilliSatoshi) float64
|
amt lnwire.MilliSatoshi) float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,13 +350,6 @@ func (e *EdgeLocator) String() string {
|
||||||
return fmt.Sprintf("%v:%v", e.ChannelID, e.Direction)
|
return fmt.Sprintf("%v:%v", e.ChannelID, e.Direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// edge is a combination of a channel and the node pubkeys of both of its
|
|
||||||
// endpoints.
|
|
||||||
type edge struct {
|
|
||||||
from, to route.Vertex
|
|
||||||
channel uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChannelRouter is the layer 3 router within the Lightning stack. Below the
|
// ChannelRouter is the layer 3 router within the Lightning stack. Below the
|
||||||
// ChannelRouter is the HtlcSwitch, and below that is the Bitcoin blockchain
|
// ChannelRouter is the HtlcSwitch, and below that is the Bitcoin blockchain
|
||||||
// itself. The primary role of the ChannelRouter is to respond to queries for
|
// itself. The primary role of the ChannelRouter is to respond to queries for
|
||||||
|
|
|
@ -2960,6 +2960,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
||||||
ChainView: chainView,
|
ChainView: chainView,
|
||||||
Control: control,
|
Control: control,
|
||||||
SessionSource: &mockPaymentSessionSource{},
|
SessionSource: &mockPaymentSessionSource{},
|
||||||
|
MissionControl: &mockMissionControl{},
|
||||||
Payer: payer,
|
Payer: payer,
|
||||||
ChannelPruneExpiry: time.Hour * 24,
|
ChannelPruneExpiry: time.Hour * 24,
|
||||||
GraphPruneInterval: time.Hour * 2,
|
GraphPruneInterval: time.Hour * 2,
|
||||||
|
|
Loading…
Add table
Reference in a new issue