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