routing+routerrpc: notify full payment structures

This commit fixes the inconsistency between the payment state as
reported by routerrpc.SendPayment/routerrpc.TrackPayment and the main
rpc ListPayments call.

In addition to that, payment state changes are now sent out for every
state change. This opens the door to user interfaces giving more
feedback to the user about the payment process. This is especially
interesting for multi-part payments.
This commit is contained in:
Joost Jager 2020-04-03 17:05:05 +02:00
parent 5bebda8c5d
commit af4abe7d58
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
8 changed files with 392 additions and 617 deletions

@ -393,14 +393,14 @@ func sendPaymentRequest(ctx *cli.Context,
return err return err
} }
if status.State != routerrpc.PaymentState_IN_FLIGHT { if status.Status != lnrpc.Payment_IN_FLIGHT {
printRespJSON(status) printRespJSON(status)
// If we get a payment error back, we pass an error up // If we get a payment error back, we pass an error up
// to main which eventually calls fatal() and returns // to main which eventually calls fatal() and returns
// with a non-zero exit code. // with a non-zero exit code.
if status.State != routerrpc.PaymentState_SUCCEEDED { if status.Status != lnrpc.Payment_SUCCEEDED {
return errors.New(status.State.String()) return errors.New(status.Status.String())
} }
return nil return nil
@ -454,7 +454,7 @@ func trackPayment(ctx *cli.Context) error {
printRespJSON(status) printRespJSON(status)
if status.State != routerrpc.PaymentState_IN_FLIGHT { if status.Status != lnrpc.Payment_IN_FLIGHT {
return nil return nil
} }
} }

@ -23,62 +23,6 @@ var _ = math.Inf
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type PaymentState int32
const (
//*
//Payment is still in flight.
PaymentState_IN_FLIGHT PaymentState = 0
//*
//Payment completed successfully.
PaymentState_SUCCEEDED PaymentState = 1
//*
//There are more routes to try, but the payment timeout was exceeded.
PaymentState_FAILED_TIMEOUT PaymentState = 2
//*
//All possible routes were tried and failed permanently. Or were no
//routes to the destination at all.
PaymentState_FAILED_NO_ROUTE PaymentState = 3
//*
//A non-recoverable error has occured.
PaymentState_FAILED_ERROR PaymentState = 4
//*
//Payment details incorrect (unknown hash, invalid amt or
//invalid final cltv delta)
PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS PaymentState = 5
//*
//Insufficient local balance.
PaymentState_FAILED_INSUFFICIENT_BALANCE PaymentState = 6
)
var PaymentState_name = map[int32]string{
0: "IN_FLIGHT",
1: "SUCCEEDED",
2: "FAILED_TIMEOUT",
3: "FAILED_NO_ROUTE",
4: "FAILED_ERROR",
5: "FAILED_INCORRECT_PAYMENT_DETAILS",
6: "FAILED_INSUFFICIENT_BALANCE",
}
var PaymentState_value = map[string]int32{
"IN_FLIGHT": 0,
"SUCCEEDED": 1,
"FAILED_TIMEOUT": 2,
"FAILED_NO_ROUTE": 3,
"FAILED_ERROR": 4,
"FAILED_INCORRECT_PAYMENT_DETAILS": 5,
"FAILED_INSUFFICIENT_BALANCE": 6,
}
func (x PaymentState) String() string {
return proto.EnumName(PaymentState_name, int32(x))
}
func (PaymentState) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{0}
}
type FailureDetail int32 type FailureDetail int32
const ( const (
@ -164,7 +108,7 @@ func (x FailureDetail) String() string {
} }
func (FailureDetail) EnumDescriptor() ([]byte, []int) { func (FailureDetail) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{1} return fileDescriptor_7a0613f69d37b0a5, []int{0}
} }
type HtlcEvent_EventType int32 type HtlcEvent_EventType int32
@ -195,7 +139,7 @@ func (x HtlcEvent_EventType) String() string {
} }
func (HtlcEvent_EventType) EnumDescriptor() ([]byte, []int) { func (HtlcEvent_EventType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{18, 0} return fileDescriptor_7a0613f69d37b0a5, []int{17, 0}
} }
type SendPaymentRequest struct { type SendPaymentRequest struct {
@ -460,66 +404,6 @@ func (m *TrackPaymentRequest) GetPaymentHash() []byte {
return nil return nil
} }
type PaymentStatus struct {
/// Current state the payment is in.
State PaymentState `protobuf:"varint,1,opt,name=state,proto3,enum=routerrpc.PaymentState" json:"state,omitempty"`
//*
//The pre-image of the payment when state is SUCCEEDED.
Preimage []byte `protobuf:"bytes,2,opt,name=preimage,proto3" json:"preimage,omitempty"`
//*
//The HTLCs made in attempt to settle the payment.
Htlcs []*lnrpc.HTLCAttempt `protobuf:"bytes,4,rep,name=htlcs,proto3" json:"htlcs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PaymentStatus) Reset() { *m = PaymentStatus{} }
func (m *PaymentStatus) String() string { return proto.CompactTextString(m) }
func (*PaymentStatus) ProtoMessage() {}
func (*PaymentStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{2}
}
func (m *PaymentStatus) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PaymentStatus.Unmarshal(m, b)
}
func (m *PaymentStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PaymentStatus.Marshal(b, m, deterministic)
}
func (m *PaymentStatus) XXX_Merge(src proto.Message) {
xxx_messageInfo_PaymentStatus.Merge(m, src)
}
func (m *PaymentStatus) XXX_Size() int {
return xxx_messageInfo_PaymentStatus.Size(m)
}
func (m *PaymentStatus) XXX_DiscardUnknown() {
xxx_messageInfo_PaymentStatus.DiscardUnknown(m)
}
var xxx_messageInfo_PaymentStatus proto.InternalMessageInfo
func (m *PaymentStatus) GetState() PaymentState {
if m != nil {
return m.State
}
return PaymentState_IN_FLIGHT
}
func (m *PaymentStatus) GetPreimage() []byte {
if m != nil {
return m.Preimage
}
return nil
}
func (m *PaymentStatus) GetHtlcs() []*lnrpc.HTLCAttempt {
if m != nil {
return m.Htlcs
}
return nil
}
type RouteFeeRequest struct { type RouteFeeRequest struct {
//* //*
//The destination once wishes to obtain a routing fee quote to. //The destination once wishes to obtain a routing fee quote to.
@ -536,7 +420,7 @@ func (m *RouteFeeRequest) Reset() { *m = RouteFeeRequest{} }
func (m *RouteFeeRequest) String() string { return proto.CompactTextString(m) } func (m *RouteFeeRequest) String() string { return proto.CompactTextString(m) }
func (*RouteFeeRequest) ProtoMessage() {} func (*RouteFeeRequest) ProtoMessage() {}
func (*RouteFeeRequest) Descriptor() ([]byte, []int) { func (*RouteFeeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{3} return fileDescriptor_7a0613f69d37b0a5, []int{2}
} }
func (m *RouteFeeRequest) XXX_Unmarshal(b []byte) error { func (m *RouteFeeRequest) XXX_Unmarshal(b []byte) error {
@ -590,7 +474,7 @@ func (m *RouteFeeResponse) Reset() { *m = RouteFeeResponse{} }
func (m *RouteFeeResponse) String() string { return proto.CompactTextString(m) } func (m *RouteFeeResponse) String() string { return proto.CompactTextString(m) }
func (*RouteFeeResponse) ProtoMessage() {} func (*RouteFeeResponse) ProtoMessage() {}
func (*RouteFeeResponse) Descriptor() ([]byte, []int) { func (*RouteFeeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{4} return fileDescriptor_7a0613f69d37b0a5, []int{3}
} }
func (m *RouteFeeResponse) XXX_Unmarshal(b []byte) error { func (m *RouteFeeResponse) XXX_Unmarshal(b []byte) error {
@ -639,7 +523,7 @@ func (m *SendToRouteRequest) Reset() { *m = SendToRouteRequest{} }
func (m *SendToRouteRequest) String() string { return proto.CompactTextString(m) } func (m *SendToRouteRequest) String() string { return proto.CompactTextString(m) }
func (*SendToRouteRequest) ProtoMessage() {} func (*SendToRouteRequest) ProtoMessage() {}
func (*SendToRouteRequest) Descriptor() ([]byte, []int) { func (*SendToRouteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{5} return fileDescriptor_7a0613f69d37b0a5, []int{4}
} }
func (m *SendToRouteRequest) XXX_Unmarshal(b []byte) error { func (m *SendToRouteRequest) XXX_Unmarshal(b []byte) error {
@ -688,7 +572,7 @@ func (m *SendToRouteResponse) Reset() { *m = SendToRouteResponse{} }
func (m *SendToRouteResponse) String() string { return proto.CompactTextString(m) } func (m *SendToRouteResponse) String() string { return proto.CompactTextString(m) }
func (*SendToRouteResponse) ProtoMessage() {} func (*SendToRouteResponse) ProtoMessage() {}
func (*SendToRouteResponse) Descriptor() ([]byte, []int) { func (*SendToRouteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{6} return fileDescriptor_7a0613f69d37b0a5, []int{5}
} }
func (m *SendToRouteResponse) XXX_Unmarshal(b []byte) error { func (m *SendToRouteResponse) XXX_Unmarshal(b []byte) error {
@ -733,7 +617,7 @@ func (m *ResetMissionControlRequest) Reset() { *m = ResetMissionControlR
func (m *ResetMissionControlRequest) String() string { return proto.CompactTextString(m) } func (m *ResetMissionControlRequest) String() string { return proto.CompactTextString(m) }
func (*ResetMissionControlRequest) ProtoMessage() {} func (*ResetMissionControlRequest) ProtoMessage() {}
func (*ResetMissionControlRequest) Descriptor() ([]byte, []int) { func (*ResetMissionControlRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{7} return fileDescriptor_7a0613f69d37b0a5, []int{6}
} }
func (m *ResetMissionControlRequest) XXX_Unmarshal(b []byte) error { func (m *ResetMissionControlRequest) XXX_Unmarshal(b []byte) error {
@ -764,7 +648,7 @@ func (m *ResetMissionControlResponse) Reset() { *m = ResetMissionControl
func (m *ResetMissionControlResponse) String() string { return proto.CompactTextString(m) } func (m *ResetMissionControlResponse) String() string { return proto.CompactTextString(m) }
func (*ResetMissionControlResponse) ProtoMessage() {} func (*ResetMissionControlResponse) ProtoMessage() {}
func (*ResetMissionControlResponse) Descriptor() ([]byte, []int) { func (*ResetMissionControlResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{8} return fileDescriptor_7a0613f69d37b0a5, []int{7}
} }
func (m *ResetMissionControlResponse) XXX_Unmarshal(b []byte) error { func (m *ResetMissionControlResponse) XXX_Unmarshal(b []byte) error {
@ -795,7 +679,7 @@ func (m *QueryMissionControlRequest) Reset() { *m = QueryMissionControlR
func (m *QueryMissionControlRequest) String() string { return proto.CompactTextString(m) } func (m *QueryMissionControlRequest) String() string { return proto.CompactTextString(m) }
func (*QueryMissionControlRequest) ProtoMessage() {} func (*QueryMissionControlRequest) ProtoMessage() {}
func (*QueryMissionControlRequest) Descriptor() ([]byte, []int) { func (*QueryMissionControlRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{9} return fileDescriptor_7a0613f69d37b0a5, []int{8}
} }
func (m *QueryMissionControlRequest) XXX_Unmarshal(b []byte) error { func (m *QueryMissionControlRequest) XXX_Unmarshal(b []byte) error {
@ -829,7 +713,7 @@ func (m *QueryMissionControlResponse) Reset() { *m = QueryMissionControl
func (m *QueryMissionControlResponse) String() string { return proto.CompactTextString(m) } func (m *QueryMissionControlResponse) String() string { return proto.CompactTextString(m) }
func (*QueryMissionControlResponse) ProtoMessage() {} func (*QueryMissionControlResponse) ProtoMessage() {}
func (*QueryMissionControlResponse) Descriptor() ([]byte, []int) { func (*QueryMissionControlResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{10} return fileDescriptor_7a0613f69d37b0a5, []int{9}
} }
func (m *QueryMissionControlResponse) XXX_Unmarshal(b []byte) error { func (m *QueryMissionControlResponse) XXX_Unmarshal(b []byte) error {
@ -873,7 +757,7 @@ func (m *PairHistory) Reset() { *m = PairHistory{} }
func (m *PairHistory) String() string { return proto.CompactTextString(m) } func (m *PairHistory) String() string { return proto.CompactTextString(m) }
func (*PairHistory) ProtoMessage() {} func (*PairHistory) ProtoMessage() {}
func (*PairHistory) Descriptor() ([]byte, []int) { func (*PairHistory) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{11} return fileDescriptor_7a0613f69d37b0a5, []int{10}
} }
func (m *PairHistory) XXX_Unmarshal(b []byte) error { func (m *PairHistory) XXX_Unmarshal(b []byte) error {
@ -941,7 +825,7 @@ func (m *PairData) Reset() { *m = PairData{} }
func (m *PairData) String() string { return proto.CompactTextString(m) } func (m *PairData) String() string { return proto.CompactTextString(m) }
func (*PairData) ProtoMessage() {} func (*PairData) ProtoMessage() {}
func (*PairData) Descriptor() ([]byte, []int) { func (*PairData) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{12} return fileDescriptor_7a0613f69d37b0a5, []int{11}
} }
func (m *PairData) XXX_Unmarshal(b []byte) error { func (m *PairData) XXX_Unmarshal(b []byte) error {
@ -1020,7 +904,7 @@ func (m *QueryProbabilityRequest) Reset() { *m = QueryProbabilityRequest
func (m *QueryProbabilityRequest) String() string { return proto.CompactTextString(m) } func (m *QueryProbabilityRequest) String() string { return proto.CompactTextString(m) }
func (*QueryProbabilityRequest) ProtoMessage() {} func (*QueryProbabilityRequest) ProtoMessage() {}
func (*QueryProbabilityRequest) Descriptor() ([]byte, []int) { func (*QueryProbabilityRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{13} return fileDescriptor_7a0613f69d37b0a5, []int{12}
} }
func (m *QueryProbabilityRequest) XXX_Unmarshal(b []byte) error { func (m *QueryProbabilityRequest) XXX_Unmarshal(b []byte) error {
@ -1076,7 +960,7 @@ func (m *QueryProbabilityResponse) Reset() { *m = QueryProbabilityRespon
func (m *QueryProbabilityResponse) String() string { return proto.CompactTextString(m) } func (m *QueryProbabilityResponse) String() string { return proto.CompactTextString(m) }
func (*QueryProbabilityResponse) ProtoMessage() {} func (*QueryProbabilityResponse) ProtoMessage() {}
func (*QueryProbabilityResponse) Descriptor() ([]byte, []int) { func (*QueryProbabilityResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{14} return fileDescriptor_7a0613f69d37b0a5, []int{13}
} }
func (m *QueryProbabilityResponse) XXX_Unmarshal(b []byte) error { func (m *QueryProbabilityResponse) XXX_Unmarshal(b []byte) error {
@ -1137,7 +1021,7 @@ func (m *BuildRouteRequest) Reset() { *m = BuildRouteRequest{} }
func (m *BuildRouteRequest) String() string { return proto.CompactTextString(m) } func (m *BuildRouteRequest) String() string { return proto.CompactTextString(m) }
func (*BuildRouteRequest) ProtoMessage() {} func (*BuildRouteRequest) ProtoMessage() {}
func (*BuildRouteRequest) Descriptor() ([]byte, []int) { func (*BuildRouteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{15} return fileDescriptor_7a0613f69d37b0a5, []int{14}
} }
func (m *BuildRouteRequest) XXX_Unmarshal(b []byte) error { func (m *BuildRouteRequest) XXX_Unmarshal(b []byte) error {
@ -1199,7 +1083,7 @@ func (m *BuildRouteResponse) Reset() { *m = BuildRouteResponse{} }
func (m *BuildRouteResponse) String() string { return proto.CompactTextString(m) } func (m *BuildRouteResponse) String() string { return proto.CompactTextString(m) }
func (*BuildRouteResponse) ProtoMessage() {} func (*BuildRouteResponse) ProtoMessage() {}
func (*BuildRouteResponse) Descriptor() ([]byte, []int) { func (*BuildRouteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{16} return fileDescriptor_7a0613f69d37b0a5, []int{15}
} }
func (m *BuildRouteResponse) XXX_Unmarshal(b []byte) error { func (m *BuildRouteResponse) XXX_Unmarshal(b []byte) error {
@ -1237,7 +1121,7 @@ func (m *SubscribeHtlcEventsRequest) Reset() { *m = SubscribeHtlcEventsR
func (m *SubscribeHtlcEventsRequest) String() string { return proto.CompactTextString(m) } func (m *SubscribeHtlcEventsRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeHtlcEventsRequest) ProtoMessage() {} func (*SubscribeHtlcEventsRequest) ProtoMessage() {}
func (*SubscribeHtlcEventsRequest) Descriptor() ([]byte, []int) { func (*SubscribeHtlcEventsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{17} return fileDescriptor_7a0613f69d37b0a5, []int{16}
} }
func (m *SubscribeHtlcEventsRequest) XXX_Unmarshal(b []byte) error { func (m *SubscribeHtlcEventsRequest) XXX_Unmarshal(b []byte) error {
@ -1304,7 +1188,7 @@ func (m *HtlcEvent) Reset() { *m = HtlcEvent{} }
func (m *HtlcEvent) String() string { return proto.CompactTextString(m) } func (m *HtlcEvent) String() string { return proto.CompactTextString(m) }
func (*HtlcEvent) ProtoMessage() {} func (*HtlcEvent) ProtoMessage() {}
func (*HtlcEvent) Descriptor() ([]byte, []int) { func (*HtlcEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{18} return fileDescriptor_7a0613f69d37b0a5, []int{17}
} }
func (m *HtlcEvent) XXX_Unmarshal(b []byte) error { func (m *HtlcEvent) XXX_Unmarshal(b []byte) error {
@ -1458,7 +1342,7 @@ func (m *HtlcInfo) Reset() { *m = HtlcInfo{} }
func (m *HtlcInfo) String() string { return proto.CompactTextString(m) } func (m *HtlcInfo) String() string { return proto.CompactTextString(m) }
func (*HtlcInfo) ProtoMessage() {} func (*HtlcInfo) ProtoMessage() {}
func (*HtlcInfo) Descriptor() ([]byte, []int) { func (*HtlcInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{19} return fileDescriptor_7a0613f69d37b0a5, []int{18}
} }
func (m *HtlcInfo) XXX_Unmarshal(b []byte) error { func (m *HtlcInfo) XXX_Unmarshal(b []byte) error {
@ -1519,7 +1403,7 @@ func (m *ForwardEvent) Reset() { *m = ForwardEvent{} }
func (m *ForwardEvent) String() string { return proto.CompactTextString(m) } func (m *ForwardEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardEvent) ProtoMessage() {} func (*ForwardEvent) ProtoMessage() {}
func (*ForwardEvent) Descriptor() ([]byte, []int) { func (*ForwardEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{20} return fileDescriptor_7a0613f69d37b0a5, []int{19}
} }
func (m *ForwardEvent) XXX_Unmarshal(b []byte) error { func (m *ForwardEvent) XXX_Unmarshal(b []byte) error {
@ -1557,7 +1441,7 @@ func (m *ForwardFailEvent) Reset() { *m = ForwardFailEvent{} }
func (m *ForwardFailEvent) String() string { return proto.CompactTextString(m) } func (m *ForwardFailEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardFailEvent) ProtoMessage() {} func (*ForwardFailEvent) ProtoMessage() {}
func (*ForwardFailEvent) Descriptor() ([]byte, []int) { func (*ForwardFailEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{21} return fileDescriptor_7a0613f69d37b0a5, []int{20}
} }
func (m *ForwardFailEvent) XXX_Unmarshal(b []byte) error { func (m *ForwardFailEvent) XXX_Unmarshal(b []byte) error {
@ -1588,7 +1472,7 @@ func (m *SettleEvent) Reset() { *m = SettleEvent{} }
func (m *SettleEvent) String() string { return proto.CompactTextString(m) } func (m *SettleEvent) String() string { return proto.CompactTextString(m) }
func (*SettleEvent) ProtoMessage() {} func (*SettleEvent) ProtoMessage() {}
func (*SettleEvent) Descriptor() ([]byte, []int) { func (*SettleEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{22} return fileDescriptor_7a0613f69d37b0a5, []int{21}
} }
func (m *SettleEvent) XXX_Unmarshal(b []byte) error { func (m *SettleEvent) XXX_Unmarshal(b []byte) error {
@ -1630,7 +1514,7 @@ func (m *LinkFailEvent) Reset() { *m = LinkFailEvent{} }
func (m *LinkFailEvent) String() string { return proto.CompactTextString(m) } func (m *LinkFailEvent) String() string { return proto.CompactTextString(m) }
func (*LinkFailEvent) ProtoMessage() {} func (*LinkFailEvent) ProtoMessage() {}
func (*LinkFailEvent) Descriptor() ([]byte, []int) { func (*LinkFailEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{23} return fileDescriptor_7a0613f69d37b0a5, []int{22}
} }
func (m *LinkFailEvent) XXX_Unmarshal(b []byte) error { func (m *LinkFailEvent) XXX_Unmarshal(b []byte) error {
@ -1680,13 +1564,11 @@ func (m *LinkFailEvent) GetFailureString() string {
} }
func init() { func init() {
proto.RegisterEnum("routerrpc.PaymentState", PaymentState_name, PaymentState_value)
proto.RegisterEnum("routerrpc.FailureDetail", FailureDetail_name, FailureDetail_value) proto.RegisterEnum("routerrpc.FailureDetail", FailureDetail_name, FailureDetail_value)
proto.RegisterEnum("routerrpc.HtlcEvent_EventType", HtlcEvent_EventType_name, HtlcEvent_EventType_value) proto.RegisterEnum("routerrpc.HtlcEvent_EventType", HtlcEvent_EventType_name, HtlcEvent_EventType_value)
proto.RegisterType((*SendPaymentRequest)(nil), "routerrpc.SendPaymentRequest") proto.RegisterType((*SendPaymentRequest)(nil), "routerrpc.SendPaymentRequest")
proto.RegisterMapType((map[uint64][]byte)(nil), "routerrpc.SendPaymentRequest.DestCustomRecordsEntry") proto.RegisterMapType((map[uint64][]byte)(nil), "routerrpc.SendPaymentRequest.DestCustomRecordsEntry")
proto.RegisterType((*TrackPaymentRequest)(nil), "routerrpc.TrackPaymentRequest") proto.RegisterType((*TrackPaymentRequest)(nil), "routerrpc.TrackPaymentRequest")
proto.RegisterType((*PaymentStatus)(nil), "routerrpc.PaymentStatus")
proto.RegisterType((*RouteFeeRequest)(nil), "routerrpc.RouteFeeRequest") proto.RegisterType((*RouteFeeRequest)(nil), "routerrpc.RouteFeeRequest")
proto.RegisterType((*RouteFeeResponse)(nil), "routerrpc.RouteFeeResponse") proto.RegisterType((*RouteFeeResponse)(nil), "routerrpc.RouteFeeResponse")
proto.RegisterType((*SendToRouteRequest)(nil), "routerrpc.SendToRouteRequest") proto.RegisterType((*SendToRouteRequest)(nil), "routerrpc.SendToRouteRequest")
@ -1713,142 +1595,134 @@ func init() {
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
var fileDescriptor_7a0613f69d37b0a5 = []byte{ var fileDescriptor_7a0613f69d37b0a5 = []byte{
// 2150 bytes of a gzipped FileDescriptorProto // 2017 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0xdd, 0x72, 0xdb, 0xb8, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0x5d, 0x77, 0xdb, 0x48,
0x15, 0x0e, 0x6d, 0xca, 0x96, 0x8e, 0x7e, 0x4c, 0x43, 0x59, 0x47, 0x95, 0x93, 0x5d, 0x2d, 0xbb, 0x19, 0x5e, 0xc5, 0x72, 0x62, 0xbf, 0xfe, 0x52, 0xc6, 0xdd, 0xd4, 0x38, 0xed, 0xe2, 0x15, 0x6c,
0x9b, 0x68, 0xd2, 0xac, 0x9d, 0x75, 0x3b, 0x6d, 0xa6, 0xed, 0x6e, 0x47, 0x96, 0xe8, 0x88, 0x8e, 0xeb, 0x53, 0x4a, 0xd2, 0x0d, 0x1c, 0xe8, 0x01, 0xb6, 0xe0, 0x58, 0xca, 0x46, 0x8d, 0x23, 0x79,
0x4c, 0x6a, 0x21, 0x3a, 0x3f, 0xcd, 0x05, 0x86, 0x96, 0x20, 0x8b, 0x35, 0x45, 0xaa, 0x24, 0x94, 0xc7, 0x4e, 0xdb, 0xa5, 0x17, 0x73, 0x14, 0x7b, 0x1c, 0x8b, 0xc8, 0x92, 0x91, 0xc6, 0xed, 0xc9,
0x8c, 0x2f, 0x3b, 0xbd, 0xeb, 0x8b, 0xf4, 0xae, 0x4f, 0xd0, 0x77, 0xe9, 0x6d, 0x9f, 0xa0, 0xd3, 0x25, 0xb7, 0xfc, 0x11, 0xfe, 0x04, 0xff, 0x85, 0x5b, 0xee, 0xb8, 0xe3, 0x70, 0xc9, 0x99, 0x91,
0xcb, 0x1d, 0x80, 0xa0, 0x44, 0xd9, 0x72, 0x76, 0x6f, 0x12, 0xf1, 0x3b, 0x1f, 0x0e, 0xce, 0x39, 0xc6, 0x96, 0x13, 0xa7, 0xbb, 0x37, 0x89, 0xf4, 0xbc, 0xcf, 0xbc, 0x1f, 0xf3, 0x3e, 0xf3, 0x21,
0xf8, 0x80, 0x03, 0x18, 0xf6, 0xa2, 0x70, 0xce, 0x68, 0x14, 0xcd, 0x86, 0x87, 0xc9, 0xaf, 0x83, 0xc3, 0x5e, 0x14, 0x2e, 0x18, 0x8d, 0xa2, 0xf9, 0xe8, 0x30, 0x79, 0x3a, 0x98, 0x47, 0x21, 0x0b,
0x59, 0x14, 0xb2, 0x10, 0x15, 0x16, 0x78, 0xbd, 0x10, 0xcd, 0x86, 0x09, 0xaa, 0xff, 0x3f, 0x07, 0x51, 0x71, 0x89, 0x37, 0x8b, 0xd1, 0x7c, 0x94, 0xa0, 0xfa, 0xff, 0xf2, 0x80, 0x06, 0x34, 0x18,
0x68, 0x40, 0x83, 0x51, 0xdf, 0xbd, 0x9e, 0xd2, 0x80, 0x61, 0xfa, 0xd7, 0x39, 0x8d, 0x19, 0x42, 0xf7, 0xdd, 0x9b, 0x19, 0x0d, 0x18, 0xa6, 0x7f, 0x5d, 0xd0, 0x98, 0x21, 0x04, 0xea, 0x98, 0xc6,
0xa0, 0x8e, 0x68, 0xcc, 0x6a, 0x4a, 0x43, 0x69, 0x96, 0xb0, 0xf8, 0x8d, 0x34, 0xd8, 0x74, 0xa7, 0xac, 0xa1, 0xb4, 0x94, 0x76, 0x19, 0x8b, 0x67, 0xa4, 0x41, 0xce, 0x9d, 0xb1, 0xc6, 0x56, 0x4b,
0xac, 0xb6, 0xd1, 0x50, 0x9a, 0x9b, 0x98, 0xff, 0x44, 0xbf, 0x80, 0xbc, 0x3b, 0x65, 0x64, 0x1a, 0x69, 0xe7, 0x30, 0x7f, 0x44, 0x3f, 0x81, 0x82, 0x3b, 0x63, 0x64, 0x16, 0xbb, 0xac, 0x51, 0x16,
0xbb, 0xac, 0x56, 0x12, 0xf0, 0xb6, 0x3b, 0x65, 0x67, 0xb1, 0xcb, 0xd0, 0x97, 0x50, 0x9a, 0x25, 0xf0, 0x8e, 0x3b, 0x63, 0xe7, 0xb1, 0xcb, 0xd0, 0x97, 0x50, 0x9e, 0x27, 0x2e, 0xc9, 0xd4, 0x8d,
0x2e, 0xc9, 0xc4, 0x8d, 0x27, 0xb5, 0x4d, 0xe1, 0xa8, 0x28, 0xb1, 0xae, 0x1b, 0x4f, 0x50, 0x13, 0xa7, 0x8d, 0x9c, 0x70, 0x54, 0x4a, 0xb1, 0x53, 0x37, 0x9e, 0xa2, 0x36, 0x68, 0x13, 0x2f, 0x70,
0xb4, 0xb1, 0x17, 0xb8, 0x3e, 0x19, 0xfa, 0xec, 0x03, 0x19, 0x51, 0x9f, 0xb9, 0x35, 0xb5, 0xa1, 0x7d, 0x32, 0xf2, 0xd9, 0x07, 0x32, 0xa6, 0x3e, 0x73, 0x1b, 0x6a, 0x4b, 0x69, 0xe7, 0x71, 0x55,
0x34, 0x73, 0xb8, 0x22, 0xf0, 0xb6, 0xcf, 0x3e, 0x74, 0x38, 0x8a, 0x9e, 0xc0, 0x4e, 0xea, 0x2c, 0xe0, 0x5d, 0x9f, 0x7d, 0x30, 0x38, 0x8a, 0x9e, 0x42, 0x4d, 0x3a, 0x8b, 0x92, 0x04, 0x1b, 0xf9,
0x4a, 0x02, 0xac, 0xe5, 0x1a, 0x4a, 0xb3, 0x80, 0x2b, 0xb3, 0xd5, 0xb0, 0x9f, 0xc0, 0x0e, 0xf3, 0x96, 0xd2, 0x2e, 0xe2, 0xea, 0x7c, 0x3d, 0xed, 0xa7, 0x50, 0x63, 0xde, 0x8c, 0x86, 0x0b, 0x46,
0xa6, 0x34, 0x9c, 0x33, 0x12, 0xd3, 0x61, 0x18, 0x8c, 0xe2, 0xda, 0x56, 0xe2, 0x51, 0xc2, 0x83, 0x62, 0x3a, 0x0a, 0x83, 0x71, 0xdc, 0xd8, 0x4e, 0x3c, 0xa6, 0xf0, 0x20, 0x41, 0x91, 0x0e, 0x95,
0x04, 0x45, 0x3a, 0x94, 0xc7, 0x94, 0x12, 0xdf, 0x9b, 0x7a, 0x8c, 0xf0, 0xf0, 0xb7, 0x45, 0xf8, 0x09, 0xa5, 0xc4, 0xf7, 0x66, 0x1e, 0x23, 0x3c, 0xfd, 0x1d, 0x91, 0x7e, 0x69, 0x42, 0x69, 0x8f,
0xc5, 0x31, 0xa5, 0x3d, 0x8e, 0x0d, 0x5c, 0x86, 0xbe, 0x82, 0xca, 0x92, 0x23, 0x72, 0x2c, 0x0b, 0x63, 0x03, 0x97, 0xa1, 0x9f, 0x43, 0x75, 0xc5, 0x11, 0x35, 0x56, 0x04, 0xa9, 0x2c, 0x49, 0xa2,
0x52, 0x29, 0x25, 0x89, 0x44, 0x9f, 0x81, 0x16, 0xce, 0xd9, 0x65, 0xe8, 0x05, 0x97, 0x64, 0x38, 0xd0, 0xe7, 0xa0, 0x85, 0x0b, 0x76, 0x15, 0x7a, 0xc1, 0x15, 0x19, 0x4d, 0xdd, 0x80, 0x78, 0xe3,
0x71, 0x03, 0xe2, 0x8d, 0x6a, 0xf9, 0x86, 0xd2, 0x54, 0x8f, 0x37, 0x9e, 0x2b, 0xb8, 0x92, 0xda, 0x46, 0xa1, 0xa5, 0xb4, 0xd5, 0xe3, 0xad, 0x17, 0x0a, 0xae, 0x4a, 0x5b, 0x77, 0xea, 0x06, 0xd6,
0xda, 0x13, 0x37, 0x30, 0x47, 0xe8, 0x31, 0xec, 0xf8, 0x6e, 0xcc, 0xc8, 0x24, 0x9c, 0x91, 0xd9, 0x18, 0x3d, 0x81, 0x9a, 0xef, 0xc6, 0x8c, 0x4c, 0xc3, 0x39, 0x99, 0x2f, 0x2e, 0xaf, 0xe9, 0x4d,
0xfc, 0xe2, 0x8a, 0x5e, 0xd7, 0x2a, 0xa2, 0x32, 0x65, 0x0e, 0x77, 0xc3, 0x59, 0x5f, 0x80, 0xe8, 0xa3, 0x2a, 0x66, 0xa6, 0xc2, 0xe1, 0xd3, 0x70, 0xde, 0x17, 0x20, 0x7a, 0x0c, 0x20, 0x66, 0x45,
0x11, 0x80, 0xa8, 0x8a, 0x98, 0xbc, 0x56, 0x10, 0x39, 0x14, 0x38, 0x22, 0x26, 0x46, 0xdf, 0x42, 0x04, 0x6f, 0x14, 0x45, 0x0d, 0x45, 0x8e, 0x88, 0xc0, 0xe8, 0x6b, 0x28, 0x89, 0x6e, 0x92, 0xa9,
0x51, 0xac, 0x26, 0x99, 0x78, 0x01, 0x8b, 0x6b, 0xd0, 0xd8, 0x6c, 0x16, 0x8f, 0xb4, 0x03, 0x3f, 0x17, 0xb0, 0xb8, 0x01, 0xad, 0x5c, 0xbb, 0x74, 0xa4, 0x1d, 0xf8, 0x01, 0x6f, 0x2c, 0xe6, 0x96,
0xe0, 0x0b, 0x8b, 0xb9, 0xa5, 0xeb, 0x05, 0x0c, 0x43, 0x94, 0xfe, 0x8c, 0xd1, 0x08, 0xaa, 0x7c, 0x53, 0x2f, 0x60, 0x18, 0x22, 0xf9, 0x18, 0xa3, 0x31, 0xd4, 0x79, 0x17, 0xc9, 0x68, 0x11, 0xb3,
0x15, 0xc9, 0x70, 0x1e, 0xb3, 0x70, 0x4a, 0x22, 0x3a, 0x0c, 0xa3, 0x51, 0x5c, 0x2b, 0x8a, 0xa1, 0x70, 0x46, 0x22, 0x3a, 0x0a, 0xa3, 0x71, 0xdc, 0x28, 0x89, 0xa1, 0xbf, 0x3e, 0x58, 0x8a, 0xe3,
0xbf, 0x39, 0x58, 0x88, 0xe3, 0xe0, 0xb6, 0x1a, 0x0e, 0x3a, 0x34, 0x66, 0x6d, 0x31, 0x0e, 0x27, 0xe0, 0xae, 0x1a, 0x0e, 0x0c, 0x1a, 0xb3, 0xae, 0x18, 0x87, 0x93, 0x61, 0x66, 0xc0, 0xa2, 0x1b,
0xc3, 0x8c, 0x80, 0x45, 0xd7, 0x78, 0x77, 0x74, 0x13, 0x47, 0xcf, 0x00, 0xb9, 0xbe, 0x1f, 0x7e, 0xbc, 0x3b, 0xbe, 0x8d, 0xa3, 0xe7, 0x80, 0x5c, 0xdf, 0x0f, 0x3f, 0x92, 0x98, 0xfa, 0x13, 0x92,
0x24, 0x31, 0xf5, 0xc7, 0x44, 0xae, 0x4e, 0x6d, 0xa7, 0xa1, 0x34, 0xf3, 0x58, 0x13, 0x96, 0x01, 0x76, 0xa7, 0x51, 0x6b, 0x29, 0xed, 0x02, 0xd6, 0x84, 0x65, 0x40, 0xfd, 0x49, 0xea, 0x1e, 0xfd,
0xf5, 0xc7, 0xd2, 0x3d, 0xfa, 0x2d, 0x94, 0x45, 0x4c, 0x63, 0xea, 0xb2, 0x79, 0x44, 0xe3, 0x9a, 0x06, 0x2a, 0x22, 0xa7, 0x09, 0x75, 0xd9, 0x22, 0xa2, 0x71, 0x43, 0x6b, 0xe5, 0xda, 0xd5, 0xa3,
0xd6, 0xd8, 0x6c, 0x56, 0x8e, 0x76, 0x65, 0x22, 0x27, 0x09, 0x7c, 0xec, 0x31, 0x5c, 0xe2, 0x3c, 0xdd, 0xb4, 0x90, 0x93, 0x04, 0x3e, 0xf6, 0x18, 0x2e, 0x73, 0x5e, 0xfa, 0x1e, 0x37, 0x0d, 0xd8,
0xf9, 0x1d, 0xd7, 0x3b, 0xb0, 0xb7, 0x3e, 0x24, 0xae, 0x51, 0x5e, 0x53, 0x2e, 0x5b, 0x15, 0xf3, 0xdb, 0x9c, 0x12, 0xd7, 0x28, 0x9f, 0x53, 0x2e, 0x5b, 0x15, 0xf3, 0x47, 0xf4, 0x00, 0xf2, 0x1f,
0x9f, 0xe8, 0x3e, 0xe4, 0x3e, 0xb8, 0xfe, 0x9c, 0x0a, 0xdd, 0x96, 0x70, 0xf2, 0xf1, 0xfb, 0x8d, 0x5c, 0x7f, 0x41, 0x85, 0x6e, 0xcb, 0x38, 0x79, 0xf9, 0xdd, 0xd6, 0x4b, 0x45, 0x7f, 0x09, 0xf5,
0x17, 0x8a, 0xfe, 0x02, 0xaa, 0x4e, 0xe4, 0x0e, 0xaf, 0x6e, 0x48, 0xff, 0xa6, 0x72, 0x95, 0x5b, 0x61, 0xe4, 0x8e, 0xae, 0x6f, 0x49, 0xff, 0xb6, 0x72, 0x95, 0x3b, 0xca, 0xd5, 0x5f, 0x41, 0x4d,
0xca, 0xd5, 0xff, 0xa1, 0x40, 0x59, 0x8e, 0x1a, 0x30, 0x97, 0xcd, 0x63, 0xf4, 0x0d, 0xe4, 0x62, 0x4c, 0xf2, 0x09, 0xa5, 0x9f, 0x5a, 0x30, 0x0f, 0x81, 0x2f, 0x07, 0x21, 0xaf, 0x64, 0xd1, 0x6c,
0xe6, 0x32, 0x2a, 0xd8, 0x95, 0xa3, 0x07, 0x99, 0x7a, 0x66, 0x88, 0x14, 0x27, 0x2c, 0x54, 0x87, 0xbb, 0x33, 0xae, 0x2c, 0x7d, 0x0c, 0xda, 0x6a, 0x7c, 0x3c, 0x0f, 0x83, 0x98, 0xf2, 0xd5, 0xc0,
0xfc, 0x2c, 0xa2, 0xde, 0xd4, 0xbd, 0x4c, 0xe3, 0x5a, 0x7c, 0xa3, 0x26, 0xe4, 0x26, 0xcc, 0x1f, 0x7b, 0xc0, 0x65, 0xc4, 0x55, 0x27, 0xf4, 0xa6, 0x88, 0x51, 0xd5, 0x14, 0x3f, 0xa1, 0x54, 0x28,
0xc6, 0x35, 0x55, 0x2c, 0x0d, 0x92, 0xc5, 0xe8, 0x3a, 0xbd, 0x76, 0x8b, 0x31, 0x3a, 0x9d, 0x31, 0xee, 0x49, 0x22, 0x72, 0xe2, 0x87, 0xa3, 0x6b, 0xbe, 0x6c, 0xdc, 0x9b, 0xd4, 0x7d, 0x85, 0xc3,
0x9c, 0x10, 0x4e, 0xd5, 0xfc, 0xa6, 0xa6, 0xea, 0xdf, 0xc3, 0x8e, 0x58, 0xf1, 0x13, 0x4a, 0x3f, 0xbd, 0x70, 0x74, 0x6d, 0x70, 0x50, 0x7f, 0x9f, 0xac, 0xec, 0x61, 0x28, 0x62, 0xfd, 0xf8, 0xf2,
0xb5, 0x7b, 0x1f, 0x00, 0xdf, 0x9b, 0x42, 0xeb, 0xc9, 0x0e, 0xde, 0x72, 0xa7, 0x5c, 0xe6, 0xfa, 0x90, 0x0e, 0x79, 0x21, 0x07, 0xe1, 0xb6, 0x74, 0x54, 0xce, 0xea, 0x0a, 0x27, 0x26, 0xfd, 0x3d,
0x08, 0xb4, 0xe5, 0xf8, 0x78, 0x16, 0x06, 0x31, 0x8f, 0x41, 0xe3, 0x09, 0x70, 0x4d, 0xf3, 0x2d, 0xd4, 0xd7, 0x9c, 0xa7, 0x55, 0x34, 0xa1, 0x30, 0x8f, 0xa8, 0x37, 0x73, 0xaf, 0x68, 0xea, 0x79,
0x20, 0xc4, 0xaf, 0x88, 0x51, 0x15, 0x89, 0x9f, 0x50, 0x2a, 0xe4, 0xff, 0x38, 0xd9, 0x71, 0xc4, 0xf9, 0x8e, 0xda, 0xb0, 0x33, 0x71, 0x3d, 0x7f, 0x11, 0x49, 0xc7, 0x55, 0xd9, 0xe7, 0x04, 0xc5,
0x0f, 0x87, 0x57, 0x7c, 0x0f, 0xbb, 0xd7, 0xd2, 0x7d, 0x99, 0xc3, 0xbd, 0x70, 0x78, 0xd5, 0xe1, 0xd2, 0xac, 0x3f, 0x82, 0x26, 0xa6, 0x31, 0x65, 0xe7, 0x5e, 0x1c, 0x7b, 0x61, 0xd0, 0x0d, 0x03,
0xa0, 0xfe, 0x3e, 0x39, 0x66, 0x9c, 0x50, 0xcc, 0xf5, 0xf3, 0x6b, 0x8d, 0x74, 0xc8, 0x89, 0x5a, 0x16, 0x85, 0x7e, 0x5a, 0x81, 0xfe, 0x18, 0xf6, 0x37, 0x5a, 0x93, 0x14, 0xf8, 0xe0, 0xef, 0x16,
0x0a, 0xb7, 0xc5, 0xa3, 0x52, 0x56, 0xe4, 0x38, 0x31, 0xe9, 0xef, 0xa1, 0xba, 0xe2, 0x5c, 0x66, 0x34, 0xba, 0xd9, 0x3c, 0xf8, 0x3b, 0xd8, 0xdf, 0x68, 0x4d, 0xf3, 0x7f, 0x0e, 0xf9, 0xb9, 0xeb,
0x91, 0xad, 0xb2, 0x72, 0xab, 0xca, 0xdb, 0x63, 0xd7, 0xf3, 0xe7, 0x51, 0xea, 0xb8, 0x92, 0x8a, 0x45, 0x71, 0x63, 0x4b, 0xac, 0x8b, 0xbd, 0xcc, 0xba, 0xe8, 0xbb, 0x5e, 0x74, 0xea, 0xc5, 0x2c,
0x2e, 0x41, 0x71, 0x6a, 0xd6, 0x1f, 0x42, 0x1d, 0xd3, 0x98, 0xb2, 0x33, 0x2f, 0x8e, 0xbd, 0x30, 0x8c, 0x6e, 0x70, 0x42, 0x7a, 0xad, 0x16, 0x14, 0x6d, 0x4b, 0xff, 0xbb, 0x02, 0xa5, 0x8c, 0x11,
0x68, 0x87, 0x01, 0x8b, 0x42, 0x5f, 0x66, 0xa0, 0x3f, 0x82, 0xfd, 0xb5, 0xd6, 0x24, 0x04, 0x3e, 0xed, 0x43, 0x31, 0x08, 0xc7, 0x94, 0x4c, 0xa2, 0x70, 0x26, 0x27, 0x81, 0x03, 0x27, 0x51, 0x38,
0xf8, 0x87, 0x39, 0x8d, 0xae, 0xd7, 0x0f, 0xfe, 0x01, 0xf6, 0xd7, 0x5a, 0x65, 0xfc, 0xcf, 0x20, 0xe3, 0x9a, 0x10, 0x46, 0x16, 0xa6, 0x82, 0xdc, 0xe6, 0xaf, 0xc3, 0x10, 0xfd, 0x12, 0x76, 0xa6,
0x37, 0x73, 0xbd, 0x28, 0xae, 0x6d, 0x08, 0x25, 0xec, 0xad, 0x88, 0xca, 0x8b, 0xba, 0x5e, 0xcc, 0x89, 0x03, 0xb1, 0x17, 0x95, 0x8e, 0xea, 0xb7, 0x62, 0x1b, 0x2e, 0x73, 0xb1, 0xe4, 0xbc, 0x56,
0xc2, 0xe8, 0x1a, 0x27, 0xa4, 0x53, 0x35, 0xaf, 0x68, 0x1b, 0x5c, 0x9a, 0xc5, 0x8c, 0x11, 0xed, 0x0b, 0x39, 0x4d, 0x7d, 0xad, 0x16, 0x54, 0x2d, 0xff, 0x5a, 0x2d, 0xe4, 0xb5, 0xed, 0xd7, 0x6a,
0x43, 0x21, 0x08, 0x47, 0x94, 0x8c, 0xa3, 0x70, 0x9a, 0x16, 0x81, 0x03, 0x27, 0x51, 0x38, 0xe5, 0x61, 0x5b, 0xdb, 0xd1, 0xff, 0xad, 0x40, 0x41, 0xb2, 0x79, 0x26, 0x7c, 0x4a, 0x09, 0xd7, 0x45,
0x9a, 0x10, 0x46, 0x16, 0x4a, 0x15, 0x6e, 0xf1, 0x4f, 0x27, 0x44, 0xdf, 0xc0, 0xf6, 0x24, 0x71, 0x2a, 0xa6, 0x02, 0x07, 0x86, 0xde, 0x8c, 0xa2, 0x16, 0x94, 0x85, 0x71, 0x5d, 0xa2, 0xc0, 0xb1,
0x20, 0x0e, 0xc6, 0xe2, 0x51, 0xf5, 0xc6, 0xdc, 0x1d, 0x97, 0xb9, 0x38, 0xe5, 0x24, 0x42, 0x3c, 0x8e, 0x90, 0xa9, 0xd8, 0x24, 0x25, 0x43, 0xe8, 0x51, 0x4d, 0x37, 0xc9, 0x84, 0x22, 0xf7, 0xf9,
0x55, 0xf3, 0xaa, 0x96, 0x3b, 0x55, 0xf3, 0x39, 0x6d, 0xeb, 0x54, 0xcd, 0x6f, 0x69, 0xdb, 0xfa, 0x78, 0x31, 0x1a, 0xd1, 0x38, 0x4e, 0xa2, 0xe4, 0x13, 0x4a, 0x8a, 0x89, 0x40, 0x4f, 0xa0, 0x26,
0x7f, 0x15, 0xc8, 0xa7, 0x6c, 0x1e, 0x09, 0x2f, 0x29, 0xe1, 0xba, 0x90, 0x62, 0xca, 0x73, 0xc0, 0x29, 0x32, 0xd6, 0x76, 0xa2, 0xd7, 0x14, 0x4e, 0xc3, 0xb5, 0x41, 0xcb, 0xf2, 0x66, 0xab, 0x6d,
0xf1, 0xa6, 0x14, 0x35, 0xa0, 0x24, 0x8c, 0xab, 0x12, 0x05, 0x8e, 0xb5, 0x84, 0x4c, 0xc5, 0x89, 0xb9, 0xba, 0x22, 0xf2, 0xa0, 0x49, 0xf1, 0xfa, 0x5f, 0xe0, 0xa1, 0x68, 0x65, 0x3f, 0x0a, 0x2f,
0x9d, 0x32, 0x84, 0x1e, 0x55, 0x79, 0x62, 0x27, 0x94, 0xb4, 0xe9, 0xc4, 0xf3, 0xe1, 0x90, 0xc6, 0xdd, 0x4b, 0xcf, 0xf7, 0xd8, 0x8d, 0x14, 0x39, 0x2f, 0x3c, 0x0a, 0x67, 0x84, 0xcf, 0xad, 0x6c,
0x71, 0x32, 0x4b, 0x2e, 0xa1, 0x48, 0x4c, 0x4c, 0xf4, 0x18, 0x76, 0x52, 0x4a, 0x3a, 0xd7, 0x56, 0x01, 0x07, 0xec, 0x70, 0x4c, 0x79, 0x0b, 0x58, 0x98, 0x98, 0xd2, 0x16, 0xb0, 0x50, 0x18, 0xb2,
0xa2, 0x57, 0x09, 0xcb, 0xe9, 0x9a, 0xa0, 0x65, 0x79, 0xd3, 0x65, 0x8f, 0xa8, 0x2c, 0x89, 0x7c, 0xc7, 0x59, 0x6e, 0xed, 0x38, 0xd3, 0xaf, 0xa1, 0x71, 0x37, 0x56, 0xaa, 0x99, 0x16, 0x94, 0xe6,
0x52, 0xb9, 0x0b, 0xff, 0x02, 0x0f, 0xc4, 0x52, 0xf6, 0xa3, 0xf0, 0xc2, 0xbd, 0xf0, 0x7c, 0x8f, 0x2b, 0x58, 0x84, 0x53, 0x70, 0x16, 0xca, 0xf6, 0x76, 0xeb, 0x87, 0x7b, 0xab, 0xff, 0x43, 0x81,
0x5d, 0xa7, 0x22, 0xe7, 0x89, 0x47, 0xe1, 0x94, 0xf0, 0xda, 0xa6, 0x4b, 0xc0, 0x01, 0x2b, 0x1c, 0xdd, 0xe3, 0x85, 0xe7, 0x8f, 0xd7, 0x16, 0x6e, 0x36, 0x3b, 0x65, 0xfd, 0xb0, 0xdd, 0x74, 0x92,
0x51, 0xbe, 0x04, 0x2c, 0x4c, 0x4c, 0x72, 0x09, 0x58, 0x28, 0x0c, 0xd9, 0xde, 0xba, 0xb9, 0xd2, 0x6e, 0x6d, 0x3c, 0x49, 0x37, 0x9d, 0x56, 0xb9, 0x7b, 0x4f, 0xab, 0x9f, 0x42, 0x69, 0x75, 0x50,
0x5b, 0xf5, 0x2b, 0xa8, 0xdd, 0x9e, 0x4b, 0x6a, 0xa6, 0x01, 0xc5, 0xd9, 0x12, 0x16, 0xd3, 0x29, 0xc5, 0x0d, 0xb5, 0x95, 0x6b, 0x97, 0x31, 0x4c, 0xe5, 0x29, 0x15, 0xeb, 0x2f, 0x01, 0x65, 0x13,
0x38, 0x0b, 0x65, 0xd7, 0x76, 0xe3, 0xa7, 0xd7, 0x56, 0xff, 0xa7, 0x02, 0xbb, 0xc7, 0x73, 0xcf, 0x4d, 0x27, 0x64, 0xb9, 0x7f, 0x28, 0xf7, 0xef, 0x1f, 0x8f, 0xa0, 0x39, 0x58, 0x5c, 0xc6, 0xa3,
0x1f, 0xad, 0x6c, 0xdc, 0x6c, 0x74, 0xca, 0x6a, 0xe7, 0x5f, 0xd7, 0xd6, 0x37, 0xd6, 0xb6, 0xf5, 0xc8, 0xbb, 0xa4, 0xa7, 0xcc, 0x1f, 0x99, 0x1f, 0x68, 0xc0, 0x62, 0xb9, 0x4a, 0xff, 0xab, 0x42,
0x75, 0xad, 0x73, 0xf3, 0xce, 0xd6, 0xf9, 0x05, 0x14, 0x97, 0x5d, 0x33, 0x39, 0x1d, 0x4b, 0x18, 0x71, 0x89, 0xa2, 0x03, 0xa8, 0x7b, 0xc1, 0x28, 0x9c, 0xc9, 0xa4, 0x03, 0xea, 0xf3, 0xbc, 0x93,
0x26, 0x69, 0xcb, 0x8c, 0xf5, 0x17, 0x80, 0xb2, 0x81, 0xca, 0x82, 0x2c, 0xce, 0x0f, 0xe5, 0xee, 0x4d, 0x7e, 0x57, 0x9a, 0xba, 0x89, 0xc5, 0x1a, 0x73, 0xfe, 0x5a, 0x91, 0x29, 0x7f, 0x2b, 0xe1,
0xf3, 0xe3, 0x21, 0xd4, 0x07, 0xf3, 0x8b, 0x78, 0x18, 0x79, 0x17, 0xb4, 0xcb, 0xfc, 0xa1, 0xf1, 0x67, 0x6b, 0x4c, 0xf8, 0x6d, 0xd0, 0x96, 0xfe, 0xa7, 0xcc, 0x1f, 0x2d, 0x27, 0x05, 0x57, 0x25,
0x81, 0x06, 0x2c, 0x4e, 0x77, 0xe9, 0xff, 0x54, 0x28, 0x2c, 0x50, 0x74, 0x00, 0x55, 0x2f, 0x18, 0xce, 0x93, 0x49, 0x98, 0x4b, 0xcf, 0x92, 0xa9, 0x26, 0x4c, 0x89, 0xa7, 0xcc, 0x2f, 0xa1, 0xcc,
0x86, 0xd3, 0x34, 0xe8, 0x80, 0xfa, 0x3c, 0xee, 0xa4, 0xe3, 0xec, 0xa6, 0xa6, 0x76, 0x62, 0x31, 0xd7, 0x43, 0xcc, 0xdc, 0xd9, 0x9c, 0x04, 0xb1, 0x58, 0x17, 0x2a, 0x2e, 0x2d, 0x31, 0x3b, 0x46,
0x47, 0x9c, 0xbf, 0x92, 0xa4, 0xe4, 0x6f, 0x24, 0xfc, 0x6c, 0x8e, 0x09, 0xbf, 0x09, 0xda, 0xc2, 0xdf, 0x00, 0x50, 0x5e, 0x1f, 0x61, 0x37, 0x73, 0x2a, 0x96, 0x44, 0xf5, 0xe8, 0x8b, 0x8c, 0x30,
0x3f, 0x3f, 0xe6, 0x17, 0x45, 0xc1, 0x95, 0x14, 0xe7, 0xc1, 0x24, 0xcc, 0x85, 0xe7, 0x94, 0xa9, 0x96, 0x13, 0x70, 0x20, 0xfe, 0x0e, 0x6f, 0xe6, 0x14, 0x17, 0xa9, 0x7c, 0x44, 0xaf, 0xa0, 0x32,
0x26, 0xcc, 0x14, 0x97, 0xcc, 0x2f, 0xa1, 0xc4, 0xf7, 0x43, 0xcc, 0xdc, 0xe9, 0x8c, 0x04, 0xb1, 0x09, 0xa3, 0x8f, 0x6e, 0x34, 0x26, 0x02, 0x4c, 0xb7, 0x8d, 0x87, 0x19, 0x0f, 0x27, 0x89, 0x5d,
0xd8, 0x17, 0x2a, 0x2e, 0x2e, 0x30, 0x2b, 0x46, 0xdf, 0x01, 0x50, 0x9e, 0x1f, 0x61, 0xd7, 0x33, 0x0c, 0x3f, 0xfd, 0x0c, 0x97, 0x27, 0x99, 0x77, 0x74, 0x06, 0x48, 0x8e, 0x17, 0xab, 0x3c, 0x71,
0x2a, 0xb6, 0x44, 0xe5, 0xe8, 0xf3, 0x8c, 0x30, 0x16, 0x05, 0x38, 0x10, 0xff, 0x3a, 0xd7, 0x33, 0x52, 0x10, 0x4e, 0xf6, 0xef, 0x3a, 0xe1, 0x9b, 0xb4, 0x74, 0xa4, 0x4d, 0x6e, 0x61, 0xe8, 0xf7,
0x8a, 0x0b, 0x34, 0xfd, 0x89, 0xbe, 0x87, 0xf2, 0x38, 0x8c, 0x3e, 0xba, 0xd1, 0x88, 0x08, 0x50, 0x50, 0x8e, 0x29, 0x63, 0x3e, 0x4d, 0xdd, 0x14, 0x85, 0x9b, 0xbd, 0xb5, 0x6b, 0x05, 0x37, 0x4b,
0x1e, 0x1b, 0xd9, 0x3e, 0x78, 0x92, 0xd8, 0xc5, 0xf0, 0xee, 0x3d, 0x5c, 0x1a, 0x67, 0xbe, 0xd1, 0x0f, 0xa5, 0x78, 0xf5, 0x8a, 0x8e, 0xa1, 0xe6, 0x7b, 0xc1, 0x75, 0x36, 0x0d, 0x10, 0xe3, 0x1b,
0x2b, 0x40, 0xe9, 0x78, 0xb1, 0xcb, 0x13, 0x27, 0x79, 0xe1, 0x64, 0xff, 0xb6, 0x13, 0x7e, 0x48, 0x99, 0xf1, 0x3d, 0x2f, 0xb8, 0xce, 0xe6, 0x50, 0xf1, 0xb3, 0x80, 0xfe, 0x07, 0x28, 0x2e, 0x67,
0xa7, 0x8e, 0xb4, 0xf1, 0x0d, 0x0c, 0xfd, 0x01, 0x4a, 0x31, 0x65, 0xcc, 0xa7, 0xd2, 0x4d, 0x41, 0x09, 0x95, 0x60, 0xe7, 0xc2, 0x3e, 0xb3, 0x9d, 0xb7, 0xb6, 0xf6, 0x19, 0x2a, 0x80, 0x3a, 0x30,
0xb8, 0xd9, 0x5b, 0xb9, 0xe3, 0x70, 0x73, 0xea, 0xa1, 0x18, 0x2f, 0x3f, 0xd1, 0x31, 0xec, 0xf8, 0x6d, 0x43, 0x53, 0x38, 0x8c, 0xcd, 0xae, 0x69, 0xbd, 0x31, 0xb5, 0x2d, 0xfe, 0x72, 0xe2, 0xe0,
0x5e, 0x70, 0x95, 0x0d, 0x03, 0xc4, 0xf8, 0x5a, 0x66, 0x7c, 0xcf, 0x0b, 0xae, 0xb2, 0x31, 0x94, 0xb7, 0x1d, 0x6c, 0x68, 0xb9, 0xe3, 0x1d, 0xc8, 0x8b, 0xb8, 0xfa, 0x3f, 0x15, 0x28, 0x88, 0x0e,
0xfd, 0x2c, 0xa0, 0xff, 0x11, 0x0a, 0x8b, 0x2a, 0xa1, 0x22, 0x6c, 0x9f, 0x5b, 0xaf, 0x2c, 0xfb, 0x06, 0x93, 0x10, 0xfd, 0x02, 0x96, 0xe2, 0x12, 0x9b, 0x1b, 0x3f, 0x70, 0x85, 0xea, 0x2a, 0x78,
0x8d, 0xa5, 0xdd, 0x43, 0x79, 0x50, 0x07, 0x86, 0xd5, 0xd1, 0x14, 0x0e, 0x63, 0xa3, 0x6d, 0x98, 0x29, 0x98, 0x61, 0x8a, 0x73, 0xf2, 0x52, 0x1a, 0x4b, 0xf2, 0x56, 0x42, 0x96, 0x86, 0x25, 0xf9,
0xaf, 0x0d, 0x6d, 0x83, 0x7f, 0x9c, 0xd8, 0xf8, 0x4d, 0x0b, 0x77, 0xb4, 0xcd, 0xe3, 0x6d, 0xc8, 0x59, 0xc6, 0xf3, 0xda, 0x96, 0xa3, 0xe2, 0x9a, 0x34, 0xc8, 0x1d, 0xf6, 0x59, 0xc6, 0xf1, 0xda,
0x89, 0x79, 0xf5, 0x7f, 0x2b, 0x90, 0x17, 0x2b, 0x18, 0x8c, 0x43, 0xf4, 0x2b, 0x58, 0x88, 0x4b, 0x4e, 0xac, 0xe2, 0x9a, 0x34, 0xa4, 0x5c, 0xfd, 0xb7, 0x50, 0xce, 0xf6, 0x1c, 0x3d, 0x05, 0xd5,
0x1c, 0x6e, 0xbc, 0xe1, 0x0a, 0xd5, 0x95, 0xf1, 0x42, 0x30, 0x8e, 0xc4, 0x39, 0x79, 0x21, 0x8d, 0x0b, 0x26, 0x61, 0xba, 0x10, 0xeb, 0xb7, 0xc4, 0xc5, 0x8b, 0xc4, 0x82, 0xa0, 0x23, 0xd0, 0x6e,
0x05, 0x79, 0x23, 0x21, 0xa7, 0x86, 0x05, 0xf9, 0x69, 0xc6, 0xf3, 0xca, 0x91, 0xa3, 0xe2, 0x9d, 0xf7, 0x59, 0xaf, 0x40, 0x29, 0xd3, 0x34, 0xfd, 0x5f, 0x0a, 0x54, 0xd6, 0x9a, 0xf0, 0xa3, 0xbd,
0xd4, 0x90, 0x9e, 0xb0, 0x4f, 0x33, 0x8e, 0x57, 0x4e, 0x62, 0x15, 0xef, 0xa4, 0x06, 0xc9, 0xd5, 0xa3, 0x6f, 0xa0, 0xfc, 0xd1, 0x8b, 0x28, 0xc9, 0x1e, 0xff, 0xd5, 0xa3, 0xe6, 0xfa, 0xf1, 0x2f,
0x7f, 0x07, 0xa5, 0xec, 0x9a, 0xa3, 0x27, 0xa0, 0x7a, 0xc1, 0x38, 0x94, 0x1b, 0xb1, 0x7a, 0x43, 0xff, 0x77, 0xc3, 0x31, 0xc5, 0x25, 0xce, 0x4f, 0x01, 0xf4, 0x47, 0xa8, 0xa6, 0x23, 0xc9, 0x98,
0x5c, 0x3c, 0x49, 0x2c, 0x08, 0x3a, 0x02, 0xed, 0xe6, 0x3a, 0xeb, 0x65, 0x28, 0x66, 0x16, 0x4d, 0x32, 0xd7, 0xf3, 0xc5, 0x54, 0x55, 0xd7, 0xe4, 0x91, 0x72, 0x0d, 0x61, 0xc7, 0x95, 0x49, 0xf6,
0xff, 0x8f, 0x02, 0xe5, 0x95, 0x45, 0xf8, 0xd9, 0xde, 0xd1, 0x77, 0x50, 0xfa, 0xe8, 0x45, 0x94, 0x15, 0x7d, 0xb5, 0x72, 0x10, 0xb3, 0xc8, 0x0b, 0xae, 0xc4, 0xfc, 0x15, 0x97, 0xb4, 0x81, 0x00,
0x64, 0xdb, 0x7f, 0xe5, 0xa8, 0xbe, 0xda, 0xfe, 0xd3, 0xff, 0xdb, 0xe1, 0x88, 0xe2, 0x22, 0xe7, 0x9f, 0xfd, 0x4d, 0x85, 0xca, 0x9a, 0x9f, 0x75, 0x21, 0x55, 0xa0, 0x68, 0x3b, 0xc4, 0x30, 0x87,
0x4b, 0x00, 0xfd, 0x09, 0x2a, 0x72, 0x24, 0x19, 0x51, 0xe6, 0x7a, 0xbe, 0x28, 0x55, 0x65, 0x45, 0x1d, 0xab, 0xa7, 0x29, 0x48, 0x83, 0xb2, 0x63, 0x5b, 0x8e, 0x4d, 0x0c, 0xb3, 0xeb, 0x18, 0x5c,
0x1e, 0x92, 0xdb, 0x11, 0x76, 0x5c, 0x1e, 0x67, 0x3f, 0xd1, 0xd7, 0x4b, 0x07, 0x31, 0x8b, 0xbc, 0x52, 0x9f, 0xc3, 0x6e, 0xcf, 0xb2, 0xcf, 0x88, 0xed, 0x0c, 0x89, 0xd9, 0xb3, 0xbe, 0xb5, 0x8e,
0xe0, 0x52, 0xd4, 0xaf, 0xb0, 0xa0, 0x0d, 0x04, 0xf8, 0xf4, 0x5f, 0x0a, 0x94, 0xb2, 0x57, 0x47, 0x7b, 0xa6, 0x96, 0x43, 0x0f, 0x40, 0x73, 0x6c, 0xd2, 0x3d, 0xed, 0x58, 0x36, 0x19, 0x5a, 0xe7,
0x54, 0x86, 0x82, 0x69, 0x91, 0x93, 0x9e, 0xf9, 0xb2, 0xeb, 0x68, 0xf7, 0xf8, 0xe7, 0xe0, 0xbc, 0xa6, 0x73, 0x31, 0xd4, 0x54, 0x8e, 0x9e, 0x0e, 0x7b, 0x5d, 0x62, 0xbe, 0xeb, 0x9a, 0xa6, 0x31,
0xdd, 0x36, 0x8c, 0x8e, 0xc1, 0xe5, 0x84, 0xa0, 0x72, 0xd2, 0x32, 0x7b, 0x46, 0x87, 0x38, 0xe6, 0x20, 0xe7, 0x9d, 0x77, 0x5a, 0x1e, 0x35, 0xe0, 0x81, 0x65, 0x0f, 0x2e, 0x4e, 0x4e, 0xac, 0xae,
0x99, 0x61, 0x9f, 0x3b, 0xda, 0x06, 0xaa, 0xc2, 0x8e, 0xc4, 0x2c, 0x9b, 0x60, 0xfb, 0xdc, 0x31, 0x65, 0xda, 0x43, 0x72, 0xdc, 0xe9, 0x75, 0xec, 0xae, 0xa9, 0x6d, 0xa3, 0x3d, 0x40, 0x96, 0xdd,
0xb4, 0x4d, 0xa4, 0x41, 0x49, 0x82, 0x06, 0xc6, 0x36, 0xd6, 0x54, 0xf4, 0x15, 0x34, 0x24, 0x62, 0x75, 0xce, 0xfb, 0x3d, 0x73, 0x68, 0x12, 0x29, 0xdd, 0x1d, 0x54, 0x87, 0x9a, 0xf0, 0xd3, 0x31,
0x5a, 0x6d, 0x1b, 0x63, 0xa3, 0xed, 0x90, 0x7e, 0xeb, 0xdd, 0x99, 0x61, 0x39, 0xa4, 0x63, 0x38, 0x0c, 0x72, 0xd2, 0xb1, 0x7a, 0xa6, 0xa1, 0x15, 0x78, 0x26, 0x29, 0x63, 0x40, 0x0c, 0x6b, 0xd0,
0x2d, 0xb3, 0x37, 0xd0, 0x72, 0xe8, 0x0b, 0xd8, 0x5f, 0xb0, 0x06, 0xe7, 0x27, 0x27, 0x66, 0xdb, 0x39, 0xe6, 0x70, 0x91, 0xc7, 0xb4, 0xec, 0x37, 0x8e, 0xd5, 0x35, 0x49, 0x97, 0xbb, 0xe5, 0x28,
0xe4, 0x84, 0xe3, 0x56, 0xaf, 0x65, 0xb5, 0x0d, 0x6d, 0xeb, 0xe9, 0xdf, 0x54, 0x28, 0xaf, 0x24, 0x70, 0xb2, 0x44, 0x2f, 0x6c, 0xc3, 0xc4, 0xfd, 0x8e, 0x65, 0x68, 0x25, 0xb4, 0x0f, 0x0f, 0x25,
0xbe, 0xaa, 0xfc, 0x32, 0x14, 0x2c, 0x5b, 0xfa, 0xd3, 0x14, 0x1e, 0x86, 0x6d, 0x99, 0xb6, 0x45, 0x6c, 0xbe, 0xeb, 0x5b, 0xf8, 0x7b, 0x32, 0x74, 0x1c, 0x32, 0x70, 0x1c, 0x5b, 0x2b, 0x67, 0x3d,
0x3a, 0x46, 0xdb, 0xee, 0xf0, 0x3d, 0xf0, 0x19, 0xec, 0xf6, 0x4c, 0xeb, 0x15, 0xb1, 0x6c, 0x87, 0xf1, 0x6a, 0x9d, 0xbe, 0x69, 0x6b, 0x15, 0xf4, 0x10, 0xea, 0xe7, 0xfd, 0x3e, 0x91, 0x16, 0x59,
0x18, 0x3d, 0xf3, 0xa5, 0x79, 0xdc, 0xe3, 0xf1, 0xde, 0x07, 0xcd, 0xb6, 0x48, 0xbb, 0xdb, 0x32, 0x6c, 0x95, 0xd3, 0x3b, 0x86, 0x81, 0xcd, 0xc1, 0x80, 0x9c, 0x5b, 0x83, 0xf3, 0xce, 0xb0, 0x7b,
0xad, 0x45, 0x6a, 0x2a, 0x47, 0xf9, 0x85, 0x98, 0x18, 0x6f, 0x79, 0x05, 0x06, 0xe4, 0xac, 0xf5, 0xaa, 0xd5, 0x78, 0x49, 0x03, 0x73, 0x48, 0x86, 0xce, 0xb0, 0xd3, 0x5b, 0xe1, 0x1a, 0x4f, 0x68,
0x56, 0xcb, 0xa1, 0x1a, 0xdc, 0x5f, 0x1f, 0x1c, 0xda, 0x03, 0xc4, 0x93, 0x3b, 0xeb, 0xf7, 0x0c, 0x85, 0xf3, 0xa0, 0x3d, 0xe7, 0xad, 0xb6, 0xcb, 0x27, 0x9c, 0xc3, 0xce, 0x9b, 0x34, 0x45, 0xc4,
0xc7, 0x20, 0xe9, 0x5e, 0xdb, 0xe6, 0x25, 0x12, 0x7e, 0x5a, 0x9d, 0x0e, 0x49, 0xd2, 0xd3, 0xf2, 0x6b, 0x4f, 0xdb, 0x23, 0x63, 0x6a, 0x75, 0x0e, 0x5a, 0xf6, 0x9b, 0x4e, 0xcf, 0x32, 0xc8, 0x99,
0x3c, 0x12, 0xc9, 0x18, 0x90, 0x8e, 0x39, 0x68, 0x1d, 0x73, 0xb8, 0xc0, 0xe7, 0x34, 0xad, 0xd7, 0xf9, 0xbd, 0x58, 0xfa, 0x0f, 0x38, 0x98, 0x64, 0x46, 0xfa, 0xd8, 0xf9, 0x96, 0x27, 0xa2, 0x7d,
0xb6, 0xd9, 0x36, 0x48, 0x9b, 0xbb, 0xe5, 0x28, 0x70, 0x72, 0x8a, 0x9e, 0x5b, 0x1d, 0x03, 0xf7, 0x8e, 0x10, 0x54, 0xbb, 0x16, 0xee, 0x5e, 0xf4, 0x3a, 0x98, 0x60, 0xe7, 0x62, 0x68, 0x6a, 0x7b,
0x5b, 0x66, 0x47, 0x2b, 0xa2, 0x7d, 0x78, 0x90, 0xc2, 0xc6, 0xdb, 0xbe, 0x89, 0xdf, 0x11, 0xc7, 0x47, 0xff, 0xc9, 0xc3, 0xb6, 0x38, 0xa8, 0x22, 0xf4, 0x8a, 0xeb, 0x7f, 0xf9, 0x2d, 0x84, 0x1e,
0xb6, 0xc9, 0xc0, 0xb6, 0x2d, 0xad, 0x94, 0xf5, 0xc4, 0xb3, 0xb5, 0xfb, 0x86, 0xa5, 0x95, 0xd1, 0x7f, 0xf2, 0x1b, 0xa9, 0x29, 0x2f, 0xb3, 0x29, 0xfc, 0x42, 0x41, 0x7f, 0x82, 0x72, 0xf6, 0xfb,
0x03, 0xa8, 0x9e, 0xf5, 0xfb, 0x24, 0xb5, 0xa4, 0xc9, 0x56, 0x38, 0xbd, 0xd5, 0xe9, 0x60, 0x63, 0x02, 0x65, 0xf7, 0xf6, 0x0d, 0x1f, 0x1e, 0x1b, 0x3c, 0x9c, 0x81, 0x66, 0xc6, 0xcc, 0x9b, 0xb9,
0x30, 0x20, 0x67, 0xe6, 0xe0, 0xac, 0xe5, 0xb4, 0xbb, 0xda, 0x0e, 0x4f, 0x69, 0x60, 0x38, 0xc4, 0x8c, 0xca, 0xef, 0x05, 0xd4, 0xcc, 0x78, 0xb9, 0xf5, 0x11, 0xd2, 0xdc, 0xdf, 0x68, 0x4b, 0x4f,
0xb1, 0x9d, 0x56, 0x6f, 0x89, 0x6b, 0x3c, 0xa0, 0x25, 0xce, 0x27, 0xed, 0xd9, 0x6f, 0xb4, 0x5d, 0xe5, 0x5e, 0x52, 0x4e, 0x7a, 0x63, 0xbf, 0x53, 0xce, 0xfa, 0x67, 0x42, 0xf3, 0x8b, 0xfb, 0xcc,
0x5e, 0x70, 0x0e, 0xdb, 0xaf, 0x65, 0x88, 0x88, 0xe7, 0x2e, 0x97, 0x27, 0x9d, 0x53, 0xab, 0x72, 0xa9, 0xb7, 0x31, 0xd4, 0x37, 0x5c, 0xc2, 0xd1, 0x57, 0xd9, 0x0c, 0xee, 0xbd, 0xc2, 0x37, 0x9f,
0xd0, 0xb4, 0x5e, 0xb7, 0x7a, 0x66, 0x87, 0xbc, 0x32, 0xde, 0x89, 0xb3, 0xea, 0x3e, 0x07, 0x93, 0xfc, 0x10, 0x6d, 0x15, 0x65, 0xc3, 0x6d, 0x7d, 0x2d, 0xca, 0xfd, 0x77, 0xfd, 0xb5, 0x28, 0x9f,
0xc8, 0x48, 0x1f, 0xdb, 0x2f, 0x79, 0x20, 0xda, 0x67, 0x5c, 0x71, 0x6d, 0x13, 0xb7, 0xcf, 0x7b, 0xba, 0xf4, 0xbf, 0x07, 0xed, 0xf6, 0xe5, 0x0e, 0xe9, 0xb7, 0xc7, 0xde, 0xbd, 0x65, 0x36, 0x7f,
0x2d, 0x2c, 0xc5, 0xb5, 0x77, 0xf4, 0xf7, 0x2d, 0xd8, 0x12, 0x9d, 0x35, 0x42, 0x5d, 0xbe, 0x61, 0xf6, 0x49, 0x4e, 0xea, 0xdc, 0x02, 0x58, 0x5d, 0x91, 0xd0, 0xa3, 0xcc, 0x90, 0x3b, 0x57, 0xbc,
0x17, 0x2f, 0x49, 0xf4, 0xe8, 0x93, 0x2f, 0xcc, 0x7a, 0x6d, 0xfd, 0x83, 0x69, 0x1e, 0x3f, 0x57, 0xe6, 0xe3, 0x7b, 0xac, 0xa9, 0xab, 0x21, 0xd4, 0x37, 0xdc, 0x99, 0xd6, 0x66, 0xe3, 0xfe, 0x3b,
0xd0, 0x29, 0x94, 0xb2, 0xef, 0x34, 0x94, 0x6d, 0x4b, 0x6b, 0x1e, 0x70, 0x9f, 0xf4, 0xf5, 0x0a, 0x55, 0xf3, 0xc1, 0xa6, 0xab, 0xc5, 0x0b, 0xe5, 0xf8, 0xeb, 0x3f, 0x1f, 0x5e, 0x79, 0x6c, 0xba,
0x34, 0x23, 0x66, 0xde, 0x94, 0xbf, 0xc5, 0xe4, 0xa3, 0x07, 0xd5, 0x33, 0xfc, 0x1b, 0x2f, 0xa9, 0xb8, 0x3c, 0x18, 0x85, 0xb3, 0x43, 0xdf, 0xbb, 0x9a, 0xb2, 0xc0, 0x0b, 0xae, 0x02, 0xca, 0x3e,
0xfa, 0xfe, 0x5a, 0x9b, 0xbc, 0x5a, 0xf4, 0x92, 0x14, 0xe5, 0xb3, 0xe3, 0x56, 0x8a, 0xab, 0x6f, 0x86, 0xd1, 0xf5, 0xa1, 0x1f, 0x8c, 0x0f, 0x85, 0x2e, 0x0f, 0x97, 0xc3, 0x2f, 0xb7, 0xc5, 0x6f,
0x9d, 0xfa, 0xe7, 0x77, 0x99, 0xa5, 0xb7, 0x11, 0x54, 0xd7, 0xbc, 0x24, 0xd0, 0xd7, 0xd9, 0x08, 0x47, 0xbf, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x74, 0xbe, 0x98, 0x4d, 0x6b, 0x12, 0x00,
0xee, 0x7c, 0x87, 0xd4, 0x1f, 0xff, 0x14, 0x6d, 0x39, 0xcb, 0x9a, 0x27, 0xc7, 0xca, 0x2c, 0x77, 0x00,
0x3f, 0x58, 0x56, 0x66, 0xf9, 0xd4, 0xcb, 0xe5, 0x3d, 0x68, 0x37, 0x6f, 0xa8, 0x48, 0xbf, 0x39,
0xf6, 0xf6, 0x55, 0xb9, 0xfe, 0xcb, 0x4f, 0x72, 0xa4, 0x73, 0x13, 0x60, 0x79, 0xcf, 0x43, 0x0f,
0x33, 0x43, 0x6e, 0xdd, 0x53, 0xeb, 0x8f, 0xee, 0xb0, 0x4a, 0x57, 0x0e, 0x54, 0xd7, 0x5c, 0xfc,
0x56, 0xaa, 0x71, 0xf7, 0xc5, 0xb0, 0x7e, 0x7f, 0xdd, 0xfd, 0xe8, 0xb9, 0x72, 0xfc, 0xed, 0x9f,
0x0f, 0x2f, 0x3d, 0x36, 0x99, 0x5f, 0x1c, 0x0c, 0xc3, 0xe9, 0xa1, 0xef, 0x5d, 0x4e, 0x58, 0xe0,
0x05, 0x97, 0x01, 0x65, 0x1f, 0xc3, 0xe8, 0xea, 0xd0, 0x0f, 0x46, 0x87, 0xa2, 0xd9, 0x1c, 0x2e,
0x86, 0x5f, 0x6c, 0x89, 0xbf, 0xc6, 0xfd, 0xfa, 0xc7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xaf, 0x84,
0xd2, 0x59, 0xbd, 0x13, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -1866,7 +1740,7 @@ type RouterClient interface {
//* //*
//SendPayment attempts to route a payment described by the passed //SendPayment attempts to route a payment described by the passed
//PaymentRequest to the final destination. The call returns a stream of //PaymentRequest to the final destination. The call returns a stream of
//payment status updates. //payment updates.
SendPayment(ctx context.Context, in *SendPaymentRequest, opts ...grpc.CallOption) (Router_SendPaymentClient, error) SendPayment(ctx context.Context, in *SendPaymentRequest, opts ...grpc.CallOption) (Router_SendPaymentClient, error)
//* //*
//TrackPayment returns an update stream for the payment identified by the //TrackPayment returns an update stream for the payment identified by the
@ -1928,7 +1802,7 @@ func (c *routerClient) SendPayment(ctx context.Context, in *SendPaymentRequest,
} }
type Router_SendPaymentClient interface { type Router_SendPaymentClient interface {
Recv() (*PaymentStatus, error) Recv() (*lnrpc.Payment, error)
grpc.ClientStream grpc.ClientStream
} }
@ -1936,8 +1810,8 @@ type routerSendPaymentClient struct {
grpc.ClientStream grpc.ClientStream
} }
func (x *routerSendPaymentClient) Recv() (*PaymentStatus, error) { func (x *routerSendPaymentClient) Recv() (*lnrpc.Payment, error) {
m := new(PaymentStatus) m := new(lnrpc.Payment)
if err := x.ClientStream.RecvMsg(m); err != nil { if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err return nil, err
} }
@ -1960,7 +1834,7 @@ func (c *routerClient) TrackPayment(ctx context.Context, in *TrackPaymentRequest
} }
type Router_TrackPaymentClient interface { type Router_TrackPaymentClient interface {
Recv() (*PaymentStatus, error) Recv() (*lnrpc.Payment, error)
grpc.ClientStream grpc.ClientStream
} }
@ -1968,8 +1842,8 @@ type routerTrackPaymentClient struct {
grpc.ClientStream grpc.ClientStream
} }
func (x *routerTrackPaymentClient) Recv() (*PaymentStatus, error) { func (x *routerTrackPaymentClient) Recv() (*lnrpc.Payment, error) {
m := new(PaymentStatus) m := new(lnrpc.Payment)
if err := x.ClientStream.RecvMsg(m); err != nil { if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err return nil, err
} }
@ -2067,7 +1941,7 @@ type RouterServer interface {
//* //*
//SendPayment attempts to route a payment described by the passed //SendPayment attempts to route a payment described by the passed
//PaymentRequest to the final destination. The call returns a stream of //PaymentRequest to the final destination. The call returns a stream of
//payment status updates. //payment updates.
SendPayment(*SendPaymentRequest, Router_SendPaymentServer) error SendPayment(*SendPaymentRequest, Router_SendPaymentServer) error
//* //*
//TrackPayment returns an update stream for the payment identified by the //TrackPayment returns an update stream for the payment identified by the
@ -2118,7 +1992,7 @@ func _Router_SendPayment_Handler(srv interface{}, stream grpc.ServerStream) erro
} }
type Router_SendPaymentServer interface { type Router_SendPaymentServer interface {
Send(*PaymentStatus) error Send(*lnrpc.Payment) error
grpc.ServerStream grpc.ServerStream
} }
@ -2126,7 +2000,7 @@ type routerSendPaymentServer struct {
grpc.ServerStream grpc.ServerStream
} }
func (x *routerSendPaymentServer) Send(m *PaymentStatus) error { func (x *routerSendPaymentServer) Send(m *lnrpc.Payment) error {
return x.ServerStream.SendMsg(m) return x.ServerStream.SendMsg(m)
} }
@ -2139,7 +2013,7 @@ func _Router_TrackPayment_Handler(srv interface{}, stream grpc.ServerStream) err
} }
type Router_TrackPaymentServer interface { type Router_TrackPaymentServer interface {
Send(*PaymentStatus) error Send(*lnrpc.Payment) error
grpc.ServerStream grpc.ServerStream
} }
@ -2147,7 +2021,7 @@ type routerTrackPaymentServer struct {
grpc.ServerStream grpc.ServerStream
} }
func (x *routerTrackPaymentServer) Send(m *PaymentStatus) error { func (x *routerTrackPaymentServer) Send(m *lnrpc.Payment) error {
return x.ServerStream.SendMsg(m) return x.ServerStream.SendMsg(m)
} }

@ -121,62 +121,6 @@ message TrackPaymentRequest {
bytes payment_hash = 1; bytes payment_hash = 1;
} }
enum PaymentState {
/**
Payment is still in flight.
*/
IN_FLIGHT = 0;
/**
Payment completed successfully.
*/
SUCCEEDED = 1;
/**
There are more routes to try, but the payment timeout was exceeded.
*/
FAILED_TIMEOUT = 2;
/**
All possible routes were tried and failed permanently. Or were no
routes to the destination at all.
*/
FAILED_NO_ROUTE = 3;
/**
A non-recoverable error has occured.
*/
FAILED_ERROR = 4;
/**
Payment details incorrect (unknown hash, invalid amt or
invalid final cltv delta)
*/
FAILED_INCORRECT_PAYMENT_DETAILS = 5;
/**
Insufficient local balance.
*/
FAILED_INSUFFICIENT_BALANCE = 6;
}
message PaymentStatus {
/// Current state the payment is in.
PaymentState state = 1;
/**
The pre-image of the payment when state is SUCCEEDED.
*/
bytes preimage = 2;
reserved 3;
/**
The HTLCs made in attempt to settle the payment.
*/
repeated lnrpc.HTLCAttempt htlcs = 4;
}
message RouteFeeRequest { message RouteFeeRequest {
/** /**
The destination once wishes to obtain a routing fee quote to. The destination once wishes to obtain a routing fee quote to.
@ -465,15 +409,15 @@ service Router {
/** /**
SendPayment attempts to route a payment described by the passed SendPayment attempts to route a payment described by the passed
PaymentRequest to the final destination. The call returns a stream of PaymentRequest to the final destination. The call returns a stream of
payment status updates. payment updates.
*/ */
rpc SendPayment (SendPaymentRequest) returns (stream PaymentStatus); rpc SendPayment (SendPaymentRequest) returns (stream lnrpc.Payment);
/** /**
TrackPayment returns an update stream for the payment identified by the TrackPayment returns an update stream for the payment identified by the
payment hash. payment hash.
*/ */
rpc TrackPayment (TrackPaymentRequest) returns (stream PaymentStatus); rpc TrackPayment (TrackPaymentRequest) returns (stream lnrpc.Payment);
/** /**
EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it

@ -441,7 +441,7 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash,
router := s.cfg.RouterBackend router := s.cfg.RouterBackend
// Subscribe to the outcome of this payment. // Subscribe to the outcome of this payment.
inFlight, resultChan, err := router.Tower.SubscribePayment( subscription, err := router.Tower.SubscribePayment(
paymentHash, paymentHash,
) )
switch { switch {
@ -450,95 +450,37 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash,
case err != nil: case err != nil:
return err return err
} }
defer subscription.Close()
// If it is in flight, send a state update to the client. Payment status // Stream updates back to the client. The first update is always the
// update streams are expected to always send the current payment state // current state of the payment.
// immediately. for {
if inFlight { select {
err = stream.Send(&PaymentStatus{ case item, ok := <-subscription.Updates:
State: PaymentState_IN_FLIGHT, if !ok {
}) // No more payment updates.
if err != nil { return nil
return err
}
}
// Wait for the outcome of the payment. For payments that have
// completed, the result should already be waiting on the channel.
select {
case result := <-resultChan:
// Marshall result to rpc type.
var status PaymentStatus
if result.Success {
log.Debugf("Payment %v successfully completed",
paymentHash)
status.State = PaymentState_SUCCEEDED
status.Preimage = result.Preimage[:]
} else {
state, err := marshallFailureReason(
result.FailureReason,
)
if err != nil {
return err
} }
status.State = state result := item.(*channeldb.MPPayment)
} rpcPayment, err := router.MarshallPayment(result)
// Marshal our list of HTLCs that have been tried for this
// payment.
htlcs := make([]*lnrpc.HTLCAttempt, 0, len(result.HTLCs))
for _, dbHtlc := range result.HTLCs {
htlc, err := router.MarshalHTLCAttempt(dbHtlc)
if err != nil { if err != nil {
return err return err
} }
htlcs = append(htlcs, htlc) // Send event to the client.
err = stream.Send(rpcPayment)
if err != nil {
return err
}
case <-s.quit:
return errServerShuttingDown
case <-stream.Context().Done():
log.Debugf("Payment status stream %v canceled", paymentHash)
return stream.Context().Err()
} }
status.Htlcs = htlcs
// Send event to the client.
err = stream.Send(&status)
if err != nil {
return err
}
case <-s.quit:
return errServerShuttingDown
case <-stream.Context().Done():
log.Debugf("Payment status stream %v canceled", paymentHash)
return stream.Context().Err()
} }
return nil
}
// marshallFailureReason marshalls the failure reason to the corresponding rpc
// type.
func marshallFailureReason(reason channeldb.FailureReason) (
PaymentState, error) {
switch reason {
case channeldb.FailureReasonTimeout:
return PaymentState_FAILED_TIMEOUT, nil
case channeldb.FailureReasonNoRoute:
return PaymentState_FAILED_NO_ROUTE, nil
case channeldb.FailureReasonError:
return PaymentState_FAILED_ERROR, nil
case channeldb.FailureReasonPaymentDetails:
return PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS, nil
case channeldb.FailureReasonInsufficientBalance:
return PaymentState_FAILED_INSUFFICIENT_BALANCE, nil
}
return 0, errors.New("unknown failure reason")
} }
// BuildRoute builds a route from a list of hop addresses. // BuildRoute builds a route from a list of hop addresses.

@ -14271,13 +14271,13 @@ func testHoldInvoicePersistence(net *lntest.NetworkHarness, t *harnessTest) {
// Wait for inlight status update. // Wait for inlight status update.
for _, payStream := range paymentStreams { for _, payStream := range paymentStreams {
status, err := payStream.Recv() payment, err := payStream.Recv()
if err != nil { if err != nil {
t.Fatalf("Failed receiving status update: %v", err) t.Fatalf("Failed receiving status update: %v", err)
} }
if status.State != routerrpc.PaymentState_IN_FLIGHT { if payment.Status != lnrpc.Payment_IN_FLIGHT {
t.Fatalf("state not in flight: %v", status.State) t.Fatalf("state not in flight: %v", payment.Status)
} }
} }
@ -14355,7 +14355,7 @@ func testHoldInvoicePersistence(net *lntest.NetworkHarness, t *harnessTest) {
// Now after a restart, we must re-track the payments. We set up a // Now after a restart, we must re-track the payments. We set up a
// goroutine for each to track thir status updates. // goroutine for each to track thir status updates.
var ( var (
statusUpdates []chan *routerrpc.PaymentStatus statusUpdates []chan *lnrpc.Payment
wg sync.WaitGroup wg sync.WaitGroup
quit = make(chan struct{}) quit = make(chan struct{})
) )
@ -14377,20 +14377,20 @@ func testHoldInvoicePersistence(net *lntest.NetworkHarness, t *harnessTest) {
} }
// We set up a channel where we'll forward any status update. // We set up a channel where we'll forward any status update.
upd := make(chan *routerrpc.PaymentStatus) upd := make(chan *lnrpc.Payment)
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
for { for {
status, err := payStream.Recv() payment, err := payStream.Recv()
if err != nil { if err != nil {
close(upd) close(upd)
return return
} }
select { select {
case upd <- status: case upd <- payment:
case <-quit: case <-quit:
return return
} }
@ -14400,17 +14400,17 @@ func testHoldInvoicePersistence(net *lntest.NetworkHarness, t *harnessTest) {
statusUpdates = append(statusUpdates, upd) statusUpdates = append(statusUpdates, upd)
} }
// Wait for the infligt status update. // Wait for the in-flight status update.
for _, upd := range statusUpdates { for _, upd := range statusUpdates {
select { select {
case status, ok := <-upd: case payment, ok := <-upd:
if !ok { if !ok {
t.Fatalf("failed getting status update") t.Fatalf("failed getting payment update")
} }
if status.State != routerrpc.PaymentState_IN_FLIGHT { if payment.Status != lnrpc.Payment_IN_FLIGHT {
t.Fatalf("state not in in flight: %v", t.Fatalf("state not in in flight: %v",
status.State) payment.Status)
} }
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
t.Fatalf("in flight status not recevied") t.Fatalf("in flight status not recevied")
@ -14439,25 +14439,38 @@ func testHoldInvoicePersistence(net *lntest.NetworkHarness, t *harnessTest) {
// Make sure we get the expected status update. // Make sure we get the expected status update.
for i, upd := range statusUpdates { for i, upd := range statusUpdates {
select { // Read until the payment is in a terminal state.
case status, ok := <-upd: var payment *lnrpc.Payment
if !ok { for payment == nil {
t.Fatalf("failed getting status update") select {
} case p, ok := <-upd:
if !ok {
t.Fatalf("failed getting payment update")
}
if i%2 == 0 { if p.Status == lnrpc.Payment_IN_FLIGHT {
if status.State != routerrpc.PaymentState_SUCCEEDED { continue
t.Fatalf("state not suceeded : %v",
status.State)
}
} else {
if status.State != routerrpc.PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS {
t.Fatalf("state not failed: %v",
status.State)
} }
payment = p
case <-time.After(5 * time.Second):
t.Fatalf("in flight status not recevied")
}
}
// Assert terminal payment state.
if i%2 == 0 {
if payment.Status != lnrpc.Payment_SUCCEEDED {
t.Fatalf("state not suceeded : %v",
payment.Status)
}
} else {
if payment.FailureReason !=
lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS {
t.Fatalf("state not failed: %v",
payment.FailureReason)
} }
case <-time.After(5 * time.Second):
t.Fatalf("in flight status not recevied")
} }
} }

@ -1,11 +1,12 @@
package routing package routing
import ( import (
"errors"
"sync" "sync"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/multimutex"
"github.com/lightningnetwork/lnd/queue"
) )
// ControlTower tracks all outgoing payments made, whose primary purpose is to // ControlTower tracks all outgoing payments made, whose primary purpose is to
@ -50,29 +51,43 @@ type ControlTower interface {
FetchInFlightPayments() ([]*channeldb.InFlightPayment, error) FetchInFlightPayments() ([]*channeldb.InFlightPayment, error)
// SubscribePayment subscribes to updates for the payment with the given // SubscribePayment subscribes to updates for the payment with the given
// hash. It returns a boolean indicating whether the payment is still in // hash. A first update with the current state of the payment is always
// flight and a channel that provides the final outcome of the payment. // sent out immediately.
SubscribePayment(paymentHash lntypes.Hash) (bool, chan PaymentResult, SubscribePayment(paymentHash lntypes.Hash) (*ControlTowerSubscriber,
error) error)
} }
// PaymentResult is the struct describing the events received by payment // ControlTowerSubscriber contains the state for a payment update subscriber.
// subscribers. type ControlTowerSubscriber struct {
type PaymentResult struct { // Updates is the channel over which *channeldb.MPPayment updates can be
// Success indicates whether the payment was successful. // received.
Success bool Updates <-chan interface{}
// Preimage is the preimage of a successful payment. This serves as a queue *queue.ConcurrentQueue
// proof of payment. It is only set for successful payments. quit chan struct{}
Preimage lntypes.Preimage }
// FailureReason is a failure reason code indicating the reason the // newControlTowerSubscriber instantiates a new subscriber state object.
// payment failed. It is only set for failed payments. func newControlTowerSubscriber() *ControlTowerSubscriber {
FailureReason channeldb.FailureReason // Create a queue for payment updates.
queue := queue.NewConcurrentQueue(20)
queue.Start()
// HTLCs is a list of HTLCs that have been attempted in order to settle return &ControlTowerSubscriber{
// the payment. Updates: queue.ChanOut(),
HTLCs []channeldb.HTLCAttempt queue: queue,
quit: make(chan struct{}),
}
}
// Close signals that the subscriber is no longer interested in updates.
func (s *ControlTowerSubscriber) Close() {
// Close quit channel so that any pending writes to the queue are
// cancelled.
close(s.quit)
// Stop the queue goroutine so that it won't leak.
s.queue.Stop()
} }
// controlTower is persistent implementation of ControlTower to restrict // controlTower is persistent implementation of ControlTower to restrict
@ -80,15 +95,21 @@ type PaymentResult struct {
type controlTower struct { type controlTower struct {
db *channeldb.PaymentControl db *channeldb.PaymentControl
subscribers map[lntypes.Hash][]chan PaymentResult subscribers map[lntypes.Hash][]*ControlTowerSubscriber
subscribersMtx sync.Mutex subscribersMtx sync.Mutex
// paymentsMtx provides synchronization on the payment level to ensure
// that no race conditions occur in between updating the database and
// sending a notification.
paymentsMtx *multimutex.HashMutex
} }
// NewControlTower creates a new instance of the controlTower. // NewControlTower creates a new instance of the controlTower.
func NewControlTower(db *channeldb.PaymentControl) ControlTower { func NewControlTower(db *channeldb.PaymentControl) ControlTower {
return &controlTower{ return &controlTower{
db: db, db: db,
subscribers: make(map[lntypes.Hash][]chan PaymentResult), subscribers: make(map[lntypes.Hash][]*ControlTowerSubscriber),
paymentsMtx: multimutex.NewHashMutex(),
} }
} }
@ -107,8 +128,18 @@ func (p *controlTower) InitPayment(paymentHash lntypes.Hash,
func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash, func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash,
attempt *channeldb.HTLCAttemptInfo) error { attempt *channeldb.HTLCAttemptInfo) error {
_, err := p.db.RegisterAttempt(paymentHash, attempt) p.paymentsMtx.Lock(paymentHash)
return err defer p.paymentsMtx.Unlock(paymentHash)
payment, err := p.db.RegisterAttempt(paymentHash, attempt)
if err != nil {
return err
}
// Notify subscribers of the attempt registration.
p.notifySubscribers(paymentHash, payment)
return nil
} }
// SettleAttempt marks the given attempt settled with the preimage. If // SettleAttempt marks the given attempt settled with the preimage. If
@ -117,15 +148,16 @@ func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash,
func (p *controlTower) SettleAttempt(paymentHash lntypes.Hash, func (p *controlTower) SettleAttempt(paymentHash lntypes.Hash,
attemptID uint64, settleInfo *channeldb.HTLCSettleInfo) error { attemptID uint64, settleInfo *channeldb.HTLCSettleInfo) error {
p.paymentsMtx.Lock(paymentHash)
defer p.paymentsMtx.Unlock(paymentHash)
payment, err := p.db.SettleAttempt(paymentHash, attemptID, settleInfo) payment, err := p.db.SettleAttempt(paymentHash, attemptID, settleInfo)
if err != nil { if err != nil {
return err return err
} }
// Notify subscribers of success event. // Notify subscribers of success event.
p.notifyFinalEvent( p.notifySubscribers(paymentHash, payment)
paymentHash, createSuccessResult(payment.HTLCs),
)
return nil return nil
} }
@ -134,8 +166,18 @@ func (p *controlTower) SettleAttempt(paymentHash lntypes.Hash,
func (p *controlTower) FailAttempt(paymentHash lntypes.Hash, func (p *controlTower) FailAttempt(paymentHash lntypes.Hash,
attemptID uint64, failInfo *channeldb.HTLCFailInfo) error { attemptID uint64, failInfo *channeldb.HTLCFailInfo) error {
_, err := p.db.FailAttempt(paymentHash, attemptID, failInfo) p.paymentsMtx.Lock(paymentHash)
return err defer p.paymentsMtx.Unlock(paymentHash)
payment, err := p.db.FailAttempt(paymentHash, attemptID, failInfo)
if err != nil {
return err
}
// Notify subscribers of failed attempt.
p.notifySubscribers(paymentHash, payment)
return nil
} }
// FetchPayment fetches the payment corresponding to the given payment hash. // FetchPayment fetches the payment corresponding to the given payment hash.
@ -145,35 +187,6 @@ func (p *controlTower) FetchPayment(paymentHash lntypes.Hash) (
return p.db.FetchPayment(paymentHash) return p.db.FetchPayment(paymentHash)
} }
// createSuccessResult creates a success result to send to subscribers.
func createSuccessResult(htlcs []channeldb.HTLCAttempt) *PaymentResult {
// Extract any preimage from the list of HTLCs.
var preimage lntypes.Preimage
for _, htlc := range htlcs {
if htlc.Settle != nil {
preimage = htlc.Settle.Preimage
break
}
}
return &PaymentResult{
Success: true,
Preimage: preimage,
HTLCs: htlcs,
}
}
// createFailResult creates a failed result to send to subscribers.
func createFailedResult(htlcs []channeldb.HTLCAttempt,
reason channeldb.FailureReason) *PaymentResult {
return &PaymentResult{
Success: false,
FailureReason: reason,
HTLCs: htlcs,
}
}
// Fail transitions a payment into the Failed state, and records the reason the // Fail transitions a payment into the Failed state, and records the reason the
// payment failed. After invoking this method, InitPayment should return nil on // payment failed. After invoking this method, InitPayment should return nil on
// its next call for this payment hash, allowing the switch to make a // its next call for this payment hash, allowing the switch to make a
@ -181,17 +194,16 @@ func createFailedResult(htlcs []channeldb.HTLCAttempt,
func (p *controlTower) Fail(paymentHash lntypes.Hash, func (p *controlTower) Fail(paymentHash lntypes.Hash,
reason channeldb.FailureReason) error { reason channeldb.FailureReason) error {
p.paymentsMtx.Lock(paymentHash)
defer p.paymentsMtx.Unlock(paymentHash)
payment, err := p.db.Fail(paymentHash, reason) payment, err := p.db.Fail(paymentHash, reason)
if err != nil { if err != nil {
return err return err
} }
// Notify subscribers of fail event. // Notify subscribers of fail event.
p.notifyFinalEvent( p.notifySubscribers(paymentHash, payment)
paymentHash, createFailedResult(
payment.HTLCs, reason,
),
)
return nil return nil
} }
@ -201,86 +213,81 @@ func (p *controlTower) FetchInFlightPayments() ([]*channeldb.InFlightPayment, er
return p.db.FetchInFlightPayments() return p.db.FetchInFlightPayments()
} }
// SubscribePayment subscribes to updates for the payment with the given hash. // SubscribePayment subscribes to updates for the payment with the given hash. A
// It returns a boolean indicating whether the payment is still in flight and a // first update with the current state of the payment is always sent out
// channel that provides the final outcome of the payment. // immediately.
func (p *controlTower) SubscribePayment(paymentHash lntypes.Hash) ( func (p *controlTower) SubscribePayment(paymentHash lntypes.Hash) (
bool, chan PaymentResult, error) { *ControlTowerSubscriber, error) {
// Create a channel with buffer size 1. For every payment there will be // Take lock before querying the db to prevent missing or duplicating an
// exactly one event sent. // update.
c := make(chan PaymentResult, 1) p.paymentsMtx.Lock(paymentHash)
defer p.paymentsMtx.Unlock(paymentHash)
// Take lock before querying the db to prevent this scenario:
// FetchPayment returns us an in-flight state -> payment succeeds, but
// there is no subscriber to notify yet -> we add ourselves as a
// subscriber -> ... we will never receive a notification.
p.subscribersMtx.Lock()
defer p.subscribersMtx.Unlock()
payment, err := p.db.FetchPayment(paymentHash) payment, err := p.db.FetchPayment(paymentHash)
if err != nil { if err != nil {
return false, nil, err return nil, err
} }
var event PaymentResult subscriber := newControlTowerSubscriber()
switch payment.Status { // Always write current payment state to the channel.
subscriber.queue.ChanIn() <- payment
// Payment is currently in flight. Register this subscriber and // Payment is currently in flight. Register this subscriber for further
// return without writing a result to the channel yet. // updates. Otherwise this update is the final update and the incoming
case channeldb.StatusInFlight: // channel can be closed. This will close the queue's outgoing channel
// when all updates have been written.
if payment.Status == channeldb.StatusInFlight {
p.subscribersMtx.Lock()
p.subscribers[paymentHash] = append( p.subscribers[paymentHash] = append(
p.subscribers[paymentHash], c, p.subscribers[paymentHash], subscriber,
) )
p.subscribersMtx.Unlock()
return true, c, nil } else {
close(subscriber.queue.ChanIn())
// Payment already succeeded. It is not necessary to register as
// a subscriber, because we can send the result on the channel
// immediately.
case channeldb.StatusSucceeded:
event = *createSuccessResult(payment.HTLCs)
// Payment already failed. It is not necessary to register as a
// subscriber, because we can send the result on the channel
// immediately.
case channeldb.StatusFailed:
event = *createFailedResult(
payment.HTLCs, *payment.FailureReason,
)
default:
return false, nil, errors.New("unknown payment status")
} }
// Write immediate result to the channel. return subscriber, nil
c <- event
close(c)
return false, c, nil
} }
// notifyFinalEvent sends a final payment event to all subscribers of this // notifySubscribers sends a final payment event to all subscribers of this
// payment. The channel will be closed after this. // payment. The channel will be closed after this. Note that this function must
func (p *controlTower) notifyFinalEvent(paymentHash lntypes.Hash, // be executed atomically (by means of a lock) with the database update to
event *PaymentResult) { // guarantuee consistency of the notifications.
func (p *controlTower) notifySubscribers(paymentHash lntypes.Hash,
event *channeldb.MPPayment) {
// Get all subscribers for this hash. As there is only a single outcome, // Get all subscribers for this payment.
// the subscriber list can be cleared.
p.subscribersMtx.Lock() p.subscribersMtx.Lock()
list, ok := p.subscribers[paymentHash] list, ok := p.subscribers[paymentHash]
if !ok { if !ok {
p.subscribersMtx.Unlock() p.subscribersMtx.Unlock()
return return
} }
delete(p.subscribers, paymentHash)
// If the payment reached a terminal state, the subscriber list can be
// cleared. There won't be any more updates.
terminal := event.Status != channeldb.StatusInFlight
if terminal {
delete(p.subscribers, paymentHash)
}
p.subscribersMtx.Unlock() p.subscribersMtx.Unlock()
// Notify all subscribers of the event. The subscriber channel is // Notify all subscribers of the event.
// buffered, so it cannot block here.
for _, subscriber := range list { for _, subscriber := range list {
subscriber <- *event select {
close(subscriber) case subscriber.queue.ChanIn() <- event:
// If this event is the last, close the incoming channel
// of the queue. This will signal the subscriber that
// there won't be any more updates.
if terminal {
close(subscriber.queue.ChanIn())
}
// If subscriber disappeared, skip notification. For further
// notifications, we'll keep skipping over this subscriber.
case <-subscriber.quit:
}
} }
} }

@ -55,7 +55,7 @@ func TestControlTowerSubscribeUnknown(t *testing.T) {
pControl := NewControlTower(channeldb.NewPaymentControl(db)) pControl := NewControlTower(channeldb.NewPaymentControl(db))
// Subscription should fail when the payment is not known. // Subscription should fail when the payment is not known.
_, _, err = pControl.SubscribePayment(lntypes.Hash{1}) _, err = pControl.SubscribePayment(lntypes.Hash{1})
if err != channeldb.ErrPaymentNotInitiated { if err != channeldb.ErrPaymentNotInitiated {
t.Fatal("expected subscribe to fail for unknown payment") t.Fatal("expected subscribe to fail for unknown payment")
} }
@ -86,13 +86,10 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
// Subscription should succeed and immediately report the InFlight // Subscription should succeed and immediately report the InFlight
// status. // status.
inFlight, subscriber1, err := pControl.SubscribePayment(info.PaymentHash) subscriber1, err := pControl.SubscribePayment(info.PaymentHash)
if err != nil { if err != nil {
t.Fatalf("expected subscribe to succeed, but got: %v", err) t.Fatalf("expected subscribe to succeed, but got: %v", err)
} }
if !inFlight {
t.Fatalf("unexpected payment to be in flight")
}
// Register an attempt. // Register an attempt.
err = pControl.RegisterAttempt(info.PaymentHash, attempt) err = pControl.RegisterAttempt(info.PaymentHash, attempt)
@ -101,13 +98,10 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
} }
// Register a second subscriber after the first attempt has started. // Register a second subscriber after the first attempt has started.
inFlight, subscriber2, err := pControl.SubscribePayment(info.PaymentHash) subscriber2, err := pControl.SubscribePayment(info.PaymentHash)
if err != nil { if err != nil {
t.Fatalf("expected subscribe to succeed, but got: %v", err) t.Fatalf("expected subscribe to succeed, but got: %v", err)
} }
if !inFlight {
t.Fatalf("unexpected payment to be in flight")
}
// Mark the payment as successful. // Mark the payment as successful.
err = pControl.SettleAttempt( err = pControl.SettleAttempt(
@ -121,32 +115,33 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
} }
// Register a third subscriber after the payment succeeded. // Register a third subscriber after the payment succeeded.
inFlight, subscriber3, err := pControl.SubscribePayment(info.PaymentHash) subscriber3, err := pControl.SubscribePayment(info.PaymentHash)
if err != nil { if err != nil {
t.Fatalf("expected subscribe to succeed, but got: %v", err) t.Fatalf("expected subscribe to succeed, but got: %v", err)
} }
if inFlight {
t.Fatalf("expected payment to be finished")
}
// We expect all subscribers to now report the final outcome followed by // We expect all subscribers to now report the final outcome followed by
// no other events. // no other events.
subscribers := []chan PaymentResult{ subscribers := []*ControlTowerSubscriber{
subscriber1, subscriber2, subscriber3, subscriber1, subscriber2, subscriber3,
} }
for _, s := range subscribers { for _, s := range subscribers {
var result PaymentResult var result *channeldb.MPPayment
select { for result == nil || result.Status == channeldb.StatusInFlight {
case result = <-s: select {
case <-time.After(testTimeout): case item := <-s.Updates:
t.Fatal("timeout waiting for payment result") result = item.(*channeldb.MPPayment)
case <-time.After(testTimeout):
t.Fatal("timeout waiting for payment result")
}
} }
if !result.Success { if result.Status != channeldb.StatusSucceeded {
t.Fatal("unexpected payment state") t.Fatal("unexpected payment state")
} }
if result.Preimage != preimg { settle, _ := result.TerminalInfo()
if settle.Preimage != preimg {
t.Fatal("unexpected preimage") t.Fatal("unexpected preimage")
} }
if len(result.HTLCs) != 1 { if len(result.HTLCs) != 1 {
@ -161,7 +156,7 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
// After the final event, we expect the channel to be closed. // After the final event, we expect the channel to be closed.
select { select {
case _, ok := <-s: case _, ok := <-s.Updates:
if ok { if ok {
t.Fatal("expected channel to be closed") t.Fatal("expected channel to be closed")
} }
@ -204,7 +199,7 @@ func testPaymentControlSubscribeFail(t *testing.T, registerAttempt bool) {
} }
// Subscription should succeed. // Subscription should succeed.
_, subscriber1, err := pControl.SubscribePayment(info.PaymentHash) subscriber1, err := pControl.SubscribePayment(info.PaymentHash)
if err != nil { if err != nil {
t.Fatalf("expected subscribe to succeed, but got: %v", err) t.Fatalf("expected subscribe to succeed, but got: %v", err)
} }
@ -235,29 +230,29 @@ func testPaymentControlSubscribeFail(t *testing.T, registerAttempt bool) {
} }
// Register a second subscriber after the payment failed. // Register a second subscriber after the payment failed.
inFlight, subscriber2, err := pControl.SubscribePayment(info.PaymentHash) subscriber2, err := pControl.SubscribePayment(info.PaymentHash)
if err != nil { if err != nil {
t.Fatalf("expected subscribe to succeed, but got: %v", err) t.Fatalf("expected subscribe to succeed, but got: %v", err)
} }
if inFlight {
t.Fatalf("expected payment to be finished")
}
// We expect all subscribers to now report the final outcome followed by // We expect all subscribers to now report the final outcome followed by
// no other events. // no other events.
subscribers := []chan PaymentResult{ subscribers := []*ControlTowerSubscriber{
subscriber1, subscriber2, subscriber1, subscriber2,
} }
for _, s := range subscribers { for _, s := range subscribers {
var result PaymentResult var result *channeldb.MPPayment
select { for result == nil || result.Status == channeldb.StatusInFlight {
case result = <-s: select {
case <-time.After(testTimeout): case item := <-s.Updates:
t.Fatal("timeout waiting for payment result") result = item.(*channeldb.MPPayment)
case <-time.After(testTimeout):
t.Fatal("timeout waiting for payment result")
}
} }
if result.Success { if result.Status == channeldb.StatusSucceeded {
t.Fatal("unexpected payment state") t.Fatal("unexpected payment state")
} }
@ -282,13 +277,13 @@ func testPaymentControlSubscribeFail(t *testing.T, registerAttempt bool) {
len(result.HTLCs)) len(result.HTLCs))
} }
if result.FailureReason != channeldb.FailureReasonTimeout { if *result.FailureReason != channeldb.FailureReasonTimeout {
t.Fatal("unexpected failure reason") t.Fatal("unexpected failure reason")
} }
// After the final event, we expect the channel to be closed. // After the final event, we expect the channel to be closed.
select { select {
case _, ok := <-s: case _, ok := <-s.Updates:
if ok { if ok {
t.Fatal("expected channel to be closed") t.Fatal("expected channel to be closed")
} }

@ -457,7 +457,7 @@ func (m *mockControlTower) FetchInFlightPayments() (
} }
func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) ( func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) (
bool, chan PaymentResult, error) { *ControlTowerSubscriber, error) {
return false, nil, errors.New("not implemented") return nil, errors.New("not implemented")
} }