routing+routerrpc: add BuildRoute function

This commit is contained in:
Joost Jager 2019-08-29 13:03:37 +02:00
parent a0e91b5d41
commit 299821152a
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
5 changed files with 804 additions and 112 deletions

@ -1197,6 +1197,122 @@ func (m *PairHistory) GetLastAttemptSuccessful() bool {
return false return false
} }
type BuildRouteRequest struct {
//*
//The amount to send expressed in msat. If set to zero, the minimum routable
//amount is used.
AmtMsat int64 `protobuf:"varint,1,opt,name=amt_msat,json=amtMsat,proto3" json:"amt_msat,omitempty"`
//*
//CLTV delta from the current height that should be used for the timelock
//of the final hop
FinalCltvDelta int32 `protobuf:"varint,2,opt,name=final_cltv_delta,json=finalCltvDelta,proto3" json:"final_cltv_delta,omitempty"`
//*
//The channel id of the channel that must be taken to the first hop. If zero,
//any channel may be used.
OutgoingChanId uint64 `protobuf:"varint,3,opt,name=outgoing_chan_id,json=outgoingChanId,proto3" json:"outgoing_chan_id,omitempty"`
//*
//A list of hops that defines the route. This does not include the source hop
//pubkey.
HopPubkeys [][]byte `protobuf:"bytes,4,rep,name=hop_pubkeys,json=hopPubkeys,proto3" json:"hop_pubkeys,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BuildRouteRequest) Reset() { *m = BuildRouteRequest{} }
func (m *BuildRouteRequest) String() string { return proto.CompactTextString(m) }
func (*BuildRouteRequest) ProtoMessage() {}
func (*BuildRouteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{15}
}
func (m *BuildRouteRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BuildRouteRequest.Unmarshal(m, b)
}
func (m *BuildRouteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BuildRouteRequest.Marshal(b, m, deterministic)
}
func (m *BuildRouteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_BuildRouteRequest.Merge(m, src)
}
func (m *BuildRouteRequest) XXX_Size() int {
return xxx_messageInfo_BuildRouteRequest.Size(m)
}
func (m *BuildRouteRequest) XXX_DiscardUnknown() {
xxx_messageInfo_BuildRouteRequest.DiscardUnknown(m)
}
var xxx_messageInfo_BuildRouteRequest proto.InternalMessageInfo
func (m *BuildRouteRequest) GetAmtMsat() int64 {
if m != nil {
return m.AmtMsat
}
return 0
}
func (m *BuildRouteRequest) GetFinalCltvDelta() int32 {
if m != nil {
return m.FinalCltvDelta
}
return 0
}
func (m *BuildRouteRequest) GetOutgoingChanId() uint64 {
if m != nil {
return m.OutgoingChanId
}
return 0
}
func (m *BuildRouteRequest) GetHopPubkeys() [][]byte {
if m != nil {
return m.HopPubkeys
}
return nil
}
type BuildRouteResponse struct {
//*
//Fully specified route that can be used to execute the payment.
Route *lnrpc.Route `protobuf:"bytes,1,opt,name=route,proto3" json:"route,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BuildRouteResponse) Reset() { *m = BuildRouteResponse{} }
func (m *BuildRouteResponse) String() string { return proto.CompactTextString(m) }
func (*BuildRouteResponse) ProtoMessage() {}
func (*BuildRouteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7a0613f69d37b0a5, []int{16}
}
func (m *BuildRouteResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BuildRouteResponse.Unmarshal(m, b)
}
func (m *BuildRouteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BuildRouteResponse.Marshal(b, m, deterministic)
}
func (m *BuildRouteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_BuildRouteResponse.Merge(m, src)
}
func (m *BuildRouteResponse) XXX_Size() int {
return xxx_messageInfo_BuildRouteResponse.Size(m)
}
func (m *BuildRouteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_BuildRouteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_BuildRouteResponse proto.InternalMessageInfo
func (m *BuildRouteResponse) GetRoute() *lnrpc.Route {
if m != nil {
return m.Route
}
return nil
}
func init() { func init() {
proto.RegisterEnum("routerrpc.PaymentState", PaymentState_name, PaymentState_value) proto.RegisterEnum("routerrpc.PaymentState", PaymentState_name, PaymentState_value)
proto.RegisterEnum("routerrpc.Failure_FailureCode", Failure_FailureCode_name, Failure_FailureCode_value) proto.RegisterEnum("routerrpc.Failure_FailureCode", Failure_FailureCode_name, Failure_FailureCode_value)
@ -1216,123 +1332,131 @@ func init() {
proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse") proto.RegisterType((*QueryMissionControlResponse)(nil), "routerrpc.QueryMissionControlResponse")
proto.RegisterType((*NodeHistory)(nil), "routerrpc.NodeHistory") proto.RegisterType((*NodeHistory)(nil), "routerrpc.NodeHistory")
proto.RegisterType((*PairHistory)(nil), "routerrpc.PairHistory") proto.RegisterType((*PairHistory)(nil), "routerrpc.PairHistory")
proto.RegisterType((*BuildRouteRequest)(nil), "routerrpc.BuildRouteRequest")
proto.RegisterType((*BuildRouteResponse)(nil), "routerrpc.BuildRouteResponse")
} }
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{
// 1769 bytes of a gzipped FileDescriptorProto // 1859 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x41, 0x73, 0x22, 0xb9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0x4f, 0x73, 0x22, 0xc7,
0x15, 0x5e, 0x0c, 0x18, 0x78, 0x80, 0xdd, 0x96, 0x3d, 0x76, 0x0f, 0x1e, 0xef, 0x7a, 0xd9, 0xcd, 0x15, 0x37, 0x02, 0x04, 0x3c, 0x40, 0x1a, 0xb5, 0xb4, 0xd2, 0x2c, 0x92, 0xbc, 0x32, 0x76, 0xd6,
0xac, 0x6b, 0x6a, 0x63, 0x6f, 0x9c, 0xda, 0xad, 0xa9, 0x3d, 0x24, 0xc5, 0x80, 0x58, 0xf7, 0x0c, 0xaa, 0x2d, 0x47, 0x72, 0x94, 0xb2, 0x6b, 0xcb, 0x87, 0xa4, 0x58, 0x68, 0xac, 0xd9, 0x85, 0x19,
0x74, 0x7b, 0x05, 0xcc, 0xee, 0x24, 0x07, 0x95, 0x0c, 0xb2, 0xe9, 0x72, 0xd3, 0xcd, 0x74, 0x0b, 0xb9, 0x81, 0xb5, 0x37, 0x39, 0x74, 0xb5, 0xa0, 0x25, 0xa6, 0x34, 0xcc, 0xb0, 0x33, 0x8d, 0xb2,
0x67, 0x9c, 0x43, 0x2e, 0xa9, 0x1c, 0x73, 0xcf, 0xbf, 0xc8, 0x6f, 0xca, 0x25, 0xf9, 0x05, 0x39, 0xca, 0x21, 0x97, 0x9c, 0x73, 0xcf, 0x3d, 0x1f, 0x20, 0x5f, 0x25, 0x5f, 0x21, 0x97, 0xe4, 0x13,
0xa6, 0x2a, 0x25, 0xa9, 0x1b, 0x1a, 0x8c, 0x27, 0x39, 0xd1, 0xfa, 0xde, 0xa7, 0x27, 0xe9, 0x3d, 0xe4, 0x98, 0xaa, 0x54, 0x77, 0xcf, 0xc0, 0x20, 0xa1, 0x8d, 0x4f, 0x9a, 0xfe, 0xbd, 0xd7, 0xaf,
0xbd, 0x4f, 0x0f, 0xd8, 0x0f, 0x83, 0x99, 0xe0, 0x61, 0x38, 0x1d, 0x9e, 0xe9, 0xaf, 0xd3, 0x69, 0x5f, 0xbf, 0x3f, 0xbf, 0x7e, 0x02, 0x76, 0xc3, 0x60, 0x26, 0x78, 0x18, 0x4e, 0x87, 0xa7, 0xfa,
0x18, 0x88, 0x00, 0x95, 0xe6, 0x78, 0xad, 0x14, 0x4e, 0x87, 0x1a, 0xad, 0xff, 0x27, 0x0b, 0xa8, 0xeb, 0x64, 0x1a, 0x06, 0x22, 0x40, 0xa5, 0x39, 0x5e, 0x2b, 0x85, 0xd3, 0xa1, 0x46, 0xeb, 0xff,
0xc7, 0xfd, 0xd1, 0x25, 0xbb, 0x9f, 0x70, 0x5f, 0x10, 0xfe, 0x7e, 0xc6, 0x23, 0x81, 0x10, 0xe4, 0xcd, 0x02, 0xea, 0x71, 0x7f, 0x74, 0xc1, 0xee, 0x26, 0xdc, 0x17, 0x84, 0xbf, 0x9f, 0xf1, 0x48,
0x46, 0x3c, 0x12, 0x66, 0xe6, 0x38, 0x73, 0x52, 0x21, 0xea, 0x1b, 0x19, 0x90, 0x65, 0x13, 0x61, 0x20, 0x04, 0xb9, 0x11, 0x8f, 0x84, 0x99, 0x39, 0xca, 0x1c, 0x57, 0x88, 0xfa, 0x46, 0x06, 0x64,
0x6e, 0x1c, 0x67, 0x4e, 0xb2, 0x44, 0x7e, 0xa2, 0xcf, 0xa1, 0x32, 0xd5, 0xf3, 0xe8, 0x98, 0x45, 0xd9, 0x44, 0x98, 0x6b, 0x47, 0x99, 0xe3, 0x2c, 0x91, 0x9f, 0xe8, 0x33, 0xa8, 0x4c, 0xf5, 0x3e,
0x63, 0x33, 0xab, 0xd8, 0xe5, 0x18, 0xbb, 0x60, 0xd1, 0x18, 0x9d, 0x80, 0x71, 0xed, 0xfa, 0xcc, 0x3a, 0x66, 0xd1, 0xd8, 0xcc, 0x2a, 0xed, 0x72, 0x8c, 0x9d, 0xb3, 0x68, 0x8c, 0x8e, 0xc1, 0xb8,
0xa3, 0x43, 0x4f, 0xdc, 0xd1, 0x11, 0xf7, 0x04, 0x33, 0x73, 0xc7, 0x99, 0x93, 0x3c, 0xd9, 0x52, 0x72, 0x7d, 0xe6, 0xd1, 0xa1, 0x27, 0x6e, 0xe9, 0x88, 0x7b, 0x82, 0x99, 0xb9, 0xa3, 0xcc, 0x71,
0x78, 0xd3, 0x13, 0x77, 0x2d, 0x89, 0xa2, 0xaf, 0x60, 0x3b, 0x71, 0x16, 0xea, 0x5d, 0x98, 0xf9, 0x9e, 0x6c, 0x28, 0xbc, 0xe9, 0x89, 0xdb, 0x96, 0x44, 0xd1, 0x97, 0xb0, 0x99, 0x18, 0x0b, 0xb5,
0xe3, 0xcc, 0x49, 0x89, 0x6c, 0x4d, 0x97, 0xf7, 0xf6, 0x15, 0x6c, 0x0b, 0x77, 0xc2, 0x83, 0x99, 0x17, 0x66, 0xfe, 0x28, 0x73, 0x5c, 0x22, 0x1b, 0xd3, 0x65, 0xdf, 0xbe, 0x84, 0x4d, 0xe1, 0x4e,
0xa0, 0x11, 0x1f, 0x06, 0xfe, 0x28, 0x32, 0x37, 0xb5, 0xc7, 0x18, 0xee, 0x69, 0x14, 0xd5, 0xa1, 0x78, 0x30, 0x13, 0x34, 0xe2, 0xc3, 0xc0, 0x1f, 0x45, 0xe6, 0xba, 0xb6, 0x18, 0xc3, 0x3d, 0x8d,
0x7a, 0xcd, 0x39, 0xf5, 0xdc, 0x89, 0x2b, 0x68, 0xc4, 0x84, 0x59, 0x50, 0x5b, 0x2f, 0x5f, 0x73, 0xa2, 0x3a, 0x54, 0xaf, 0x38, 0xa7, 0x9e, 0x3b, 0x71, 0x05, 0x8d, 0x98, 0x30, 0x0b, 0xca, 0xf5,
0xde, 0x91, 0x58, 0x8f, 0x09, 0xb9, 0xbf, 0x60, 0x26, 0x6e, 0x02, 0xd7, 0xbf, 0xa1, 0xc3, 0x31, 0xf2, 0x15, 0xe7, 0x1d, 0x89, 0xf5, 0x98, 0x90, 0xfe, 0x05, 0x33, 0x71, 0x1d, 0xb8, 0xfe, 0x35,
0xf3, 0xa9, 0x3b, 0x32, 0x8b, 0xc7, 0x99, 0x93, 0x1c, 0xd9, 0x4a, 0xf0, 0xe6, 0x98, 0xf9, 0xd6, 0x1d, 0x8e, 0x99, 0x4f, 0xdd, 0x91, 0x59, 0x3c, 0xca, 0x1c, 0xe7, 0xc8, 0x46, 0x82, 0x37, 0xc7,
0x08, 0x1d, 0x01, 0xa8, 0x33, 0x28, 0x77, 0x66, 0x49, 0xad, 0x58, 0x92, 0x88, 0xf2, 0x85, 0xce, 0xcc, 0xb7, 0x46, 0xe8, 0x10, 0x40, 0xdd, 0x41, 0x99, 0x33, 0x4b, 0xea, 0xc4, 0x92, 0x44, 0x94,
0xa1, 0xac, 0x02, 0x4c, 0xc7, 0xae, 0x2f, 0x22, 0x13, 0x8e, 0xb3, 0x27, 0xe5, 0x73, 0xe3, 0xd4, 0x2d, 0x74, 0x06, 0x65, 0x15, 0x60, 0x3a, 0x76, 0x7d, 0x11, 0x99, 0x70, 0x94, 0x3d, 0x2e, 0x9f,
0xf3, 0x65, 0xac, 0x89, 0xb4, 0x5c, 0xb8, 0xbe, 0x20, 0x69, 0x12, 0xc2, 0x50, 0x94, 0x91, 0xa5, 0x19, 0x27, 0x9e, 0x2f, 0x63, 0x4d, 0xa4, 0xe4, 0xdc, 0xf5, 0x05, 0x49, 0x2b, 0x21, 0x0c, 0x45,
0xc2, 0xbb, 0x33, 0xcb, 0x6a, 0xc2, 0x8b, 0xd3, 0x79, 0x96, 0x4e, 0x1f, 0xa6, 0xe5, 0xb4, 0xc5, 0x19, 0x59, 0x2a, 0xbc, 0x5b, 0xb3, 0xac, 0x36, 0xbc, 0x38, 0x99, 0x67, 0xe9, 0xe4, 0x61, 0x5a,
0x23, 0xd1, 0xf7, 0xee, 0xb0, 0x2f, 0xc2, 0x7b, 0x52, 0x18, 0xe9, 0x51, 0xed, 0x7b, 0xa8, 0xa4, 0x4e, 0x5a, 0x3c, 0x12, 0x7d, 0xef, 0x16, 0xfb, 0x22, 0xbc, 0x23, 0x85, 0x91, 0x5e, 0xd5, 0xbe,
0x0d, 0x32, 0x51, 0xb7, 0xfc, 0x5e, 0xe5, 0x2e, 0x47, 0xe4, 0x27, 0xda, 0x83, 0xfc, 0x1d, 0xf3, 0x83, 0x4a, 0x5a, 0x20, 0x13, 0x75, 0xc3, 0xef, 0x54, 0xee, 0x72, 0x44, 0x7e, 0xa2, 0x1d, 0xc8,
0x66, 0x5c, 0x25, 0xaf, 0x42, 0xf4, 0xe0, 0xfb, 0x8d, 0x97, 0x99, 0xfa, 0x4b, 0xd8, 0xed, 0x87, 0xdf, 0x32, 0x6f, 0xc6, 0x55, 0xf2, 0x2a, 0x44, 0x2f, 0xbe, 0x5b, 0x7b, 0x99, 0xa9, 0xbf, 0x84,
0x6c, 0x78, 0xbb, 0x92, 0xff, 0xd5, 0xcc, 0x66, 0x1e, 0x64, 0xb6, 0xfe, 0x27, 0xa8, 0xc6, 0x93, 0xed, 0x7e, 0xc8, 0x86, 0x37, 0xf7, 0xf2, 0x7f, 0x3f, 0xb3, 0x99, 0x07, 0x99, 0xad, 0xff, 0x09,
0x7a, 0x82, 0x89, 0x59, 0x84, 0x7e, 0x09, 0xf9, 0x48, 0x30, 0xc1, 0x15, 0x79, 0xeb, 0xfc, 0x20, 0xaa, 0xf1, 0xa6, 0x9e, 0x60, 0x62, 0x16, 0xa1, 0x5f, 0x42, 0x3e, 0x12, 0x4c, 0x70, 0xa5, 0xbc,
0x75, 0x94, 0x14, 0x91, 0x13, 0xcd, 0x42, 0x35, 0x28, 0x4e, 0x43, 0xee, 0x4e, 0xd8, 0x4d, 0xb2, 0x71, 0xb6, 0x97, 0xba, 0x4a, 0x4a, 0x91, 0x13, 0xad, 0x85, 0x6a, 0x50, 0x9c, 0x86, 0xdc, 0x9d,
0xad, 0xf9, 0x18, 0xd5, 0x21, 0xaf, 0x26, 0xab, 0x1b, 0x55, 0x3e, 0xaf, 0xa4, 0xc3, 0x48, 0xb4, 0xb0, 0xeb, 0xc4, 0xad, 0xf9, 0x1a, 0xd5, 0x21, 0xaf, 0x36, 0xab, 0x8a, 0x2a, 0x9f, 0x55, 0xd2,
0xa9, 0xfe, 0x1b, 0xd8, 0x56, 0xe3, 0x36, 0xe7, 0x1f, 0xbb, 0xb5, 0x07, 0x50, 0x60, 0x13, 0x9d, 0x61, 0x24, 0x5a, 0x54, 0xff, 0x0d, 0x6c, 0xaa, 0x75, 0x9b, 0xf3, 0x8f, 0x55, 0xed, 0x1e, 0x14,
0x7e, 0x7d, 0x73, 0x37, 0xd9, 0x44, 0x66, 0xbe, 0x3e, 0x02, 0x63, 0x31, 0x3f, 0x9a, 0x06, 0x7e, 0xd8, 0x44, 0xa7, 0x5f, 0x57, 0xee, 0x3a, 0x9b, 0xc8, 0xcc, 0xd7, 0x47, 0x60, 0x2c, 0xf6, 0x47,
0xc4, 0xe5, 0x6d, 0x90, 0xce, 0xe5, 0x65, 0x90, 0x37, 0x67, 0x22, 0x67, 0x65, 0xd4, 0xac, 0xad, 0xd3, 0xc0, 0x8f, 0xb8, 0xac, 0x06, 0x69, 0x5c, 0x16, 0x83, 0xac, 0x9c, 0x89, 0xdc, 0x95, 0x51,
0x18, 0x6f, 0x73, 0xde, 0x8d, 0x98, 0x40, 0xcf, 0xf5, 0x25, 0xa4, 0x5e, 0x30, 0xbc, 0x95, 0xd7, 0xbb, 0x36, 0x62, 0xbc, 0xcd, 0x79, 0x37, 0x62, 0x02, 0x3d, 0xd7, 0x45, 0x48, 0xbd, 0x60, 0x78,
0x9a, 0xdd, 0xc7, 0xee, 0xab, 0x12, 0xee, 0x04, 0xc3, 0xdb, 0x96, 0x04, 0xeb, 0xbf, 0xd7, 0xe5, 0x23, 0xcb, 0x9a, 0xdd, 0xc5, 0xe6, 0xab, 0x12, 0xee, 0x04, 0xc3, 0x9b, 0x96, 0x04, 0xeb, 0xbf,
0xd5, 0x0f, 0xf4, 0xde, 0xff, 0xef, 0xf0, 0x2e, 0x42, 0xb0, 0xf1, 0x78, 0x08, 0x28, 0xec, 0x2e, 0xd7, 0xed, 0xd5, 0x0f, 0xb4, 0xef, 0x3f, 0x3b, 0xbc, 0x8b, 0x10, 0xac, 0x3d, 0x1e, 0x02, 0x0a,
0x39, 0x8f, 0x4f, 0x91, 0x8e, 0x6c, 0x66, 0x25, 0xb2, 0x5f, 0x43, 0xe1, 0x9a, 0xb9, 0xde, 0x2c, 0xdb, 0x4b, 0xc6, 0xe3, 0x5b, 0xa4, 0x23, 0x9b, 0xb9, 0x17, 0xd9, 0xaf, 0xa0, 0x70, 0xc5, 0x5c,
0x4c, 0x1c, 0xa3, 0x54, 0x9a, 0xda, 0xda, 0x42, 0x12, 0x4a, 0xfd, 0x1f, 0x05, 0x28, 0xc4, 0x20, 0x6f, 0x16, 0x26, 0x86, 0x51, 0x2a, 0x4d, 0x6d, 0x2d, 0x21, 0x89, 0x4a, 0xfd, 0x9f, 0x05, 0x28,
0x3a, 0x87, 0xdc, 0x30, 0x18, 0x25, 0xd9, 0xfd, 0xf4, 0xe1, 0xb4, 0xe4, 0xb7, 0x19, 0x8c, 0x38, 0xc4, 0x20, 0x3a, 0x83, 0xdc, 0x30, 0x18, 0x25, 0xd9, 0xfd, 0xf4, 0xe1, 0xb6, 0xe4, 0x6f, 0x33,
0x51, 0x5c, 0xf4, 0x5b, 0xd8, 0x92, 0x45, 0xe5, 0x73, 0x8f, 0xce, 0xa6, 0x23, 0x36, 0x4f, 0xa8, 0x18, 0x71, 0xa2, 0x74, 0xd1, 0x6f, 0x61, 0x43, 0x36, 0x95, 0xcf, 0x3d, 0x3a, 0x9b, 0x8e, 0xd8,
0x99, 0x9a, 0xdd, 0xd4, 0x84, 0x81, 0xb2, 0x93, 0xea, 0x30, 0x3d, 0x44, 0x87, 0x50, 0x1a, 0x0b, 0x3c, 0xa1, 0x66, 0x6a, 0x77, 0x53, 0x2b, 0x0c, 0x94, 0x9c, 0x54, 0x87, 0xe9, 0x25, 0xda, 0x87,
0x6f, 0xa8, 0x33, 0x91, 0x53, 0x17, 0xba, 0x28, 0x01, 0x95, 0x83, 0x3a, 0x54, 0x03, 0xdf, 0x0d, 0xd2, 0x58, 0x78, 0x43, 0x9d, 0x89, 0x9c, 0x2a, 0xe8, 0xa2, 0x04, 0x54, 0x0e, 0xea, 0x50, 0x0d,
0x7c, 0x1a, 0x8d, 0x19, 0x3d, 0xff, 0xf6, 0x3b, 0xa5, 0x17, 0x15, 0x52, 0x56, 0x60, 0x6f, 0xcc, 0x7c, 0x37, 0xf0, 0x69, 0x34, 0x66, 0xf4, 0xec, 0x9b, 0x6f, 0x15, 0x5f, 0x54, 0x48, 0x59, 0x81,
0xce, 0xbf, 0xfd, 0x0e, 0x7d, 0x06, 0x65, 0x55, 0xb5, 0xfc, 0xc3, 0xd4, 0x0d, 0xef, 0x95, 0x50, 0xbd, 0x31, 0x3b, 0xfb, 0xe6, 0x5b, 0xf4, 0x0c, 0xca, 0xaa, 0x6b, 0xf9, 0x87, 0xa9, 0x1b, 0xde,
0x54, 0x89, 0x2a, 0x64, 0xac, 0x10, 0x59, 0x1a, 0xd7, 0x1e, 0xbb, 0x89, 0x94, 0x38, 0x54, 0x89, 0x29, 0xa2, 0xa8, 0x12, 0xd5, 0xc8, 0x58, 0x21, 0xb2, 0x35, 0xae, 0x3c, 0x76, 0x1d, 0x29, 0x72,
0x1e, 0xa0, 0x6f, 0x60, 0x2f, 0x8e, 0x01, 0x8d, 0x82, 0x59, 0x38, 0xe4, 0xd4, 0xf5, 0x47, 0xfc, 0xa8, 0x12, 0xbd, 0x40, 0x5f, 0xc3, 0x4e, 0x1c, 0x03, 0x1a, 0x05, 0xb3, 0x70, 0xc8, 0xa9, 0xeb,
0x83, 0x92, 0x86, 0x2a, 0x41, 0xb1, 0xad, 0xa7, 0x4c, 0x96, 0xb4, 0xa0, 0x7d, 0xd8, 0x1c, 0x73, 0x8f, 0xf8, 0x07, 0x45, 0x0d, 0x55, 0x82, 0x62, 0x59, 0x4f, 0x89, 0x2c, 0x29, 0x41, 0xbb, 0xb0,
0xf7, 0x66, 0xac, 0xa5, 0xa1, 0x4a, 0xe2, 0x51, 0xfd, 0x6f, 0x79, 0x28, 0xa7, 0x02, 0x83, 0x2a, 0x3e, 0xe6, 0xee, 0xf5, 0x58, 0x53, 0x43, 0x95, 0xc4, 0xab, 0xfa, 0x5f, 0xf3, 0x50, 0x4e, 0x05,
0x50, 0x24, 0xb8, 0x87, 0xc9, 0x5b, 0xdc, 0x32, 0x3e, 0x41, 0x27, 0xf0, 0xa5, 0x65, 0x37, 0x1d, 0x06, 0x55, 0xa0, 0x48, 0x70, 0x0f, 0x93, 0xb7, 0xb8, 0x65, 0x7c, 0x82, 0x8e, 0xe1, 0x0b, 0xcb,
0x42, 0x70, 0xb3, 0x4f, 0x1d, 0x42, 0x07, 0xf6, 0x1b, 0xdb, 0xf9, 0xc9, 0xa6, 0x97, 0x8d, 0x77, 0x6e, 0x3a, 0x84, 0xe0, 0x66, 0x9f, 0x3a, 0x84, 0x0e, 0xec, 0x37, 0xb6, 0xf3, 0xa3, 0x4d, 0x2f,
0x5d, 0x6c, 0xf7, 0x69, 0x0b, 0xf7, 0x1b, 0x56, 0xa7, 0x67, 0x64, 0xd0, 0x33, 0x30, 0x17, 0xcc, 0x1a, 0xef, 0xba, 0xd8, 0xee, 0xd3, 0x16, 0xee, 0x37, 0xac, 0x4e, 0xcf, 0xc8, 0xa0, 0x03, 0x30,
0xc4, 0xdc, 0xe8, 0x3a, 0x03, 0xbb, 0x6f, 0x6c, 0xa0, 0xcf, 0xe0, 0xb0, 0x6d, 0xd9, 0x8d, 0x0e, 0x17, 0x9a, 0x89, 0xb8, 0xd1, 0x75, 0x06, 0x76, 0xdf, 0x58, 0x43, 0xcf, 0x60, 0xbf, 0x6d, 0xd9,
0x5d, 0x70, 0x9a, 0x9d, 0xfe, 0x5b, 0x8a, 0x7f, 0xbe, 0xb4, 0xc8, 0x3b, 0x23, 0xbb, 0x8e, 0x70, 0x8d, 0x0e, 0x5d, 0xe8, 0x34, 0x3b, 0xfd, 0xb7, 0x14, 0xff, 0x74, 0x61, 0x91, 0x77, 0x46, 0x76,
0xd1, 0xef, 0x34, 0x13, 0x0f, 0x39, 0xf4, 0x14, 0x9e, 0x68, 0x82, 0x9e, 0x42, 0xfb, 0x8e, 0x43, 0x95, 0xc2, 0x79, 0xbf, 0xd3, 0x4c, 0x2c, 0xe4, 0xd0, 0x53, 0x78, 0xa2, 0x15, 0xf4, 0x16, 0xda,
0x7b, 0x8e, 0x63, 0x1b, 0x79, 0xb4, 0x03, 0x55, 0xcb, 0x7e, 0xdb, 0xe8, 0x58, 0x2d, 0x4a, 0x70, 0x77, 0x1c, 0xda, 0x73, 0x1c, 0xdb, 0xc8, 0xa3, 0x2d, 0xa8, 0x5a, 0xf6, 0xdb, 0x46, 0xc7, 0x6a,
0xa3, 0xd3, 0x35, 0x36, 0xd1, 0x2e, 0x6c, 0xaf, 0xf2, 0x0a, 0xd2, 0x45, 0xc2, 0x73, 0x6c, 0xcb, 0x51, 0x82, 0x1b, 0x9d, 0xae, 0xb1, 0x8e, 0xb6, 0x61, 0xf3, 0xbe, 0x5e, 0x41, 0x9a, 0x48, 0xf4,
0xb1, 0xe9, 0x5b, 0x4c, 0x7a, 0x96, 0x63, 0x1b, 0x45, 0xb4, 0x0f, 0x68, 0xd9, 0x74, 0xd1, 0x6d, 0x1c, 0xdb, 0x72, 0x6c, 0xfa, 0x16, 0x93, 0x9e, 0xe5, 0xd8, 0x46, 0x11, 0xed, 0x02, 0x5a, 0x16,
0x34, 0x8d, 0x12, 0x7a, 0x02, 0x3b, 0xcb, 0xf8, 0x1b, 0xfc, 0xce, 0x00, 0x64, 0xc2, 0x9e, 0xde, 0x9d, 0x77, 0x1b, 0x4d, 0xa3, 0x84, 0x9e, 0xc0, 0xd6, 0x32, 0xfe, 0x06, 0xbf, 0x33, 0x00, 0x99,
0x18, 0x7d, 0x85, 0x3b, 0xce, 0x4f, 0xb4, 0x6b, 0xd9, 0x56, 0x77, 0xd0, 0x35, 0xca, 0x68, 0x0f, 0xb0, 0xa3, 0x1d, 0xa3, 0xaf, 0x70, 0xc7, 0xf9, 0x91, 0x76, 0x2d, 0xdb, 0xea, 0x0e, 0xba, 0x46,
0x8c, 0x36, 0xc6, 0xd4, 0xb2, 0x7b, 0x83, 0x76, 0xdb, 0x6a, 0x5a, 0xd8, 0xee, 0x1b, 0x15, 0xbd, 0x19, 0xed, 0x80, 0xd1, 0xc6, 0x98, 0x5a, 0x76, 0x6f, 0xd0, 0x6e, 0x5b, 0x4d, 0x0b, 0xdb, 0x7d,
0xf2, 0xba, 0x83, 0x57, 0xe5, 0x84, 0xe6, 0x45, 0xc3, 0xb6, 0x71, 0x87, 0xb6, 0xac, 0x5e, 0xe3, 0xa3, 0xa2, 0x4f, 0x5e, 0x75, 0xf1, 0xaa, 0xdc, 0xd0, 0x3c, 0x6f, 0xd8, 0x36, 0xee, 0xd0, 0x96,
0x55, 0x07, 0xb7, 0x8c, 0x2d, 0x74, 0x04, 0x4f, 0xfb, 0xb8, 0x7b, 0xe9, 0x90, 0x06, 0x79, 0x47, 0xd5, 0x6b, 0xbc, 0xea, 0xe0, 0x96, 0xb1, 0x81, 0x0e, 0xe1, 0x69, 0x1f, 0x77, 0x2f, 0x1c, 0xd2,
0x13, 0x7b, 0xbb, 0x61, 0x75, 0x06, 0x04, 0x1b, 0xdb, 0xe8, 0x73, 0x38, 0x22, 0xf8, 0xc7, 0x81, 0x20, 0xef, 0x68, 0x22, 0x6f, 0x37, 0xac, 0xce, 0x80, 0x60, 0x63, 0x13, 0x7d, 0x06, 0x87, 0x04,
0x45, 0x70, 0x8b, 0xda, 0x4e, 0x0b, 0xd3, 0x36, 0x6e, 0xf4, 0x07, 0x04, 0xd3, 0xae, 0xd5, 0xeb, 0xff, 0x30, 0xb0, 0x08, 0x6e, 0x51, 0xdb, 0x69, 0x61, 0xda, 0xc6, 0x8d, 0xfe, 0x80, 0x60, 0xda,
0x59, 0xf6, 0x0f, 0x86, 0x81, 0xbe, 0x84, 0xe3, 0x39, 0x65, 0xee, 0x60, 0x85, 0xb5, 0x23, 0xcf, 0xb5, 0x7a, 0x3d, 0xcb, 0xfe, 0xde, 0x30, 0xd0, 0x17, 0x70, 0x34, 0x57, 0x99, 0x1b, 0xb8, 0xa7,
0x97, 0xa4, 0xd4, 0xc6, 0x3f, 0xf7, 0xe9, 0x25, 0xc6, 0xc4, 0x40, 0xa8, 0x06, 0xfb, 0x8b, 0xe5, 0xb5, 0x25, 0xef, 0x97, 0xa4, 0xd4, 0xc6, 0x3f, 0xf5, 0xe9, 0x05, 0xc6, 0xc4, 0x40, 0xa8, 0x06,
0xf5, 0x02, 0xf1, 0xda, 0xbb, 0xd2, 0x76, 0x89, 0x49, 0xb7, 0x61, 0xcb, 0x04, 0x2f, 0xd9, 0xf6, 0xbb, 0x8b, 0xe3, 0xf5, 0x01, 0xf1, 0xd9, 0xdb, 0x52, 0x76, 0x81, 0x49, 0xb7, 0x61, 0xcb, 0x04,
0xe4, 0xb6, 0x17, 0xb6, 0xd5, 0x6d, 0x3f, 0x41, 0x7b, 0xb0, 0x9d, 0xac, 0x96, 0x80, 0xff, 0x2c, 0x2f, 0xc9, 0x76, 0xa4, 0xdb, 0x0b, 0xd9, 0x7d, 0xb7, 0x9f, 0xa0, 0x1d, 0xd8, 0x4c, 0x4e, 0x4b,
0xa0, 0x03, 0x40, 0x03, 0x9b, 0xe0, 0x46, 0x4b, 0x1e, 0x7e, 0x6e, 0xf8, 0x57, 0xe1, 0x75, 0xae, 0xc0, 0x7f, 0x15, 0xd0, 0x1e, 0xa0, 0x81, 0x4d, 0x70, 0xa3, 0x25, 0x2f, 0x3f, 0x17, 0xfc, 0xbb,
0xb8, 0x61, 0x64, 0xeb, 0x7f, 0xcf, 0x42, 0x75, 0xa9, 0x06, 0xd1, 0x33, 0x28, 0x45, 0xee, 0x8d, 0xf0, 0x3a, 0x57, 0x5c, 0x33, 0xb2, 0xf5, 0xbf, 0x67, 0xa1, 0xba, 0xd4, 0x83, 0xe8, 0x00, 0x4a,
0xcf, 0x84, 0x54, 0x09, 0x2d, 0x20, 0x0b, 0x40, 0xbd, 0x83, 0x63, 0xe6, 0xfa, 0x5a, 0xb9, 0xb4, 0x91, 0x7b, 0xed, 0x33, 0x21, 0x59, 0x42, 0x13, 0xc8, 0x02, 0x50, 0xef, 0xe0, 0x98, 0xb9, 0xbe,
0x72, 0x97, 0x14, 0xa2, 0x74, 0xeb, 0x00, 0x0a, 0xc9, 0x3b, 0x9a, 0x55, 0xf5, 0xba, 0x39, 0xd4, 0x66, 0x2e, 0xcd, 0xdc, 0x25, 0x85, 0x28, 0xde, 0xda, 0x83, 0x42, 0xf2, 0x8e, 0x66, 0x55, 0xbf,
0xef, 0xe7, 0x33, 0x28, 0x49, 0x69, 0x8c, 0x04, 0x9b, 0x4c, 0x55, 0x29, 0x57, 0xc9, 0x02, 0x40, 0xae, 0x0f, 0xf5, 0xfb, 0x79, 0x00, 0x25, 0x49, 0x8d, 0x91, 0x60, 0x93, 0xa9, 0x6a, 0xe5, 0x2a,
0x5f, 0x40, 0x75, 0xc2, 0xa3, 0x88, 0xdd, 0x70, 0xaa, 0xcb, 0x11, 0x14, 0xa3, 0x12, 0x83, 0x6d, 0x59, 0x00, 0xe8, 0x73, 0xa8, 0x4e, 0x78, 0x14, 0xb1, 0x6b, 0x4e, 0x75, 0x3b, 0x82, 0xd2, 0xa8,
0x55, 0x95, 0x5f, 0x40, 0x22, 0x0f, 0x31, 0x29, 0xaf, 0x49, 0x31, 0xa8, 0x49, 0xab, 0xca, 0x2c, 0xc4, 0x60, 0x5b, 0x75, 0xe5, 0xe7, 0x90, 0xd0, 0x43, 0xac, 0x94, 0xd7, 0x4a, 0x31, 0xa8, 0x95,
0x58, 0x5c, 0xf5, 0x69, 0x65, 0x16, 0x0c, 0xbd, 0x80, 0x1d, 0x2d, 0x2d, 0xae, 0xef, 0x4e, 0x66, 0xee, 0x33, 0xb3, 0x60, 0x71, 0xd7, 0xa7, 0x99, 0x59, 0x30, 0xf4, 0x02, 0xb6, 0x34, 0xb5, 0xb8,
0x13, 0x2d, 0x31, 0x05, 0xb5, 0xe5, 0x6d, 0x25, 0x31, 0x1a, 0x57, 0x4a, 0xf3, 0x14, 0x8a, 0x57, 0xbe, 0x3b, 0x99, 0x4d, 0x34, 0xc5, 0x14, 0x94, 0xcb, 0x9b, 0x8a, 0x62, 0x34, 0xae, 0x98, 0xe6,
0x2c, 0xe2, 0xf2, 0x51, 0x88, 0x25, 0xa0, 0x20, 0xc7, 0x6d, 0xce, 0xa5, 0x49, 0x3e, 0x15, 0xa1, 0x29, 0x14, 0x2f, 0x59, 0xc4, 0xe5, 0xa3, 0x10, 0x53, 0x40, 0x41, 0xae, 0xdb, 0x9c, 0x4b, 0x91,
0x14, 0x37, 0x5d, 0xf9, 0x85, 0x6b, 0xce, 0x89, 0x8c, 0xe3, 0x7c, 0x05, 0xf6, 0x61, 0xb1, 0x42, 0x7c, 0x2a, 0x42, 0x49, 0x6e, 0xba, 0xf3, 0x0b, 0x57, 0x9c, 0x13, 0x19, 0xc7, 0xf9, 0x09, 0xec,
0x39, 0xb5, 0x82, 0xc6, 0xd5, 0x0a, 0x2f, 0x60, 0x87, 0x7f, 0x10, 0x21, 0xa3, 0xc1, 0x94, 0xbd, 0xc3, 0xe2, 0x84, 0x72, 0xea, 0x04, 0x8d, 0xab, 0x13, 0x5e, 0xc0, 0x16, 0xff, 0x20, 0x42, 0x46,
0x9f, 0x71, 0x3a, 0x62, 0x82, 0x99, 0x15, 0x15, 0xdc, 0x6d, 0x65, 0x70, 0x14, 0xde, 0x62, 0x82, 0x83, 0x29, 0x7b, 0x3f, 0xe3, 0x74, 0xc4, 0x04, 0x33, 0x2b, 0x2a, 0xb8, 0x9b, 0x4a, 0xe0, 0x28,
0xd5, 0x9f, 0x41, 0x8d, 0xf0, 0x88, 0x8b, 0xae, 0x1b, 0x45, 0x6e, 0xe0, 0x37, 0x03, 0x5f, 0x84, 0xbc, 0xc5, 0x04, 0xab, 0x1f, 0x40, 0x8d, 0xf0, 0x88, 0x8b, 0xae, 0x1b, 0x45, 0x6e, 0xe0, 0x37,
0x81, 0x17, 0xbf, 0x2d, 0xf5, 0x23, 0x38, 0x5c, 0x6b, 0xd5, 0x8f, 0x83, 0x9c, 0xfc, 0xe3, 0x8c, 0x03, 0x5f, 0x84, 0x81, 0x17, 0xbf, 0x2d, 0xf5, 0x43, 0xd8, 0x5f, 0x29, 0xd5, 0x8f, 0x83, 0xdc,
0x87, 0xf7, 0xeb, 0x27, 0xdf, 0xc3, 0xe1, 0x5a, 0x6b, 0xfc, 0xb2, 0x7c, 0x0d, 0x79, 0x3f, 0x18, 0xfc, 0xc3, 0x8c, 0x87, 0x77, 0xab, 0x37, 0xdf, 0xc1, 0xfe, 0x4a, 0x69, 0xfc, 0xb2, 0x7c, 0x05,
0xf1, 0xc8, 0xcc, 0xa8, 0x6e, 0x65, 0x3f, 0x25, 0xe3, 0x76, 0x30, 0xe2, 0x17, 0x6e, 0x24, 0x82, 0x79, 0x3f, 0x18, 0xf1, 0xc8, 0xcc, 0xa8, 0x69, 0x65, 0x37, 0x45, 0xe3, 0x76, 0x30, 0xe2, 0xe7,
0xf0, 0x9e, 0x68, 0x92, 0x64, 0x4f, 0x99, 0x1b, 0x46, 0xe6, 0xc6, 0x03, 0xf6, 0x25, 0x73, 0xc3, 0x6e, 0x24, 0x82, 0xf0, 0x8e, 0x68, 0x25, 0xa9, 0x3d, 0x65, 0x6e, 0x18, 0x99, 0x6b, 0x0f, 0xb4,
0x39, 0x5b, 0x91, 0xea, 0x7f, 0xce, 0x40, 0x39, 0xe5, 0x44, 0x0a, 0xea, 0x74, 0x76, 0x95, 0x34, 0x2f, 0x98, 0x1b, 0xce, 0xb5, 0x95, 0x52, 0xfd, 0xcf, 0x19, 0x28, 0xa7, 0x8c, 0x48, 0x42, 0x9d,
0x32, 0x15, 0x12, 0x8f, 0xd0, 0x73, 0xd8, 0xf2, 0x58, 0x24, 0xa8, 0xd4, 0x60, 0x2a, 0x53, 0x1a, 0xce, 0x2e, 0x93, 0x41, 0xa6, 0x42, 0xe2, 0x15, 0x7a, 0x0e, 0x1b, 0x1e, 0x8b, 0x04, 0x95, 0x1c,
0x3f, 0xbc, 0x2b, 0x28, 0x3a, 0x05, 0x14, 0x88, 0x31, 0x0f, 0x69, 0x34, 0x1b, 0x0e, 0x79, 0x14, 0x4c, 0x65, 0x4a, 0xe3, 0x87, 0xf7, 0x1e, 0x8a, 0x4e, 0x00, 0x05, 0x62, 0xcc, 0x43, 0x1a, 0xcd,
0xd1, 0x69, 0x18, 0x5c, 0xa9, 0x3b, 0xb9, 0x41, 0xd6, 0x58, 0x5e, 0xe7, 0x8a, 0x39, 0x23, 0x5f, 0x86, 0x43, 0x1e, 0x45, 0x74, 0x1a, 0x06, 0x97, 0xaa, 0x26, 0xd7, 0xc8, 0x0a, 0xc9, 0xeb, 0x5c,
0xff, 0x77, 0x06, 0xca, 0xa9, 0xcd, 0xc9, 0x5b, 0x2b, 0x0f, 0x43, 0xaf, 0xc3, 0x60, 0x92, 0xd4, 0x31, 0x67, 0xe4, 0xeb, 0xff, 0xc9, 0x40, 0x39, 0xe5, 0x9c, 0xac, 0x5a, 0x79, 0x19, 0x7a, 0x15,
0xc2, 0x1c, 0x40, 0x26, 0x14, 0xd4, 0x40, 0x04, 0x71, 0x21, 0x24, 0xc3, 0xe5, 0xdb, 0x9e, 0x55, 0x06, 0x93, 0xa4, 0x17, 0xe6, 0x00, 0x32, 0xa1, 0xa0, 0x16, 0x22, 0x88, 0x1b, 0x21, 0x59, 0x2e,
0x1b, 0x4c, 0xdd, 0xf6, 0x73, 0xd8, 0x9b, 0xb8, 0x3e, 0x9d, 0x72, 0x9f, 0x79, 0xee, 0x1f, 0x39, 0x57, 0x7b, 0x56, 0x39, 0x98, 0xaa, 0xf6, 0x33, 0xd8, 0x99, 0xb8, 0x3e, 0x9d, 0x72, 0x9f, 0x79,
0x4d, 0x3a, 0x94, 0x9c, 0x22, 0xae, 0xb5, 0xa1, 0x3a, 0x54, 0x96, 0x4e, 0x92, 0x57, 0x27, 0x59, 0xee, 0x1f, 0x39, 0x4d, 0x26, 0x94, 0x9c, 0x52, 0x5c, 0x29, 0x43, 0x75, 0xa8, 0x2c, 0xdd, 0x24,
0xc2, 0xd0, 0x4b, 0x38, 0x50, 0x51, 0x60, 0x42, 0xf0, 0xc9, 0x54, 0x24, 0x07, 0xbc, 0x9e, 0x79, 0xaf, 0x6e, 0xb2, 0x84, 0xa1, 0x97, 0xb0, 0xa7, 0xa2, 0xc0, 0x84, 0xe0, 0x93, 0xa9, 0x48, 0x2e,
0xaa, 0x06, 0x8a, 0xe4, 0x31, 0xf3, 0x8b, 0xbf, 0x66, 0xa0, 0x92, 0xee, 0xd2, 0x50, 0x15, 0x4a, 0x78, 0x35, 0xf3, 0x54, 0x0f, 0x14, 0xc9, 0x63, 0xe2, 0xfa, 0xdf, 0x32, 0xb0, 0xf5, 0x6a, 0xe6,
0x96, 0x4d, 0xdb, 0x1d, 0xeb, 0x87, 0x8b, 0xbe, 0xf1, 0x89, 0x1c, 0xf6, 0x06, 0xcd, 0x26, 0xc6, 0x7a, 0xa3, 0xa5, 0x39, 0xe5, 0x29, 0x14, 0xe5, 0xf1, 0xa9, 0x39, 0x48, 0x0e, 0x53, 0xaa, 0x60,
0x2d, 0xdc, 0x32, 0x32, 0x08, 0xc1, 0x96, 0x14, 0x12, 0xdc, 0xa2, 0x7d, 0xab, 0x8b, 0x9d, 0x81, 0x57, 0x0d, 0xf6, 0x6b, 0x2b, 0x07, 0xfb, 0x55, 0x23, 0x76, 0x76, 0xe5, 0x88, 0xfd, 0x0c, 0xca,
0x7c, 0x83, 0x76, 0x61, 0x3b, 0xc6, 0x6c, 0x87, 0x12, 0x67, 0xd0, 0xc7, 0x46, 0x16, 0x19, 0x50, 0xe3, 0x60, 0x4a, 0x75, 0xa2, 0x23, 0x33, 0x77, 0x94, 0x3d, 0xae, 0x10, 0x18, 0x07, 0xd3, 0x0b,
0x89, 0x41, 0x4c, 0x88, 0x43, 0x8c, 0x9c, 0x14, 0xce, 0x18, 0x79, 0xf8, 0x9e, 0x25, 0xcf, 0x5d, 0x8d, 0xd4, 0x5f, 0x02, 0x4a, 0x3b, 0x19, 0x57, 0xe5, 0x7c, 0x54, 0xca, 0x3c, 0x3a, 0x2a, 0xbd,
0xfe, 0xfc, 0x2f, 0x39, 0xd8, 0x54, 0x5d, 0x4d, 0x88, 0x2e, 0xa0, 0x9c, 0x6a, 0x85, 0xd1, 0xd1, 0xf8, 0x4b, 0x06, 0x2a, 0xe9, 0x29, 0x14, 0x55, 0xa1, 0x64, 0xd9, 0xb4, 0xdd, 0xb1, 0xbe, 0x3f,
0x47, 0x5b, 0xe4, 0x9a, 0xb9, 0xbe, 0xed, 0x9c, 0x45, 0xdf, 0x64, 0xd0, 0x6b, 0xa8, 0xa4, 0x9b, 0xef, 0x1b, 0x9f, 0xc8, 0x65, 0x6f, 0xd0, 0x6c, 0x62, 0xdc, 0xc2, 0x2d, 0x23, 0x83, 0x10, 0x6c,
0x5d, 0x94, 0x6e, 0x62, 0xd6, 0x74, 0xc1, 0x1f, 0xf5, 0xf5, 0x06, 0x0c, 0x1c, 0x09, 0x77, 0x22, 0x48, 0xa2, 0xc4, 0x2d, 0xda, 0xb7, 0xba, 0xd8, 0x19, 0xc8, 0x37, 0x76, 0x1b, 0x36, 0x63, 0xcc,
0x9b, 0x96, 0xb8, 0x8d, 0x44, 0xb5, 0x14, 0x7f, 0xa5, 0x37, 0xad, 0x1d, 0xae, 0xb5, 0xc5, 0x75, 0x76, 0x28, 0x71, 0x06, 0x7d, 0x6c, 0x64, 0x91, 0x01, 0x95, 0x18, 0xc4, 0x84, 0x38, 0xc4, 0xc8,
0xd5, 0xd1, 0x47, 0x8c, 0x1b, 0xb9, 0x07, 0x47, 0x5c, 0xee, 0x1e, 0x6b, 0x9f, 0x3e, 0x66, 0x8e, 0xc9, 0x87, 0x21, 0x46, 0x1e, 0xbe, 0xd7, 0xc9, 0x73, 0x9e, 0x3f, 0xfb, 0x47, 0x0e, 0xd6, 0x95,
0xbd, 0x8d, 0x60, 0x77, 0x8d, 0x02, 0xa0, 0x5f, 0xa4, 0x77, 0xf0, 0xa8, 0x7e, 0xd4, 0x9e, 0xff, 0x83, 0x21, 0x3a, 0x87, 0x72, 0x6a, 0xd4, 0x47, 0x87, 0x1f, 0xfd, 0x17, 0xa0, 0x66, 0xae, 0x1e,
0x2f, 0xda, 0x62, 0x95, 0x35, 0x52, 0xb1, 0xb4, 0xca, 0xe3, 0x42, 0xb3, 0xb4, 0xca, 0x47, 0x14, 0xab, 0x67, 0xd1, 0xd7, 0x19, 0xf4, 0x1a, 0x2a, 0xe9, 0x61, 0x1e, 0xa5, 0x87, 0xb4, 0x15, 0x53,
0xe7, 0xd5, 0xaf, 0x7e, 0x77, 0x76, 0xe3, 0x8a, 0xf1, 0xec, 0xea, 0x74, 0x18, 0x4c, 0xce, 0x3c, 0xfe, 0x47, 0x6d, 0xbd, 0x01, 0x03, 0x47, 0xc2, 0x9d, 0xc8, 0xa1, 0x2c, 0x1e, 0x93, 0x51, 0x2d,
0xd9, 0x52, 0xf9, 0xae, 0x7f, 0xe3, 0x73, 0xf1, 0x87, 0x20, 0xbc, 0x3d, 0xf3, 0xfc, 0xd1, 0x99, 0xa5, 0x7f, 0x6f, 0xf6, 0xae, 0xed, 0xaf, 0x94, 0xc5, 0x19, 0xea, 0xe8, 0x2b, 0xc6, 0x83, 0xea,
0x6a, 0x8c, 0xcf, 0xe6, 0xee, 0xae, 0x36, 0xd5, 0x3f, 0xdb, 0x5f, 0xff, 0x37, 0x00, 0x00, 0xff, 0x83, 0x2b, 0x2e, 0x4f, 0xc7, 0xb5, 0x4f, 0x1f, 0x13, 0xc7, 0xd6, 0x46, 0xb0, 0xbd, 0x82, 0xe1,
0xff, 0x3c, 0xe4, 0x5c, 0x67, 0x09, 0x0f, 0x00, 0x00, 0xd0, 0x2f, 0xd2, 0x1e, 0x3c, 0xca, 0x8f, 0xb5, 0xe7, 0xff, 0x4f, 0x6d, 0x71, 0xca, 0x0a, 0x2a,
0x5c, 0x3a, 0xe5, 0x71, 0x22, 0x5d, 0x3a, 0xe5, 0x63, 0x8c, 0x6a, 0x01, 0x2c, 0x2a, 0x1a, 0x1d,
0xa4, 0x76, 0x3d, 0xe8, 0xc6, 0xda, 0xe1, 0x23, 0x52, 0x6d, 0xea, 0xd5, 0xaf, 0x7e, 0x77, 0x7a,
0xed, 0x8a, 0xf1, 0xec, 0xf2, 0x64, 0x18, 0x4c, 0x4e, 0x3d, 0x39, 0x7d, 0xfa, 0xae, 0x7f, 0xed,
0x73, 0xf1, 0x87, 0x20, 0xbc, 0x39, 0xf5, 0xfc, 0xd1, 0xa9, 0x6a, 0x8c, 0xd3, 0xb9, 0x95, 0xcb,
0x75, 0xf5, 0x23, 0xc0, 0xaf, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0x85, 0xe2, 0x3f, 0x31, 0x34,
0x10, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -1373,6 +1497,11 @@ type RouterClient interface {
//QueryMissionControl exposes the internal mission control state to callers. //QueryMissionControl exposes the internal mission control state to callers.
//It is a development feature. //It is a development feature.
QueryMissionControl(ctx context.Context, in *QueryMissionControlRequest, opts ...grpc.CallOption) (*QueryMissionControlResponse, error) QueryMissionControl(ctx context.Context, in *QueryMissionControlRequest, opts ...grpc.CallOption) (*QueryMissionControlResponse, error)
//*
//BuildRoute builds a fully specified route based on a list of hop public
//keys. It retrieves the relevant channel policies from the graph in order to
//calculate the correct fees and time locks.
BuildRoute(ctx context.Context, in *BuildRouteRequest, opts ...grpc.CallOption) (*BuildRouteResponse, error)
} }
type routerClient struct { type routerClient struct {
@ -1483,6 +1612,15 @@ func (c *routerClient) QueryMissionControl(ctx context.Context, in *QueryMission
return out, nil return out, nil
} }
func (c *routerClient) BuildRoute(ctx context.Context, in *BuildRouteRequest, opts ...grpc.CallOption) (*BuildRouteResponse, error) {
out := new(BuildRouteResponse)
err := c.cc.Invoke(ctx, "/routerrpc.Router/BuildRoute", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RouterServer is the server API for Router service. // RouterServer is the server API for Router service.
type RouterServer interface { type RouterServer interface {
//* //*
@ -1511,6 +1649,11 @@ type RouterServer interface {
//QueryMissionControl exposes the internal mission control state to callers. //QueryMissionControl exposes the internal mission control state to callers.
//It is a development feature. //It is a development feature.
QueryMissionControl(context.Context, *QueryMissionControlRequest) (*QueryMissionControlResponse, error) QueryMissionControl(context.Context, *QueryMissionControlRequest) (*QueryMissionControlResponse, error)
//*
//BuildRoute builds a fully specified route based on a list of hop public
//keys. It retrieves the relevant channel policies from the graph in order to
//calculate the correct fees and time locks.
BuildRoute(context.Context, *BuildRouteRequest) (*BuildRouteResponse, error)
} }
func RegisterRouterServer(s *grpc.Server, srv RouterServer) { func RegisterRouterServer(s *grpc.Server, srv RouterServer) {
@ -1631,6 +1774,24 @@ func _Router_QueryMissionControl_Handler(srv interface{}, ctx context.Context, d
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Router_BuildRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BuildRouteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RouterServer).BuildRoute(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/routerrpc.Router/BuildRoute",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RouterServer).BuildRoute(ctx, req.(*BuildRouteRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Router_serviceDesc = grpc.ServiceDesc{ var _Router_serviceDesc = grpc.ServiceDesc{
ServiceName: "routerrpc.Router", ServiceName: "routerrpc.Router",
HandlerType: (*RouterServer)(nil), HandlerType: (*RouterServer)(nil),
@ -1651,6 +1812,10 @@ var _Router_serviceDesc = grpc.ServiceDesc{
MethodName: "QueryMissionControl", MethodName: "QueryMissionControl",
Handler: _Router_QueryMissionControl_Handler, Handler: _Router_QueryMissionControl_Handler,
}, },
{
MethodName: "BuildRoute",
Handler: _Router_BuildRoute_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {

@ -380,6 +380,39 @@ message PairHistory {
bool last_attempt_successful = 6 [json_name = "last_attempt_successful"]; bool last_attempt_successful = 6 [json_name = "last_attempt_successful"];
} }
message BuildRouteRequest {
/**
The amount to send expressed in msat. If set to zero, the minimum routable
amount is used.
*/
int64 amt_msat = 1;
/**
CLTV delta from the current height that should be used for the timelock
of the final hop
*/
int32 final_cltv_delta = 2;
/**
The channel id of the channel that must be taken to the first hop. If zero,
any channel may be used.
*/
uint64 outgoing_chan_id = 3;
/**
A list of hops that defines the route. This does not include the source hop
pubkey.
*/
repeated bytes hop_pubkeys = 4;
}
message BuildRouteResponse {
/**
Fully specified route that can be used to execute the payment.
*/
lnrpc.Route route = 1;
}
service Router { service Router {
/** /**
SendPayment attempts to route a payment described by the passed SendPayment attempts to route a payment described by the passed
@ -419,4 +452,11 @@ service Router {
It is a development feature. It is a development feature.
*/ */
rpc QueryMissionControl(QueryMissionControlRequest) returns (QueryMissionControlResponse); rpc QueryMissionControl(QueryMissionControlRequest) returns (QueryMissionControlResponse);
/**
BuildRoute builds a fully specified route based on a list of hop public
keys. It retrieves the relevant channel policies from the graph in order to
calculate the correct fees and time locks.
*/
rpc BuildRoute(BuildRouteRequest) returns (BuildRouteResponse);
} }

@ -72,6 +72,10 @@ var (
Entity: "offchain", Entity: "offchain",
Action: "write", Action: "write",
}}, }},
"/routerrpc.Router/BuildRoute": {{
Entity: "offchain",
Action: "read",
}},
} }
// DefaultRouterMacFilename is the default name of the router macaroon // DefaultRouterMacFilename is the default name of the router macaroon
@ -612,3 +616,49 @@ func marshallFailureReason(reason channeldb.FailureReason) (
return 0, errors.New("unknown failure reason") return 0, errors.New("unknown failure reason")
} }
// BuildRoute builds a route from a list of hop addresses.
func (s *Server) BuildRoute(ctx context.Context,
req *BuildRouteRequest) (*BuildRouteResponse, error) {
// Unmarshall hop list.
hops := make([]route.Vertex, len(req.HopPubkeys))
for i, pubkeyBytes := range req.HopPubkeys {
pubkey, err := route.NewVertexFromBytes(pubkeyBytes)
if err != nil {
return nil, err
}
hops[i] = pubkey
}
// Prepare BuildRoute call parameters from rpc request.
var amt *lnwire.MilliSatoshi
if req.AmtMsat != 0 {
rpcAmt := lnwire.MilliSatoshi(req.AmtMsat)
amt = &rpcAmt
}
var outgoingChan *uint64
if req.OutgoingChanId != 0 {
outgoingChan = &req.OutgoingChanId
}
// Build the route and return it to the caller.
route, err := s.cfg.Router.BuildRoute(
amt, hops, outgoingChan, req.FinalCltvDelta,
)
if err != nil {
return nil, err
}
rpcRoute, err := s.cfg.RouterBackend.MarshallRoute(route)
if err != nil {
return nil, err
}
routeResp := &BuildRouteResponse{
Route: rpcRoute,
}
return routeResp, nil
}

@ -2249,3 +2249,297 @@ func generateBandwidthHints(sourceNode *channeldb.LightningNode,
return bandwidthHints, nil return bandwidthHints, nil
} }
// runningAmounts keeps running amounts while the route is traversed.
type runningAmounts struct {
// amt is the intended amount to send via the route.
amt lnwire.MilliSatoshi
// max is the running maximum that the route can carry.
max lnwire.MilliSatoshi
}
// prependChannel returns a new set of running amounts that would result from
// prepending the given channel to the route. If canIncreaseAmt is set, the
// amount may be increased if it is too small to satisfy the channel's minimum
// htlc amount.
func (r *runningAmounts) prependChannel(policy *channeldb.ChannelEdgePolicy,
capacity btcutil.Amount, localChan bool, canIncreaseAmt bool) (
runningAmounts, error) {
// Determine max htlc value.
maxHtlc := lnwire.NewMSatFromSatoshis(capacity)
if policy.MessageFlags.HasMaxHtlc() {
maxHtlc = policy.MaxHTLC
}
amt := r.amt
// If we have a specific amount for which we are building the route,
// validate it against the channel constraints and return the new
// running amount.
if !canIncreaseAmt {
if amt < policy.MinHTLC || amt > maxHtlc {
return runningAmounts{}, fmt.Errorf("channel htlc "+
"constraints [%v - %v] violated with amt %v",
policy.MinHTLC, maxHtlc, amt)
}
// Update running amount by adding the fee for non-local
// channels.
if !localChan {
amt += policy.ComputeFee(amt)
}
return runningAmounts{
amt: amt,
}, nil
}
// Adapt the minimum amount to what this channel allows.
if policy.MinHTLC > r.amt {
amt = policy.MinHTLC
}
// Update the maximum amount too to be able to detect incompatible
// channels.
max := r.max
if maxHtlc < r.max {
max = maxHtlc
}
// If we get in the situation that the minimum amount exceeds the
// maximum amount (enforced further down stream), we have incompatible
// channel policies.
//
// There is possibility with pubkey addressing that we should have
// selected a different channel downstream, but we don't backtrack to
// try to fix that. It would complicate path finding while we expect
// this situation to be rare. The spec recommends to keep all policies
// towards a peer identical. If that is the case, there isn't a better
// channel that we should have selected.
if amt > max {
return runningAmounts{},
fmt.Errorf("incompatible channel policies: %v "+
"exceeds %v", amt, max)
}
// Add fees to the running amounts. Skip the source node fees as
// those do not need to be paid.
if !localChan {
amt += policy.ComputeFee(amt)
max += policy.ComputeFee(max)
}
return runningAmounts{amt: amt, max: max}, nil
}
// ErrNoChannel is returned when a route cannot be built because there are no
// channels that satisfy all requirements.
type ErrNoChannel struct {
position int
fromNode route.Vertex
}
// Error returns a human readable string describing the error.
func (e ErrNoChannel) Error() string {
return fmt.Sprintf("no matching outgoing channel available for "+
"node %v (%v)", e.position, e.fromNode)
}
// BuildRoute returns a fully specified route based on a list of pubkeys. If
// amount is nil, the minimum routable amount is used. To force a specific
// outgoing channel, use the outgoingChan parameter.
func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
hops []route.Vertex, outgoingChan *uint64,
finalCltvDelta int32) (*route.Route, error) {
log.Tracef("BuildRoute called: hopsCount=%v, amt=%v",
len(hops), amt)
// If no amount is specified, we need to build a route for the minimum
// amount that this route can carry.
useMinAmt := amt == nil
// We'll attempt to obtain a set of bandwidth hints that helps us select
// the best outgoing channel to use in case no outgoing channel is set.
bandwidthHints, err := generateBandwidthHints(
r.selfNode, r.cfg.QueryBandwidth,
)
if err != nil {
return nil, err
}
// Allocate a list that will contain the selected channels for this
// route.
edges := make([]*channeldb.ChannelEdgePolicy, len(hops))
// Keep a running amount and the maximum for this route.
amts := runningAmounts{
max: lnwire.MilliSatoshi(^uint64(0)),
}
if useMinAmt {
// For minimum amount routes, aim to deliver at least 1 msat to
// the destination. There are nodes in the wild that have a
// min_htlc channel policy of zero, which could lead to a zero
// amount payment being made.
amts.amt = 1
} else {
// If an amount is specified, we need to build a route that
// delivers exactly this amount to the final destination.
amts.amt = *amt
}
// Traverse hops backwards to accumulate fees in the running amounts.
source := r.selfNode.PubKeyBytes
for i := len(hops) - 1; i >= 0; i-- {
toNode := hops[i]
var fromNode route.Vertex
if i == 0 {
fromNode = source
} else {
fromNode = hops[i-1]
}
localChan := i == 0
// Iterate over candidate channels to select the channel
// to use for the final route.
var (
bestEdge *channeldb.ChannelEdgePolicy
bestAmts *runningAmounts
bestBandwidth lnwire.MilliSatoshi
)
cb := func(tx *bbolt.Tx,
edgeInfo *channeldb.ChannelEdgeInfo,
_, inEdge *channeldb.ChannelEdgePolicy) error {
chanID := edgeInfo.ChannelID
// Apply outgoing channel restriction is active.
if localChan && outgoingChan != nil &&
chanID != *outgoingChan {
return nil
}
// No unknown policy channels.
if inEdge == nil {
return nil
}
// Before we can process the edge, we'll need to
// fetch the node on the _other_ end of this
// channel as we may later need to iterate over
// the incoming edges of this node if we explore
// it further.
chanFromNode, err := edgeInfo.FetchOtherNode(
tx, toNode[:],
)
if err != nil {
return err
}
// Continue searching if this channel doesn't
// connect with the previous hop.
if chanFromNode.PubKeyBytes != fromNode {
return nil
}
// Validate whether this channel's policy is satisfied
// and obtain the new running amounts if this channel
// was to be selected.
newAmts, err := amts.prependChannel(
inEdge, edgeInfo.Capacity, localChan,
useMinAmt,
)
if err != nil {
log.Tracef("Skipping chan %v: %v",
inEdge.ChannelID, err)
return nil
}
// If we already have a best edge, check whether this
// edge is better.
bandwidth := bandwidthHints[chanID]
if bestEdge != nil {
if localChan {
// For local channels, better is defined
// as having more bandwidth. We try to
// maximize the chance that the returned
// route succeeds.
if bandwidth < bestBandwidth {
return nil
}
} else {
// For other channels, better is defined
// as lower fees for the amount to send.
// Normally all channels between two
// nodes should have the same policy,
// but in case not we minimize our cost
// here. Regular path finding would do
// the same.
if newAmts.amt > bestAmts.amt {
return nil
}
}
}
// If we get here, the current edge is better. Replace
// the best.
bestEdge = inEdge
bestAmts = &newAmts
bestBandwidth = bandwidth
return nil
}
err := r.cfg.Graph.ForEachNodeChannel(nil, toNode[:], cb)
if err != nil {
return nil, err
}
// There is no matching channel. Stop building the route here.
if bestEdge == nil {
return nil, ErrNoChannel{
fromNode: fromNode,
position: i,
}
}
log.Tracef("Select channel %v at position %v", bestEdge.ChannelID, i)
edges[i] = bestEdge
amts = *bestAmts
}
_, height, err := r.cfg.Chain.GetBestBlock()
if err != nil {
return nil, err
}
var receiverAmt lnwire.MilliSatoshi
if useMinAmt {
// We've calculated the minimum amount for the htlc that the
// source node hands out. The newRoute call below expects the
// amount that must reach the receiver after subtraction of fees
// along the way. Iterate over all edges to calculate the
// receiver amount.
receiverAmt = amts.amt
for _, edge := range edges[1:] {
receiverAmt -= edge.ComputeFeeFromIncoming(receiverAmt)
}
} else {
// Deliver the specified amount to the receiver.
receiverAmt = *amt
}
// Build and return the final route.
return newRoute(
receiverAmt, source, edges, uint32(height),
uint16(finalCltvDelta), nil,
)
}

@ -3330,3 +3330,146 @@ func TestSendToRouteStructuredError(t *testing.T) {
t.Fatalf("initPayment not called") t.Fatalf("initPayment not called")
} }
} }
// TestBuildRoute tests whether correct routes are built.
func TestBuildRoute(t *testing.T) {
// Setup a three node network.
chanCapSat := btcutil.Amount(100000)
testChannels := []*testChannel{
// Create two local channels from a. The bandwidth is estimated
// in this test as the channel capacity. For building routes, we
// expected the channel with the largest estimated bandwidth to
// be selected.
symmetricTestChannel("a", "b", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 20000,
MinHTLC: lnwire.NewMSatFromSatoshis(5),
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
}, 1),
symmetricTestChannel("a", "b", chanCapSat/2, &testChannelPolicy{
Expiry: 144,
FeeRate: 20000,
MinHTLC: lnwire.NewMSatFromSatoshis(5),
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat / 2),
}, 6),
// Create two channels from b to c. For building routes, we
// expect the lowest cost channel to be selected. Note that this
// isn't a situation that we are expecting in reality. Routing
// nodes are recommended to keep their channel policies towards
// the same peer identical.
symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 50000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(120),
}, 2),
symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 60000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(120),
}, 7),
symmetricTestChannel("a", "e", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 80000,
MinHTLC: lnwire.NewMSatFromSatoshis(5),
MaxHTLC: lnwire.NewMSatFromSatoshis(10),
}, 5),
symmetricTestChannel("e", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 100000,
MinHTLC: lnwire.NewMSatFromSatoshis(20),
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
}, 4),
}
testGraph, err := createTestGraphFromChannels(testChannels, "a")
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraph.cleanUp()
const startingBlockHeight = 101
ctx, cleanUp, err := createTestCtxFromGraphInstance(
startingBlockHeight, testGraph,
)
if err != nil {
t.Fatalf("unable to create router: %v", err)
}
defer cleanUp()
checkHops := func(rt *route.Route, expected []uint64) {
if len(rt.Hops) != len(expected) {
t.Fatal("hop count mismatch")
}
for i, hop := range rt.Hops {
if hop.ChannelID != expected[i] {
t.Fatalf("expected channel %v at pos %v, but "+
"got channel %v",
expected[i], i, hop.ChannelID)
}
}
}
// Create hop list from the route node pubkeys.
hops := []route.Vertex{
ctx.aliases["b"], ctx.aliases["c"],
}
amt := lnwire.NewMSatFromSatoshis(100)
// Build the route for the given amount.
rt, err := ctx.router.BuildRoute(
&amt, hops, nil, 40,
)
if err != nil {
t.Fatal(err)
}
// Check that we get the expected route back. The total amount should be
// the amount to deliver to hop c (100 sats) plus the fee for hop b (5
// sats).
checkHops(rt, []uint64{1, 2})
if rt.TotalAmount != 105000 {
t.Fatalf("unexpected total amount %v", rt.TotalAmount)
}
// Build the route for the minimum amount.
rt, err = ctx.router.BuildRoute(
nil, hops, nil, 40,
)
if err != nil {
t.Fatal(err)
}
// Check that we get the expected route back. The minimum that we can
// send from b to c is 20 sats. Hop b charges 1 sat for the forwarding.
// The channel between hop a and b can carry amounts in the range [5,
// 100], so 21 sats is the minimum amount for this route.
checkHops(rt, []uint64{1, 2})
if rt.TotalAmount != 21000 {
t.Fatalf("unexpected total amount %v", rt.TotalAmount)
}
// Test a route that contains incompatible channel htlc constraints.
// There is no amount that can pass through both channel 5 and 4.
hops = []route.Vertex{
ctx.aliases["e"], ctx.aliases["c"],
}
_, err = ctx.router.BuildRoute(
nil, hops, nil, 40,
)
errNoChannel, ok := err.(ErrNoChannel)
if !ok {
t.Fatalf("expected incompatible policies error, but got %v",
err)
}
if errNoChannel.position != 0 {
t.Fatalf("unexpected no channel error position")
}
if errNoChannel.fromNode != ctx.aliases["a"] {
t.Fatalf("unexpected no channel error node")
}
}