Merge pull request #4257 from joostjager/multi-outgoing-chan-restriction

routing+routerrpc: add multiple outgoing channel restriction
This commit is contained in:
Joost Jager 2020-05-12 13:07:36 +02:00 committed by GitHub
commit 185813abb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 303 additions and 229 deletions

@ -321,7 +321,10 @@ func sendPaymentRequest(ctx *cli.Context,
client := lnrpc.NewLightningClient(conn) client := lnrpc.NewLightningClient(conn)
routerClient := routerrpc.NewRouterClient(conn) routerClient := routerrpc.NewRouterClient(conn)
req.OutgoingChanId = ctx.Uint64("outgoing_chan_id") outChan := ctx.Uint64("outgoing_chan_id")
if outChan != 0 {
req.OutgoingChanIds = []uint64{outChan}
}
if ctx.IsSet(lastHopFlag.Name) { if ctx.IsSet(lastHopFlag.Name) {
lastHop, err := route.NewVertexFromStr( lastHop, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name), ctx.String(lastHopFlag.Name),

@ -248,9 +248,14 @@ type SendPaymentRequest struct {
//The fields fee_limit_sat and fee_limit_msat are mutually exclusive. //The fields fee_limit_sat and fee_limit_msat are mutually exclusive.
FeeLimitMsat int64 `protobuf:"varint,13,opt,name=fee_limit_msat,json=feeLimitMsat,proto3" json:"fee_limit_msat,omitempty"` FeeLimitMsat int64 `protobuf:"varint,13,opt,name=fee_limit_msat,json=feeLimitMsat,proto3" json:"fee_limit_msat,omitempty"`
// //
//The channel id of the channel that must be taken to the first hop. If zero, //Deprecated, use outgoing_chan_ids. The channel id of the channel that must
//be taken to the first hop. If zero, any channel may be used (unless
//outgoing_chan_ids are set).
OutgoingChanId uint64 `protobuf:"varint,8,opt,name=outgoing_chan_id,json=outgoingChanId,proto3" json:"outgoing_chan_id,omitempty"` // Deprecated: Do not use.
//
//The channel ids of the channels are allowed for the first hop. If empty,
//any channel may be used. //any channel may be used.
OutgoingChanId uint64 `protobuf:"varint,8,opt,name=outgoing_chan_id,json=outgoingChanId,proto3" json:"outgoing_chan_id,omitempty"` OutgoingChanIds []uint64 `protobuf:"varint,19,rep,packed,name=outgoing_chan_ids,json=outgoingChanIds,proto3" json:"outgoing_chan_ids,omitempty"`
// //
//The pubkey of the last hop of the route. If empty, any hop may be used. //The pubkey of the last hop of the route. If empty, any hop may be used.
LastHopPubkey []byte `protobuf:"bytes,14,opt,name=last_hop_pubkey,json=lastHopPubkey,proto3" json:"last_hop_pubkey,omitempty"` LastHopPubkey []byte `protobuf:"bytes,14,opt,name=last_hop_pubkey,json=lastHopPubkey,proto3" json:"last_hop_pubkey,omitempty"`
@ -379,6 +384,7 @@ func (m *SendPaymentRequest) GetFeeLimitMsat() int64 {
return 0 return 0
} }
// Deprecated: Do not use.
func (m *SendPaymentRequest) GetOutgoingChanId() uint64 { func (m *SendPaymentRequest) GetOutgoingChanId() uint64 {
if m != nil { if m != nil {
return m.OutgoingChanId return m.OutgoingChanId
@ -386,6 +392,13 @@ func (m *SendPaymentRequest) GetOutgoingChanId() uint64 {
return 0 return 0
} }
func (m *SendPaymentRequest) GetOutgoingChanIds() []uint64 {
if m != nil {
return m.OutgoingChanIds
}
return nil
}
func (m *SendPaymentRequest) GetLastHopPubkey() []byte { func (m *SendPaymentRequest) GetLastHopPubkey() []byte {
if m != nil { if m != nil {
return m.LastHopPubkey return m.LastHopPubkey
@ -1746,148 +1759,149 @@ 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{
// 2248 bytes of a gzipped FileDescriptorProto // 2268 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x58, 0xcd, 0x76, 0xdb, 0xb8, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x58, 0xcf, 0x77, 0xdb, 0xc6,
0xf5, 0x1f, 0x4a, 0x94, 0x2d, 0x5d, 0x7d, 0x98, 0x86, 0x33, 0x8e, 0xfe, 0x72, 0x32, 0xa3, 0xe1, 0xf1, 0x0f, 0x48, 0x50, 0x22, 0x87, 0x3f, 0x04, 0x2d, 0x1d, 0x19, 0x5f, 0x2a, 0x4e, 0x18, 0x7c,
0x7f, 0x26, 0xd1, 0x49, 0x33, 0x76, 0xc6, 0xed, 0x69, 0xe7, 0xb4, 0x9d, 0x69, 0x65, 0x89, 0x8e, 0x13, 0x87, 0xcf, 0x4d, 0x24, 0x47, 0xed, 0x6b, 0xf3, 0xda, 0x26, 0x2d, 0x45, 0x42, 0x11, 0x6c,
0x99, 0xc8, 0xa4, 0x06, 0x92, 0x9d, 0xa4, 0x59, 0xe0, 0xd0, 0x12, 0x64, 0xb1, 0xe6, 0x87, 0x4a, 0x0a, 0x60, 0x96, 0x94, 0x6c, 0xd7, 0x87, 0x7d, 0x10, 0xb9, 0x14, 0x51, 0x81, 0x00, 0x0b, 0x2c,
0x42, 0xc9, 0x78, 0xd9, 0x5d, 0x4f, 0xb7, 0x7d, 0x88, 0xee, 0xfa, 0x04, 0xdd, 0xf6, 0x39, 0xba, 0xed, 0xe8, 0xd8, 0x5b, 0x5f, 0xaf, 0xfd, 0x23, 0x7a, 0xeb, 0x5f, 0xd0, 0xff, 0xa5, 0xd7, 0xde,
0xed, 0x13, 0x74, 0xdd, 0x03, 0x10, 0x94, 0x28, 0x5b, 0x4e, 0xda, 0x8d, 0x4d, 0xfc, 0xee, 0xc5, 0xfb, 0x5e, 0xcf, 0x7d, 0xbb, 0x58, 0x90, 0xa0, 0x44, 0xd9, 0xed, 0x45, 0xc2, 0x7e, 0xe6, 0xb3,
0xc5, 0xbd, 0xb8, 0xbf, 0x8b, 0x0b, 0x08, 0x76, 0xa3, 0x70, 0xce, 0x68, 0x14, 0xcd, 0x46, 0x07, 0xb3, 0x33, 0x3b, 0x33, 0x3b, 0xbb, 0x84, 0xbd, 0x28, 0x5c, 0x30, 0x1a, 0x45, 0xf3, 0xd1, 0x61,
0xc9, 0xd7, 0xfe, 0x2c, 0x0a, 0x59, 0x88, 0x4a, 0x0b, 0xbc, 0x51, 0x8a, 0x66, 0xa3, 0x04, 0xd5, 0xf2, 0x75, 0x30, 0x8f, 0x42, 0x16, 0xa2, 0xd2, 0x12, 0x6f, 0x94, 0xa2, 0xf9, 0x28, 0x41, 0x8d,
0xff, 0xb1, 0x01, 0x68, 0x40, 0x83, 0x71, 0xdf, 0xb9, 0xf6, 0x69, 0xc0, 0x30, 0xfd, 0xc3, 0x9c, 0x7f, 0x6d, 0x01, 0x1a, 0xd0, 0x60, 0xdc, 0x77, 0x6f, 0x66, 0x34, 0x60, 0x98, 0xfe, 0x61, 0x41,
0xc6, 0x0c, 0x21, 0x50, 0xc7, 0x34, 0x66, 0x75, 0xa5, 0xa9, 0xb4, 0x2a, 0x58, 0x7c, 0x23, 0x0d, 0x63, 0x86, 0x10, 0xa8, 0x63, 0x1a, 0x33, 0x5d, 0x69, 0x2a, 0xad, 0x0a, 0x16, 0xdf, 0x48, 0x83,
0xf2, 0x8e, 0xcf, 0xea, 0xb9, 0xa6, 0xd2, 0xca, 0x63, 0xfe, 0x89, 0xfe, 0x0f, 0x8a, 0x8e, 0xcf, 0xbc, 0x3b, 0x63, 0x7a, 0xae, 0xa9, 0xb4, 0xf2, 0x98, 0x7f, 0xa2, 0xff, 0x83, 0xa2, 0x3b, 0x63,
0x88, 0x1f, 0x3b, 0xac, 0x5e, 0x11, 0xf0, 0xa6, 0xe3, 0xb3, 0xd3, 0xd8, 0x61, 0xe8, 0x0b, 0xa8, 0x64, 0x16, 0xbb, 0x4c, 0xaf, 0x08, 0x78, 0xdb, 0x9d, 0xb1, 0xb3, 0xd8, 0x65, 0xe8, 0x53, 0xa8,
0xcc, 0x12, 0x93, 0x64, 0xea, 0xc4, 0xd3, 0x7a, 0x5e, 0x18, 0x2a, 0x4b, 0xec, 0xc4, 0x89, 0xa7, 0xcc, 0x13, 0x95, 0x64, 0xea, 0xc6, 0x53, 0x3d, 0x2f, 0x14, 0x95, 0x25, 0x76, 0xea, 0xc6, 0x53,
0xa8, 0x05, 0xda, 0xc4, 0x0d, 0x1c, 0x8f, 0x8c, 0x3c, 0xf6, 0x8e, 0x8c, 0xa9, 0xc7, 0x9c, 0xba, 0xd4, 0x02, 0x6d, 0xe2, 0x05, 0xae, 0x4f, 0x46, 0x3e, 0x7b, 0x43, 0xc6, 0xd4, 0x67, 0xae, 0xae,
0xda, 0x54, 0x5a, 0x05, 0x5c, 0x13, 0x78, 0xc7, 0x63, 0xef, 0xba, 0x1c, 0x45, 0x8f, 0x61, 0x2b, 0x36, 0x95, 0x56, 0x01, 0xd7, 0x04, 0xde, 0xf1, 0xd9, 0x9b, 0x2e, 0x47, 0xd1, 0x17, 0xb0, 0x93,
0x35, 0x16, 0x25, 0x0e, 0xd6, 0x0b, 0x4d, 0xa5, 0x55, 0xc2, 0xb5, 0xd9, 0xaa, 0xdb, 0x8f, 0x61, 0x2a, 0x8b, 0x12, 0x03, 0xf5, 0x42, 0x53, 0x69, 0x95, 0x70, 0x6d, 0xbe, 0x6e, 0xf6, 0x17, 0xb0,
0x8b, 0xb9, 0x3e, 0x0d, 0xe7, 0x8c, 0xc4, 0x74, 0x14, 0x06, 0xe3, 0xb8, 0xbe, 0x91, 0x58, 0x94, 0xc3, 0xbc, 0x19, 0x0d, 0x17, 0x8c, 0xc4, 0x74, 0x14, 0x06, 0xe3, 0x58, 0xdf, 0x4a, 0x34, 0x4a,
0xf0, 0x20, 0x41, 0x91, 0x0e, 0xd5, 0x09, 0xa5, 0xc4, 0x73, 0x7d, 0x97, 0x11, 0xee, 0xfe, 0xa6, 0x78, 0x90, 0xa0, 0xc8, 0x80, 0xea, 0x84, 0x52, 0xe2, 0x7b, 0x33, 0x8f, 0x11, 0x6e, 0xfe, 0xb6,
0x70, 0xbf, 0x3c, 0xa1, 0xb4, 0xc7, 0xb1, 0x81, 0xc3, 0xd0, 0x97, 0x50, 0x5b, 0xea, 0x88, 0x18, 0x30, 0xbf, 0x3c, 0xa1, 0xb4, 0xc7, 0xb1, 0x81, 0xcb, 0xd0, 0x67, 0x50, 0x5b, 0x71, 0x84, 0x8f,
0xab, 0x42, 0xa9, 0x92, 0x2a, 0x89, 0x40, 0x9f, 0x82, 0x16, 0xce, 0xd9, 0x65, 0xe8, 0x06, 0x97, 0x55, 0x41, 0xaa, 0xa4, 0x24, 0xe1, 0xe8, 0x01, 0x68, 0xe1, 0x82, 0x5d, 0x85, 0x5e, 0x70, 0x45,
0x64, 0x34, 0x75, 0x02, 0xe2, 0x8e, 0xeb, 0xc5, 0xa6, 0xd2, 0x52, 0x8f, 0x72, 0xcf, 0x14, 0x5c, 0x46, 0x53, 0x37, 0x20, 0xde, 0x58, 0x2f, 0x36, 0x95, 0x96, 0x7a, 0xac, 0xea, 0xca, 0x53, 0x05,
0x4b, 0x65, 0x9d, 0xa9, 0x13, 0x98, 0x63, 0xf4, 0x08, 0xb6, 0x3c, 0x27, 0x66, 0x64, 0x1a, 0xce, 0xd7, 0x52, 0x69, 0x67, 0xea, 0x06, 0xd6, 0x18, 0x3d, 0x81, 0xdd, 0xdb, 0xfc, 0x58, 0xaf, 0x37,
0xc8, 0x6c, 0x7e, 0x71, 0x45, 0xaf, 0xeb, 0x35, 0xb1, 0x33, 0x55, 0x0e, 0x9f, 0x84, 0xb3, 0xbe, 0xf3, 0x2d, 0x15, 0xef, 0xac, 0x53, 0x63, 0xf4, 0x18, 0x76, 0x7c, 0x37, 0x66, 0x64, 0x1a, 0xce,
0x00, 0xd1, 0x43, 0x00, 0xb1, 0x2b, 0x62, 0xf1, 0x7a, 0x49, 0xc4, 0x50, 0xe2, 0x88, 0x58, 0x18, 0xc9, 0x7c, 0x71, 0x79, 0x4d, 0x6f, 0xf4, 0x9a, 0xd8, 0xc7, 0x2a, 0x87, 0x4f, 0xc3, 0x79, 0x5f,
0x7d, 0x03, 0x65, 0x91, 0x4d, 0x32, 0x75, 0x03, 0x16, 0xd7, 0xa1, 0x99, 0x6f, 0x95, 0x0f, 0xb5, 0x80, 0xe8, 0x11, 0x80, 0xd8, 0x43, 0x61, 0xaa, 0x5e, 0x12, 0x1e, 0x97, 0x38, 0x22, 0xcc, 0x44,
0x7d, 0x2f, 0xe0, 0x89, 0xc5, 0x5c, 0x72, 0xe2, 0x06, 0x0c, 0x43, 0x94, 0x7e, 0xc6, 0x68, 0x0c, 0x5f, 0x43, 0x59, 0xc4, 0x9e, 0x4c, 0xbd, 0x80, 0xc5, 0x3a, 0x34, 0xf3, 0xad, 0xf2, 0x91, 0x76,
0x3b, 0x3c, 0x8b, 0x64, 0x34, 0x8f, 0x59, 0xe8, 0x93, 0x88, 0x8e, 0xc2, 0x68, 0x1c, 0xd7, 0xcb, 0xe0, 0x07, 0x3c, 0x0d, 0x30, 0x97, 0x9c, 0x7a, 0x01, 0xc3, 0x10, 0xa5, 0x9f, 0x31, 0x1a, 0x43,
0x62, 0xea, 0xcf, 0xf6, 0x17, 0xe4, 0xd8, 0xbf, 0xcd, 0x86, 0xfd, 0x2e, 0x8d, 0x59, 0x47, 0xcc, 0x9d, 0xc7, 0x9c, 0x8c, 0x16, 0x31, 0x0b, 0x67, 0x24, 0xa2, 0xa3, 0x30, 0x1a, 0xc7, 0x7a, 0x59,
0xc3, 0xc9, 0x34, 0x23, 0x60, 0xd1, 0x35, 0xde, 0x1e, 0xdf, 0xc4, 0xd1, 0x53, 0x40, 0x8e, 0xe7, 0x4c, 0xfd, 0xd9, 0xc1, 0x32, 0x95, 0x0e, 0xee, 0xe6, 0xce, 0x41, 0x97, 0xc6, 0xac, 0x23, 0xe6,
0x85, 0xef, 0x49, 0x4c, 0xbd, 0x09, 0x91, 0xd9, 0xa9, 0x6f, 0x35, 0x95, 0x56, 0x11, 0x6b, 0x42, 0xe1, 0x64, 0x9a, 0x19, 0xb0, 0xe8, 0x06, 0xef, 0x8e, 0x6f, 0xe3, 0xe8, 0x4b, 0x40, 0xae, 0xef,
0x32, 0xa0, 0xde, 0x44, 0x9a, 0x47, 0x3f, 0x87, 0xaa, 0xf0, 0x69, 0x42, 0x1d, 0x36, 0x8f, 0x68, 0x87, 0x6f, 0x49, 0x4c, 0xfd, 0x09, 0x91, 0xb1, 0xd4, 0x77, 0x9a, 0x4a, 0xab, 0x88, 0x35, 0x21,
0x5c, 0xd7, 0x9a, 0xf9, 0x56, 0xed, 0x70, 0x5b, 0x06, 0x72, 0x9c, 0xc0, 0x47, 0x2e, 0xc3, 0x15, 0x19, 0x50, 0x7f, 0x22, 0xd5, 0xa3, 0x9f, 0x43, 0x55, 0xd8, 0x34, 0xa1, 0x2e, 0x5b, 0x44, 0x34,
0xae, 0x27, 0xc7, 0x31, 0xda, 0x83, 0x92, 0xef, 0xfc, 0x48, 0x66, 0x4e, 0xc4, 0xe2, 0xfa, 0x76, 0xd6, 0xb5, 0x66, 0xbe, 0x55, 0x3b, 0xda, 0x95, 0x8e, 0x9c, 0x24, 0xf0, 0xb1, 0xc7, 0x70, 0x85,
0x53, 0x69, 0x55, 0x71, 0xd1, 0x77, 0x7e, 0xec, 0xf3, 0x31, 0xda, 0x87, 0x9d, 0x20, 0x24, 0x6e, 0xf3, 0xe4, 0x38, 0x46, 0xfb, 0x50, 0x9a, 0xb9, 0x3f, 0x92, 0xb9, 0x1b, 0xb1, 0x58, 0xdf, 0x6d,
0x30, 0xf1, 0xdc, 0xcb, 0x29, 0x23, 0xf3, 0xd9, 0xd8, 0x61, 0x34, 0xae, 0x23, 0xe1, 0xc3, 0x76, 0x2a, 0xad, 0x2a, 0x2e, 0xce, 0xdc, 0x1f, 0xfb, 0x7c, 0x8c, 0x0e, 0xa0, 0x1e, 0x84, 0xc4, 0x0b,
0x10, 0x9a, 0x52, 0x72, 0x96, 0x08, 0x1a, 0x5d, 0xd8, 0x5d, 0x1f, 0x1f, 0x27, 0x3c, 0x4f, 0x10, 0x26, 0xbe, 0x77, 0x35, 0x65, 0x64, 0x31, 0x1f, 0xbb, 0x8c, 0xc6, 0x3a, 0x12, 0x36, 0xec, 0x06,
0xaf, 0x01, 0x15, 0xf3, 0x4f, 0x74, 0x0f, 0x0a, 0xef, 0x1c, 0x6f, 0x4e, 0x45, 0x11, 0x54, 0x70, 0xa1, 0x25, 0x25, 0xe7, 0x89, 0xa0, 0xd1, 0x85, 0xbd, 0xcd, 0xfe, 0xf1, 0xf2, 0xe0, 0x01, 0xe2,
0x32, 0xf8, 0x65, 0xee, 0x5b, 0x45, 0x9f, 0xc2, 0xce, 0x30, 0x72, 0x46, 0x57, 0x37, 0xea, 0xe8, 0x15, 0xa3, 0x62, 0xfe, 0x89, 0x1e, 0x40, 0xe1, 0x8d, 0xeb, 0x2f, 0xa8, 0x28, 0x99, 0x0a, 0x4e,
0x66, 0x19, 0x28, 0xb7, 0xcb, 0xe0, 0x0e, 0x7f, 0x73, 0x77, 0xf8, 0xab, 0x7f, 0x0f, 0x5b, 0x22, 0x06, 0xbf, 0xcc, 0x7d, 0xa3, 0x18, 0x53, 0xa8, 0x0f, 0x23, 0x77, 0x74, 0x7d, 0xab, 0xea, 0x6e,
0xc3, 0xc7, 0x94, 0x7e, 0xa8, 0x5a, 0xef, 0x03, 0xaf, 0x45, 0xc1, 0xed, 0xa4, 0x62, 0x37, 0x1c, 0x17, 0x8d, 0x72, 0xb7, 0x68, 0xee, 0xb1, 0x37, 0x77, 0x8f, 0xbd, 0xc6, 0x77, 0xb0, 0x23, 0x22,
0x9f, 0xd3, 0x5a, 0x1f, 0x83, 0xb6, 0x9c, 0x1f, 0xcf, 0xc2, 0x20, 0xa6, 0xbc, 0x14, 0x39, 0x01, 0x7c, 0x42, 0xe9, 0xbb, 0x6a, 0xfb, 0x21, 0xf0, 0xca, 0x15, 0x95, 0x90, 0xd4, 0xf7, 0x96, 0x3b,
0x38, 0x87, 0x39, 0xe5, 0x05, 0xd9, 0x15, 0x31, 0xab, 0x26, 0xf1, 0x63, 0x4a, 0x05, 0xdd, 0x1f, 0xe3, 0x45, 0x60, 0x8c, 0x41, 0x5b, 0xcd, 0x8f, 0xe7, 0x61, 0x10, 0x53, 0x5e, 0xb8, 0x3c, 0x01,
0x25, 0x15, 0x46, 0xbc, 0x70, 0x74, 0xc5, 0x6b, 0xd6, 0xb9, 0x96, 0xe6, 0xab, 0x1c, 0xee, 0x85, 0x78, 0x06, 0xf3, 0x02, 0x11, 0xa5, 0xa1, 0x88, 0x59, 0x35, 0x89, 0x9f, 0x50, 0x2a, 0x8a, 0xe3,
0xa3, 0xab, 0x2e, 0x07, 0xf5, 0xb7, 0xc9, 0xb1, 0x32, 0x0c, 0xc5, 0x5a, 0xff, 0xc3, 0x76, 0xe8, 0x71, 0x52, 0x8f, 0xc4, 0x0f, 0x47, 0xd7, 0xbc, 0xc2, 0xdd, 0x1b, 0xa9, 0xbe, 0xca, 0xe1, 0x5e,
0x50, 0x10, 0x5c, 0x14, 0x66, 0xcb, 0x87, 0x95, 0x2c, 0xa9, 0x71, 0x22, 0xd2, 0xdf, 0xc2, 0xce, 0x38, 0xba, 0xee, 0x72, 0xd0, 0x78, 0x9d, 0x1c, 0x42, 0xc3, 0x50, 0xac, 0xf5, 0x3f, 0x6c, 0x87,
0x8a, 0x71, 0x19, 0x45, 0x03, 0x8a, 0xb3, 0x88, 0xba, 0xbe, 0x73, 0x49, 0xa5, 0xe5, 0xc5, 0x18, 0x01, 0x05, 0x91, 0x8b, 0x42, 0x6d, 0xf9, 0xa8, 0x92, 0x4d, 0x6a, 0x9c, 0x88, 0x8c, 0xd7, 0x50,
0xb5, 0x60, 0x73, 0xe2, 0xb8, 0xde, 0x3c, 0x4a, 0x0d, 0xd7, 0x52, 0x92, 0x25, 0x28, 0x4e, 0xc5, 0x5f, 0x53, 0x2e, 0xbd, 0x68, 0x40, 0x71, 0x1e, 0x51, 0x6f, 0xe6, 0x5e, 0x51, 0xa9, 0x79, 0x39,
0xfa, 0x03, 0x68, 0x60, 0x1a, 0x53, 0x76, 0xea, 0xc6, 0xb1, 0x1b, 0x06, 0x9d, 0x30, 0x60, 0x51, 0x46, 0x2d, 0xd8, 0x9e, 0xb8, 0x9e, 0xbf, 0x88, 0x52, 0xc5, 0xb5, 0x34, 0xc9, 0x12, 0x14, 0xa7,
0xe8, 0xc9, 0x08, 0xf4, 0x87, 0xb0, 0xb7, 0x56, 0x9a, 0xb8, 0xc0, 0x27, 0xff, 0x30, 0xa7, 0xd1, 0x62, 0xe3, 0x23, 0x68, 0x60, 0x1a, 0x53, 0x76, 0xe6, 0xc5, 0xb1, 0x17, 0x06, 0x9d, 0x30, 0x60,
0xf5, 0xfa, 0xc9, 0x3f, 0xc0, 0xde, 0x5a, 0xa9, 0xf4, 0xff, 0x29, 0x14, 0x66, 0x8e, 0x1b, 0xf1, 0x51, 0xe8, 0x4b, 0x0f, 0x8c, 0x47, 0xb0, 0xbf, 0x51, 0x9a, 0x98, 0xc0, 0x27, 0xff, 0xb0, 0xa0,
0xdc, 0xf3, 0xa2, 0xdc, 0xcd, 0x14, 0x65, 0xdf, 0x71, 0xa3, 0x13, 0x37, 0x66, 0x61, 0x74, 0x8d, 0xd1, 0xcd, 0xe6, 0xc9, 0x3f, 0xc0, 0xfe, 0x46, 0xa9, 0xb4, 0xff, 0x4b, 0x28, 0xcc, 0x5d, 0x2f,
0x13, 0xa5, 0x17, 0x6a, 0x51, 0xd1, 0x72, 0xfa, 0x9f, 0x15, 0x28, 0x67, 0x84, 0xbc, 0x34, 0x82, 0xe2, 0xb1, 0xe7, 0x45, 0xb9, 0x97, 0x29, 0xca, 0xbe, 0xeb, 0x45, 0xa7, 0x5e, 0xcc, 0xc2, 0xe8,
0x70, 0x4c, 0xc9, 0x24, 0x0a, 0xfd, 0x74, 0x13, 0x38, 0x70, 0x1c, 0x85, 0x3e, 0xe7, 0x84, 0x10, 0x06, 0x27, 0xa4, 0x67, 0x6a, 0x51, 0xd1, 0x72, 0xc6, 0x9f, 0x15, 0x28, 0x67, 0x84, 0xbc, 0x34,
0xb2, 0x50, 0x12, 0x78, 0x83, 0x0f, 0x87, 0x21, 0xfa, 0x1a, 0x36, 0xa7, 0x89, 0x01, 0x71, 0x10, 0x82, 0x70, 0x4c, 0xc9, 0x24, 0x0a, 0x67, 0xe9, 0x26, 0x70, 0xe0, 0x24, 0x0a, 0x67, 0x3c, 0x27,
0x96, 0x0f, 0x77, 0x6e, 0xac, 0xdd, 0x75, 0x98, 0x83, 0x53, 0x9d, 0x17, 0x6a, 0x31, 0xaf, 0xa9, 0x84, 0x90, 0x85, 0x32, 0x81, 0xb7, 0xf8, 0x70, 0x18, 0xa2, 0xaf, 0x60, 0x7b, 0x9a, 0x28, 0x10,
0x2f, 0xd4, 0xa2, 0xaa, 0x15, 0x5e, 0xa8, 0xc5, 0x82, 0xb6, 0xf1, 0x42, 0x2d, 0x6e, 0x68, 0x9b, 0xc7, 0x66, 0xf9, 0xa8, 0x7e, 0x6b, 0xed, 0xae, 0xcb, 0x5c, 0x9c, 0x72, 0x9e, 0xa9, 0xc5, 0xbc,
0xfa, 0xbf, 0x14, 0x28, 0xa6, 0xda, 0xdc, 0x13, 0xbe, 0xa5, 0x84, 0xf3, 0x42, 0x92, 0xa9, 0xc8, 0xa6, 0x3e, 0x53, 0x8b, 0xaa, 0x56, 0x78, 0xa6, 0x16, 0x0b, 0xda, 0xd6, 0x33, 0xb5, 0xb8, 0xa5,
0x81, 0xa1, 0xeb, 0x53, 0xd4, 0x84, 0x8a, 0x10, 0xae, 0x52, 0x14, 0x38, 0xd6, 0x16, 0x34, 0x15, 0x6d, 0x1b, 0xff, 0x54, 0xa0, 0x98, 0xb2, 0xb9, 0x25, 0x7c, 0x4b, 0x09, 0xcf, 0x0b, 0x99, 0x4c,
0x27, 0x74, 0xaa, 0x21, 0xf8, 0xa8, 0xca, 0x13, 0x3a, 0x51, 0x49, 0x9b, 0x4c, 0x3c, 0x1f, 0x8d, 0x45, 0x0e, 0x0c, 0xbd, 0x19, 0x45, 0x4d, 0xa8, 0x08, 0xe1, 0x7a, 0x8a, 0x02, 0xc7, 0xda, 0x22,
0x68, 0x1c, 0x27, 0xab, 0x14, 0x12, 0x15, 0x89, 0x89, 0x85, 0x1e, 0xc1, 0x56, 0xaa, 0x92, 0xae, 0x4d, 0xc5, 0x79, 0x9e, 0x32, 0x44, 0x3e, 0xaa, 0xf2, 0x3c, 0x4f, 0x28, 0x69, 0x4b, 0x8a, 0x17,
0xb5, 0x91, 0xf0, 0x55, 0xc2, 0x72, 0xb9, 0x16, 0x68, 0x59, 0x3d, 0x7f, 0xd9, 0x13, 0x6a, 0x4b, 0xa3, 0x11, 0x8d, 0xe3, 0x64, 0x95, 0x42, 0x42, 0x91, 0x98, 0x58, 0xe8, 0x31, 0xec, 0xa4, 0x94,
0x45, 0xbe, 0x68, 0x12, 0xbc, 0xfe, 0x7b, 0xb8, 0x2f, 0x52, 0xd9, 0x8f, 0xc2, 0x0b, 0xe7, 0xc2, 0x74, 0xad, 0xad, 0x24, 0x5f, 0x25, 0x2c, 0x97, 0x6b, 0x81, 0x96, 0xe5, 0xcd, 0x56, 0x1d, 0xa4,
0xf5, 0x5c, 0x76, 0x9d, 0x92, 0x9c, 0x07, 0x1e, 0x85, 0x3e, 0xe1, 0x7b, 0x9b, 0xa6, 0x80, 0x03, 0xb6, 0x22, 0xf2, 0x45, 0x13, 0xe7, 0x8d, 0xdf, 0xc3, 0x43, 0x11, 0xca, 0x7e, 0x14, 0x5e, 0xba,
0x56, 0x38, 0xa6, 0x3c, 0x05, 0x2c, 0x4c, 0x44, 0x32, 0x05, 0x2c, 0x14, 0x82, 0x6c, 0x2f, 0xcd, 0x97, 0x9e, 0xef, 0xb1, 0x9b, 0x34, 0xc9, 0xb9, 0xe3, 0x51, 0x38, 0x23, 0x7c, 0x6f, 0xd3, 0x10,
0xaf, 0xf4, 0x52, 0xfd, 0x0a, 0xea, 0xb7, 0xd7, 0x92, 0x9c, 0x69, 0x42, 0x79, 0xb6, 0x84, 0xc5, 0x70, 0xc0, 0x0e, 0xc7, 0x94, 0x87, 0x80, 0x85, 0x89, 0x48, 0x86, 0x80, 0x85, 0x42, 0x90, 0xed,
0x72, 0x0a, 0xce, 0x42, 0xd9, 0xdc, 0xe6, 0x3e, 0x9e, 0x5b, 0xfd, 0xaf, 0x0a, 0x6c, 0x1f, 0xcd, 0xbc, 0xf9, 0xb5, 0xce, 0x6b, 0x5c, 0x83, 0x7e, 0x77, 0x2d, 0x99, 0x33, 0x4d, 0x28, 0xcf, 0x57,
0x5d, 0x6f, 0xbc, 0x52, 0xb8, 0x59, 0xef, 0x94, 0xd5, 0x4e, 0xbf, 0xae, 0x8d, 0xe7, 0xd6, 0xb6, 0xb0, 0x58, 0x4e, 0xc1, 0x59, 0x28, 0x1b, 0xdb, 0xdc, 0xfb, 0x63, 0x6b, 0xfc, 0x55, 0x81, 0xdd,
0xf1, 0x75, 0xad, 0x32, 0x7f, 0x67, 0xab, 0xfc, 0x1c, 0xca, 0xcb, 0x2e, 0x19, 0xd7, 0xd5, 0x66, 0xe3, 0x85, 0xe7, 0x8f, 0xd7, 0x0a, 0x37, 0x6b, 0x9d, 0xb2, 0x7e, 0x2f, 0xd8, 0xd4, 0xf4, 0x73,
0xbe, 0x55, 0xc1, 0x30, 0x4d, 0x5b, 0x64, 0xac, 0x7f, 0x0b, 0x28, 0xeb, 0xa8, 0xdc, 0x90, 0xc5, 0x1b, 0x9b, 0xfe, 0x97, 0x1b, 0x1a, 0x6b, 0x5e, 0x34, 0xd6, 0xdc, 0x86, 0xb6, 0xfa, 0x09, 0x94,
0xf9, 0xa1, 0xdc, 0x7d, 0x7e, 0x3c, 0x80, 0xc6, 0x60, 0x7e, 0x11, 0x8f, 0x22, 0xf7, 0x82, 0x9e, 0x57, 0x5d, 0x32, 0xd6, 0xd5, 0x66, 0xbe, 0x55, 0xc1, 0x30, 0x4d, 0x5b, 0x64, 0x6c, 0x7c, 0x03,
0x30, 0x6f, 0x64, 0xbc, 0xa3, 0x01, 0x8b, 0xd3, 0x2a, 0xfd, 0xb7, 0x0a, 0xa5, 0x05, 0xca, 0x8f, 0x28, 0x6b, 0xa8, 0xdc, 0x90, 0xe5, 0xf9, 0xa1, 0xdc, 0x7f, 0x7e, 0x7c, 0x04, 0x8d, 0xc1, 0xe2,
0x67, 0x37, 0x18, 0x85, 0x7e, 0xea, 0x74, 0x40, 0x3d, 0xee, 0x77, 0xd2, 0x14, 0xb6, 0x53, 0x51, 0x32, 0x1e, 0x45, 0xde, 0x25, 0x3d, 0x65, 0xfe, 0xc8, 0x7c, 0x43, 0x03, 0x16, 0xa7, 0x55, 0xfa,
0x27, 0x91, 0x98, 0x63, 0xae, 0xbf, 0x12, 0xa4, 0xd4, 0xcf, 0x25, 0xfa, 0xd9, 0x18, 0x13, 0xfd, 0x6f, 0x15, 0x4a, 0x4b, 0x94, 0x1f, 0xcf, 0x5e, 0x30, 0x0a, 0x67, 0xa9, 0xd1, 0x01, 0xf5, 0xb9,
0x16, 0x68, 0x0b, 0xfb, 0x53, 0xe6, 0x8d, 0x16, 0x9b, 0x82, 0x6b, 0x29, 0xce, 0x9d, 0x49, 0x34, 0xdd, 0x49, 0x53, 0xd8, 0x4d, 0x45, 0x9d, 0x44, 0x62, 0x8d, 0x39, 0x7f, 0xcd, 0x49, 0xc9, 0xcf,
0x17, 0x96, 0x53, 0x4d, 0x35, 0xd1, 0x4c, 0x71, 0xa9, 0xf9, 0x05, 0x54, 0x78, 0x3d, 0xc4, 0xcc, 0x25, 0xfc, 0xac, 0x8f, 0x09, 0xbf, 0x05, 0xda, 0x52, 0xff, 0x94, 0xf9, 0xa3, 0xe5, 0xa6, 0xe0,
0xf1, 0x67, 0x24, 0x88, 0x45, 0x5d, 0xa8, 0xb8, 0xbc, 0xc0, 0xac, 0x18, 0x7d, 0x07, 0x40, 0x79, 0x5a, 0x8a, 0x73, 0x63, 0x12, 0xe6, 0x52, 0x73, 0xca, 0x54, 0x13, 0x66, 0x8a, 0x4b, 0xe6, 0xa7,
0x7c, 0x84, 0x5d, 0xcf, 0xa8, 0x28, 0x89, 0xda, 0xe1, 0x67, 0x19, 0x62, 0x2c, 0x36, 0x60, 0x5f, 0x50, 0xe1, 0xf5, 0x10, 0x33, 0x77, 0x36, 0x27, 0x41, 0x2c, 0xea, 0x42, 0xc5, 0xe5, 0x25, 0x66,
0xfc, 0x1d, 0x5e, 0xcf, 0x28, 0x2e, 0xd1, 0xf4, 0x13, 0x7d, 0x0f, 0xd5, 0x49, 0x18, 0xbd, 0x77, 0xc7, 0xe8, 0x5b, 0x00, 0xca, 0xfd, 0x23, 0xec, 0x66, 0x4e, 0x45, 0x49, 0xd4, 0x8e, 0x3e, 0xce,
0xa2, 0x31, 0x11, 0xa0, 0x3c, 0x36, 0xee, 0x67, 0x2c, 0x1c, 0x27, 0x72, 0x31, 0xfd, 0xe4, 0x13, 0x24, 0xc6, 0x72, 0x03, 0x0e, 0xc4, 0xdf, 0xe1, 0xcd, 0x9c, 0xe2, 0x12, 0x4d, 0x3f, 0xd1, 0x77,
0x5c, 0x99, 0x64, 0xc6, 0xe8, 0x25, 0xa0, 0x74, 0xbe, 0xa8, 0xf2, 0xc4, 0x48, 0x51, 0x18, 0xd9, 0x50, 0x9d, 0x84, 0xd1, 0x5b, 0x37, 0x1a, 0x13, 0x01, 0xca, 0x63, 0xe3, 0x61, 0x46, 0xc3, 0x49,
0xbb, 0x6d, 0x84, 0x1f, 0xd2, 0xa9, 0x21, 0x6d, 0x72, 0x03, 0x43, 0xbf, 0x82, 0x4a, 0x4c, 0x19, 0x22, 0x17, 0xd3, 0x4f, 0x3f, 0xc0, 0x95, 0x49, 0x66, 0x8c, 0x9e, 0x03, 0x4a, 0xe7, 0x8b, 0x2a,
0xf3, 0xa8, 0x34, 0x53, 0x12, 0x66, 0x76, 0x57, 0xee, 0x34, 0x5c, 0x9c, 0x5a, 0x28, 0xc7, 0xcb, 0x4f, 0x94, 0x14, 0x85, 0x92, 0xfd, 0xbb, 0x4a, 0xf8, 0x21, 0x9d, 0x2a, 0xd2, 0x26, 0xb7, 0x30,
0x21, 0x3a, 0x82, 0x2d, 0xcf, 0x0d, 0xae, 0xb2, 0x6e, 0x80, 0x98, 0x5f, 0xcf, 0xcc, 0xef, 0xb9, 0xf4, 0x2b, 0xa8, 0xc4, 0x94, 0x31, 0x9f, 0x4a, 0x35, 0x25, 0xa1, 0x66, 0x6f, 0xed, 0x4e, 0xc3,
0xc1, 0x55, 0xd6, 0x87, 0xaa, 0x97, 0x05, 0xf4, 0x5f, 0x43, 0x69, 0xb1, 0x4b, 0xa8, 0x0c, 0x9b, 0xc5, 0xa9, 0x86, 0x72, 0xbc, 0x1a, 0xa2, 0x63, 0xd8, 0xf1, 0xbd, 0xe0, 0x3a, 0x6b, 0x06, 0x88,
0x67, 0xd6, 0x4b, 0xcb, 0x7e, 0x65, 0x69, 0x9f, 0xa0, 0x22, 0xa8, 0x03, 0xc3, 0xea, 0x6a, 0x0a, 0xf9, 0x7a, 0x66, 0x7e, 0xcf, 0x0b, 0xae, 0xb3, 0x36, 0x54, 0xfd, 0x2c, 0x60, 0xfc, 0x1a, 0x4a,
0x87, 0xb1, 0xd1, 0x31, 0xcc, 0x73, 0x43, 0xcb, 0xf1, 0xc1, 0xb1, 0x8d, 0x5f, 0xb5, 0x71, 0x57, 0xcb, 0x5d, 0x42, 0x65, 0xd8, 0x3e, 0xb7, 0x9f, 0xdb, 0xce, 0x0b, 0x5b, 0xfb, 0x00, 0x15, 0x41,
0xcb, 0x1f, 0x6d, 0x42, 0x41, 0xac, 0xab, 0xff, 0x5d, 0x81, 0xa2, 0xc8, 0x60, 0x30, 0x09, 0xd1, 0x1d, 0x98, 0x76, 0x57, 0x53, 0x38, 0x8c, 0xcd, 0x8e, 0x69, 0x5d, 0x98, 0x5a, 0x8e, 0x0f, 0x4e,
0x4f, 0x60, 0x41, 0x2e, 0x71, 0xb8, 0xf1, 0x86, 0x2b, 0x58, 0x57, 0xc5, 0x0b, 0xc2, 0x0c, 0x25, 0x1c, 0xfc, 0xa2, 0x8d, 0xbb, 0x5a, 0xfe, 0x78, 0x1b, 0x0a, 0x62, 0x5d, 0xe3, 0xef, 0x0a, 0x14,
0xce, 0x95, 0x17, 0xd4, 0x58, 0x28, 0xe7, 0x12, 0xe5, 0x54, 0xb0, 0x50, 0x7e, 0x92, 0xb1, 0xbc, 0x45, 0x04, 0x83, 0x49, 0x88, 0x7e, 0x02, 0xcb, 0xe4, 0x12, 0x87, 0x1b, 0x6f, 0xb8, 0x22, 0xeb,
0x72, 0xe4, 0xa8, 0x78, 0x2b, 0x15, 0xa4, 0x27, 0xec, 0x93, 0x8c, 0xe1, 0x95, 0x93, 0x58, 0xc5, 0xaa, 0x78, 0x99, 0x30, 0x43, 0x89, 0x73, 0xf2, 0x32, 0x35, 0x96, 0xe4, 0x5c, 0x42, 0x4e, 0x05,
0x5b, 0xa9, 0x40, 0xea, 0xea, 0xbf, 0x80, 0x4a, 0x36, 0xe7, 0xe8, 0x31, 0xa8, 0x6e, 0x30, 0x09, 0x4b, 0xf2, 0x93, 0x8c, 0xe6, 0xb5, 0x23, 0x47, 0xc5, 0x3b, 0xa9, 0x20, 0x3d, 0x61, 0xb3, 0x77,
0x65, 0x21, 0xee, 0xdc, 0x20, 0x17, 0x0f, 0x12, 0x0b, 0x05, 0x1d, 0x81, 0x76, 0x33, 0xcf, 0x7a, 0xdb, 0xb5, 0x93, 0x38, 0x73, 0xb7, 0x95, 0x5c, 0xe3, 0x17, 0x50, 0xc9, 0xc6, 0x1c, 0x7d, 0x01,
0x15, 0xca, 0x99, 0xa4, 0xe9, 0xff, 0x54, 0xa0, 0xba, 0x92, 0x84, 0xff, 0xda, 0x3a, 0xfa, 0x0e, 0xaa, 0x17, 0x4c, 0x42, 0x59, 0x88, 0xf5, 0x5b, 0xc9, 0xc5, 0x9d, 0xc4, 0x82, 0x60, 0x20, 0xd0,
0x2a, 0xef, 0xdd, 0x88, 0x92, 0x6c, 0xfb, 0xaf, 0x1d, 0x36, 0x56, 0xdb, 0x7f, 0xfa, 0xbf, 0x13, 0x6e, 0xc7, 0xd9, 0xa8, 0x42, 0x39, 0x13, 0x34, 0xe3, 0x1f, 0x0a, 0x54, 0xd7, 0x82, 0xf0, 0x5f,
0x8e, 0x29, 0x2e, 0x73, 0x7d, 0x09, 0xa0, 0xdf, 0x40, 0x4d, 0xce, 0x24, 0x63, 0xca, 0x1c, 0xd7, 0x6b, 0x47, 0xdf, 0x42, 0xe5, 0xad, 0x17, 0x51, 0x92, 0x6d, 0xff, 0xb5, 0xa3, 0xc6, 0x7a, 0xfb,
0x13, 0x5b, 0x55, 0x5b, 0xa1, 0x87, 0xd4, 0xed, 0x0a, 0x39, 0xae, 0x4e, 0xb2, 0x43, 0xf4, 0xd5, 0x4f, 0xff, 0x77, 0xc2, 0x31, 0xc5, 0x65, 0xce, 0x97, 0x00, 0xfa, 0x0d, 0xd4, 0xe4, 0x4c, 0x32,
0xd2, 0x40, 0xcc, 0x22, 0x37, 0xb8, 0x14, 0xfb, 0x57, 0x5a, 0xa8, 0x0d, 0x04, 0xc8, 0x1b, 0x79, 0xa6, 0xcc, 0xf5, 0x7c, 0xb1, 0x55, 0xb5, 0xb5, 0xf4, 0x90, 0xdc, 0xae, 0x90, 0xe3, 0xea, 0x24,
0x55, 0x5e, 0x1e, 0x07, 0xcc, 0x61, 0xf3, 0x18, 0x7d, 0x0d, 0x85, 0x98, 0x39, 0xf2, 0x24, 0xab, 0x3b, 0x44, 0x9f, 0xaf, 0x14, 0xc4, 0x2c, 0xf2, 0x82, 0x2b, 0xb1, 0x7f, 0xa5, 0x25, 0x6d, 0x20,
0xad, 0xd4, 0x56, 0x46, 0x91, 0xe2, 0x44, 0x6b, 0xe5, 0xf6, 0x93, 0xbb, 0x75, 0xfb, 0x29, 0xf0, 0x40, 0xde, 0xc8, 0xab, 0xf2, 0xf2, 0x38, 0x60, 0x2e, 0x5b, 0xc4, 0xe8, 0x2b, 0x28, 0xc4, 0xcc,
0x13, 0x23, 0x39, 0x45, 0xcb, 0x87, 0x48, 0x06, 0x7f, 0x32, 0xec, 0x75, 0xda, 0x8c, 0x51, 0x7f, 0x95, 0x27, 0x59, 0x6d, 0xad, 0xb6, 0x32, 0x44, 0x8a, 0x13, 0xd6, 0xda, 0xed, 0x27, 0x77, 0xe7,
0xc6, 0x70, 0xa2, 0x90, 0x74, 0xb7, 0x27, 0x7f, 0x54, 0xa1, 0xba, 0x12, 0xd4, 0x2a, 0xab, 0xab, 0xf6, 0x53, 0xe0, 0x27, 0x46, 0x72, 0x8a, 0x96, 0x8f, 0x90, 0x74, 0xfe, 0x74, 0xd8, 0xeb, 0xb4,
0x50, 0xb2, 0x6c, 0xd2, 0x35, 0x86, 0x6d, 0xb3, 0xa7, 0x29, 0x48, 0x83, 0x8a, 0x6d, 0x99, 0xb6, 0x19, 0xa3, 0xb3, 0x39, 0xc3, 0x09, 0x21, 0xe9, 0x6e, 0x4f, 0xfe, 0xa8, 0x42, 0x75, 0xcd, 0xa9,
0x45, 0xba, 0x46, 0xc7, 0xee, 0x72, 0x7e, 0x7f, 0x0a, 0xdb, 0x3d, 0xd3, 0x7a, 0x49, 0x2c, 0x7b, 0xf5, 0xac, 0xae, 0x42, 0xc9, 0x76, 0x48, 0xd7, 0x1c, 0xb6, 0xad, 0x9e, 0xa6, 0x20, 0x0d, 0x2a,
0x48, 0x8c, 0x9e, 0xf9, 0xdc, 0x3c, 0xea, 0x19, 0x5a, 0x1e, 0xdd, 0x03, 0xcd, 0xb6, 0x48, 0xe7, 0x8e, 0x6d, 0x39, 0x36, 0xe9, 0x9a, 0x1d, 0xa7, 0xcb, 0xf3, 0xfb, 0x43, 0xd8, 0xed, 0x59, 0xf6,
0xa4, 0x6d, 0x5a, 0x64, 0x68, 0x9e, 0x1a, 0xf6, 0xd9, 0x50, 0x53, 0x39, 0xca, 0x1d, 0x21, 0xc6, 0x73, 0x62, 0x3b, 0x43, 0x62, 0xf6, 0xac, 0xef, 0xad, 0xe3, 0x9e, 0xa9, 0xe5, 0xd1, 0x03, 0xd0,
0xeb, 0x8e, 0x61, 0x74, 0x07, 0xe4, 0xb4, 0xfd, 0x5a, 0x2b, 0xa0, 0x3a, 0xdc, 0x33, 0xad, 0xc1, 0x1c, 0x9b, 0x74, 0x4e, 0xdb, 0x96, 0x4d, 0x86, 0xd6, 0x99, 0xe9, 0x9c, 0x0f, 0x35, 0x95, 0xa3,
0xd9, 0xf1, 0xb1, 0xd9, 0x31, 0x0d, 0x6b, 0x48, 0x8e, 0xda, 0xbd, 0xb6, 0xd5, 0x31, 0xb4, 0x0d, 0xdc, 0x10, 0x62, 0xbe, 0xec, 0x98, 0x66, 0x77, 0x40, 0xce, 0xda, 0x2f, 0xb5, 0x02, 0xd2, 0xe1,
0xb4, 0x0b, 0xc8, 0xb4, 0x3a, 0xf6, 0x69, 0xbf, 0x67, 0x0c, 0x0d, 0x92, 0xd6, 0xd1, 0x26, 0xda, 0x81, 0x65, 0x0f, 0xce, 0x4f, 0x4e, 0xac, 0x8e, 0x65, 0xda, 0x43, 0x72, 0xdc, 0xee, 0xb5, 0xed,
0x81, 0x2d, 0x61, 0xa7, 0xdd, 0xed, 0x92, 0xe3, 0xb6, 0xd9, 0x33, 0xba, 0x5a, 0x91, 0x7b, 0x22, 0x8e, 0xa9, 0x6d, 0xa1, 0x3d, 0x40, 0x96, 0xdd, 0x71, 0xce, 0xfa, 0x3d, 0x73, 0x68, 0x92, 0xb4,
0x35, 0x06, 0xa4, 0x6b, 0x0e, 0xda, 0x47, 0x1c, 0x2e, 0xf1, 0x35, 0x4d, 0xeb, 0xdc, 0x36, 0x3b, 0x8e, 0xb6, 0x51, 0x1d, 0x76, 0x84, 0x9e, 0x76, 0xb7, 0x4b, 0x4e, 0xda, 0x56, 0xcf, 0xec, 0x6a,
0x06, 0xe9, 0x70, 0xb3, 0x1c, 0x05, 0xae, 0x9c, 0xa2, 0x67, 0x56, 0xd7, 0xc0, 0xfd, 0xb6, 0xd9, 0x45, 0x6e, 0x89, 0x64, 0x0c, 0x48, 0xd7, 0x1a, 0xb4, 0x8f, 0x39, 0x5c, 0xe2, 0x6b, 0x5a, 0xf6,
0xd5, 0xca, 0x68, 0x0f, 0xee, 0xa7, 0xb0, 0xf1, 0xba, 0x6f, 0xe2, 0x37, 0x64, 0x68, 0xdb, 0x64, 0x85, 0x63, 0x75, 0x4c, 0xd2, 0xe1, 0x6a, 0x39, 0x0a, 0x9c, 0x9c, 0xa2, 0xe7, 0x76, 0xd7, 0xc4,
0x60, 0xdb, 0x96, 0x56, 0xc9, 0x5a, 0xe2, 0xd1, 0xda, 0x7d, 0xc3, 0xd2, 0xaa, 0xe8, 0x3e, 0xec, 0xfd, 0xb6, 0xd5, 0xd5, 0xca, 0x68, 0x1f, 0x1e, 0xa6, 0xb0, 0xf9, 0xb2, 0x6f, 0xe1, 0x57, 0x64,
0x9c, 0xf6, 0xfb, 0x24, 0x95, 0xa4, 0xc1, 0xd6, 0xb8, 0x7a, 0xbb, 0xdb, 0xc5, 0xc6, 0x60, 0x40, 0xe8, 0x38, 0x64, 0xe0, 0x38, 0xb6, 0x56, 0xc9, 0x6a, 0xe2, 0xde, 0x3a, 0x7d, 0xd3, 0xd6, 0xaa,
0x4e, 0xcd, 0xc1, 0x69, 0x7b, 0xd8, 0x39, 0xd1, 0xb6, 0x78, 0x48, 0x03, 0x63, 0x48, 0x86, 0xf6, 0xe8, 0x21, 0xd4, 0xcf, 0xfa, 0x7d, 0x92, 0x4a, 0x52, 0x67, 0x6b, 0x9c, 0xde, 0xee, 0x76, 0xb1,
0xb0, 0xdd, 0x5b, 0xe2, 0x1a, 0x77, 0x68, 0x89, 0xf3, 0x45, 0x7b, 0xf6, 0x2b, 0x6d, 0x9b, 0x6f, 0x39, 0x18, 0x90, 0x33, 0x6b, 0x70, 0xd6, 0x1e, 0x76, 0x4e, 0xb5, 0x1d, 0xee, 0xd2, 0xc0, 0x1c,
0x38, 0x87, 0xed, 0x73, 0xe9, 0x22, 0xe2, 0xb1, 0xcb, 0xf4, 0xa4, 0x6b, 0x6a, 0x3b, 0x1c, 0x34, 0x92, 0xa1, 0x33, 0x6c, 0xf7, 0x56, 0xb8, 0xc6, 0x0d, 0x5a, 0xe1, 0x7c, 0xd1, 0x9e, 0xf3, 0x42,
0xad, 0xf3, 0x76, 0xcf, 0xec, 0x92, 0x97, 0xc6, 0x1b, 0x71, 0x0e, 0xdd, 0xe3, 0x60, 0xe2, 0x19, 0xdb, 0xe5, 0x1b, 0xce, 0x61, 0xe7, 0x42, 0x9a, 0x88, 0xb8, 0xef, 0x32, 0x3c, 0xe9, 0x9a, 0x5a,
0xe9, 0x63, 0xfb, 0x39, 0x77, 0x44, 0xfb, 0x14, 0x21, 0xa8, 0x75, 0x4c, 0xdc, 0x39, 0xeb, 0xb5, 0x9d, 0x83, 0x96, 0x7d, 0xd1, 0xee, 0x59, 0x5d, 0xf2, 0xdc, 0x7c, 0x25, 0xce, 0xa1, 0x07, 0x1c,
0x31, 0xc1, 0xf6, 0xd9, 0xd0, 0xd0, 0x76, 0x9f, 0xfc, 0x4d, 0x81, 0x4a, 0x96, 0x67, 0x3c, 0xeb, 0x4c, 0x2c, 0x23, 0x7d, 0xec, 0x7c, 0xcf, 0x0d, 0xd1, 0x3e, 0x44, 0x08, 0x6a, 0x1d, 0x0b, 0x77,
0xa6, 0x45, 0x8e, 0x7b, 0xe6, 0xf3, 0x93, 0x61, 0x42, 0x82, 0xc1, 0x59, 0x87, 0xa7, 0xcc, 0xe0, 0xce, 0x7b, 0x6d, 0x4c, 0xb0, 0x73, 0x3e, 0x34, 0xb5, 0xbd, 0x27, 0x7f, 0x53, 0xa0, 0x92, 0xcd,
0xe7, 0x1b, 0x82, 0x5a, 0xb2, 0xe9, 0x8b, 0x60, 0x73, 0x7c, 0x2d, 0x89, 0x59, 0xb6, 0xb4, 0x9b, 0x33, 0x1e, 0x75, 0xcb, 0x26, 0x27, 0x3d, 0xeb, 0xfb, 0xd3, 0x61, 0x92, 0x04, 0x83, 0xf3, 0x0e,
0xe7, 0xce, 0x4b, 0xd0, 0xc0, 0xd8, 0xc6, 0x9a, 0x8a, 0xbe, 0x84, 0xa6, 0x44, 0x78, 0x5e, 0x31, 0x0f, 0x99, 0xc9, 0xcf, 0x37, 0x04, 0xb5, 0x64, 0xd3, 0x97, 0xce, 0xe6, 0xf8, 0x5a, 0x12, 0xb3,
0x36, 0x3a, 0x43, 0xd2, 0x6f, 0xbf, 0x39, 0xe5, 0x69, 0x4f, 0x48, 0x36, 0xd0, 0x0a, 0xe8, 0x73, 0x1d, 0xa9, 0x37, 0xcf, 0x8d, 0x97, 0xa0, 0x89, 0xb1, 0x83, 0x35, 0x15, 0x7d, 0x06, 0x4d, 0x89,
0xd8, 0x5b, 0x68, 0xad, 0xe3, 0xc5, 0xe1, 0x5f, 0x36, 0x61, 0x43, 0xb4, 0xf9, 0x08, 0xfd, 0x16, 0xf0, 0xb8, 0x62, 0x6c, 0x76, 0x86, 0xa4, 0xdf, 0x7e, 0x75, 0xc6, 0xc3, 0x9e, 0x24, 0xd9, 0x40,
0xaa, 0x99, 0x67, 0xec, 0xf9, 0x21, 0x7a, 0xf8, 0xc1, 0x07, 0x6e, 0x23, 0x7d, 0x0c, 0x48, 0xf8, 0x2b, 0xa0, 0x4f, 0x60, 0x7f, 0xc9, 0xda, 0x94, 0x17, 0x47, 0x7f, 0xd9, 0x86, 0x2d, 0xd1, 0xe6,
0x99, 0x82, 0x8e, 0xa0, 0x96, 0x7d, 0xcf, 0x9d, 0x1f, 0xa2, 0x6c, 0x77, 0x5c, 0xf3, 0xd4, 0x5b, 0x23, 0xf4, 0x5b, 0xa8, 0x66, 0x9e, 0xb1, 0x17, 0x47, 0xe8, 0xd1, 0x3b, 0x1f, 0xb8, 0x8d, 0xf4,
0x63, 0xe3, 0x25, 0x68, 0x46, 0xcc, 0x5c, 0x9f, 0x17, 0xa9, 0x7c, 0x71, 0xa1, 0x46, 0xc6, 0xca, 0x31, 0x20, 0xe1, 0xa7, 0x0a, 0x3a, 0x86, 0x5a, 0xf6, 0x3d, 0x77, 0x71, 0x84, 0xb2, 0xdd, 0x71,
0x8d, 0x67, 0x5c, 0x63, 0x6f, 0xad, 0x4c, 0xde, 0x6b, 0x7a, 0xfc, 0x40, 0x5c, 0xbc, 0x79, 0x6e, 0xc3, 0x53, 0x6f, 0x83, 0x8e, 0xe7, 0xa0, 0x99, 0x31, 0xf3, 0x66, 0xbc, 0x48, 0xe5, 0x8b, 0x0b,
0x05, 0xb4, 0xfa, 0xd0, 0x6a, 0x7c, 0x76, 0x97, 0x58, 0x5a, 0x1b, 0xc3, 0xce, 0x9a, 0x67, 0x0c, 0x35, 0x32, 0x5a, 0x6e, 0x3d, 0xe3, 0x1a, 0xfb, 0x1b, 0x65, 0xf2, 0x5e, 0xd3, 0xe3, 0x07, 0xe2,
0xfa, 0x2a, 0xeb, 0xc1, 0x9d, 0x8f, 0xa0, 0xc6, 0xa3, 0x8f, 0xa9, 0x2d, 0x57, 0x59, 0xf3, 0xde, 0xf2, 0xcd, 0x73, 0xc7, 0xa1, 0xf5, 0x87, 0x56, 0xe3, 0xe3, 0xfb, 0xc4, 0x52, 0xdb, 0x18, 0xea,
0x59, 0x59, 0xe5, 0xee, 0xd7, 0xd2, 0xca, 0x2a, 0x1f, 0x7a, 0x36, 0xbd, 0x05, 0xed, 0xe6, 0xf5, 0x1b, 0x9e, 0x31, 0xe8, 0xf3, 0xac, 0x05, 0xf7, 0x3e, 0x82, 0x1a, 0x8f, 0xdf, 0x47, 0x5b, 0xad,
0x18, 0xe9, 0x37, 0xe7, 0xde, 0xbe, 0xa7, 0x37, 0xfe, 0xff, 0x83, 0x3a, 0xd2, 0xb8, 0x09, 0xb0, 0xb2, 0xe1, 0xbd, 0xb3, 0xb6, 0xca, 0xfd, 0xaf, 0xa5, 0xb5, 0x55, 0xde, 0xf5, 0x6c, 0x7a, 0x0d,
0xbc, 0x64, 0xa2, 0x07, 0x99, 0x29, 0xb7, 0x2e, 0xc9, 0x8d, 0x87, 0x77, 0x48, 0xa5, 0xa9, 0x21, 0xda, 0xed, 0xeb, 0x31, 0x32, 0x6e, 0xcf, 0xbd, 0x7b, 0x4f, 0x6f, 0xfc, 0xff, 0x3b, 0x39, 0x52,
0xec, 0xac, 0xb9, 0x75, 0xae, 0xec, 0xc6, 0xdd, 0xb7, 0xd2, 0xc6, 0xbd, 0x75, 0x97, 0xb3, 0x67, 0xb9, 0x05, 0xb0, 0xba, 0x64, 0xa2, 0x8f, 0x32, 0x53, 0xee, 0x5c, 0x92, 0x1b, 0x8f, 0xee, 0x91,
0x0a, 0x3a, 0x4d, 0x78, 0x91, 0xfe, 0xa4, 0xf2, 0x11, 0xa2, 0xd7, 0xd7, 0x37, 0x91, 0x79, 0xac, 0x4a, 0x55, 0x43, 0xa8, 0x6f, 0xb8, 0x75, 0xae, 0xed, 0xc6, 0xfd, 0xb7, 0xd2, 0xc6, 0x83, 0x4d,
0xe7, 0xff, 0x94, 0x53, 0x9e, 0x29, 0xc8, 0x86, 0x4a, 0x96, 0xdc, 0x1f, 0x65, 0xfd, 0xc7, 0x0c, 0x97, 0xb3, 0xa7, 0x0a, 0x3a, 0x4b, 0xf2, 0x22, 0xfd, 0x49, 0xe5, 0x3d, 0x89, 0xae, 0x6f, 0x6e,
0x1e, 0x7d, 0xf3, 0xbb, 0x83, 0x4b, 0x97, 0x4d, 0xe7, 0x17, 0xfb, 0xa3, 0xd0, 0x3f, 0x10, 0xbf, 0x22, 0x8b, 0xd8, 0xc8, 0xff, 0x29, 0xa7, 0x3c, 0x55, 0x90, 0x03, 0x95, 0x6c, 0x72, 0xbf, 0x37,
0x64, 0x04, 0x6e, 0x70, 0x19, 0x50, 0xf6, 0x3e, 0x8c, 0xae, 0x0e, 0xbc, 0x60, 0x7c, 0x20, 0xea, 0xeb, 0xdf, 0xa7, 0xf0, 0xf8, 0xeb, 0xdf, 0x1d, 0x5e, 0x79, 0x6c, 0xba, 0xb8, 0x3c, 0x18, 0x85,
0xe6, 0x60, 0x61, 0xe7, 0x62, 0x43, 0xfc, 0x34, 0xf9, 0xd3, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x43, 0xf1, 0x4b, 0x46, 0xe0, 0x05, 0x57, 0x01, 0x65, 0x6f, 0xc3, 0xe8, 0xfa, 0xd0, 0x0f,
0x16, 0x00, 0xbc, 0x19, 0xca, 0x14, 0x00, 0x00, 0xc6, 0x87, 0xa2, 0x6e, 0x0e, 0x97, 0x7a, 0x2e, 0xb7, 0xc4, 0x0f, 0x99, 0x3f, 0xfd, 0x4f, 0x00,
0x00, 0x00, 0xff, 0xff, 0x61, 0x45, 0xd3, 0x59, 0xf8, 0x14, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.

@ -154,10 +154,17 @@ message SendPaymentRequest {
int64 fee_limit_msat = 13; int64 fee_limit_msat = 13;
/* /*
The channel id of the channel that must be taken to the first hop. If zero, Deprecated, use outgoing_chan_ids. The channel id of the channel that must
be taken to the first hop. If zero, any channel may be used (unless
outgoing_chan_ids are set).
*/
uint64 outgoing_chan_id = 8 [jstype = JS_STRING, deprecated = true];
/*
The channel ids of the channels are allowed for the first hop. If empty,
any channel may be used. any channel may be used.
*/ */
uint64 outgoing_chan_id = 8 [jstype = JS_STRING]; repeated uint64 outgoing_chan_ids = 19;
/* /*
The pubkey of the last hop of the route. If empty, any hop may be used. The pubkey of the last hop of the route. If empty, any hop may be used.

@ -248,7 +248,7 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
// Pass along an outgoing channel restriction if specified. // Pass along an outgoing channel restriction if specified.
if in.OutgoingChanId != 0 { if in.OutgoingChanId != 0 {
restrictions.OutgoingChannelID = &in.OutgoingChanId restrictions.OutgoingChannelIDs = []uint64{in.OutgoingChanId}
} }
// Pass along a last hop restriction if specified. // Pass along a last hop restriction if specified.
@ -522,9 +522,19 @@ func (r *RouterBackend) extractIntentFromSendRequest(
payIntent := &routing.LightningPayment{} payIntent := &routing.LightningPayment{}
// Pass along an outgoing channel restriction if specified. // Pass along restrictions on the outgoing channels that may be used.
payIntent.OutgoingChannelIDs = rpcPayReq.OutgoingChanIds
// Add the deprecated single outgoing channel restriction if present.
if rpcPayReq.OutgoingChanId != 0 { if rpcPayReq.OutgoingChanId != 0 {
payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId if payIntent.OutgoingChannelIDs != nil {
return nil, errors.New("outgoing_chan_id and " +
"outgoing_chan_ids are mutually exclusive")
}
payIntent.OutgoingChannelIDs = append(
payIntent.OutgoingChannelIDs, rpcPayReq.OutgoingChanId,
)
} }
// Pass along a last hop restriction if specified. // Pass along a last hop restriction if specified.

@ -159,7 +159,7 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool) {
t.Fatal("unexpected last hop") t.Fatal("unexpected last hop")
} }
if *restrictions.OutgoingChannelID != outgoingChan { if restrictions.OutgoingChannelIDs[0] != outgoingChan {
t.Fatal("unexpected outgoing channel id") t.Fatal("unexpected outgoing channel id")
} }

@ -46,6 +46,7 @@
<time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=Number of pending channels exceed maximum <time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=Number of pending channels exceed maximum
<time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=Synchronizing blockchain <time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=Synchronizing blockchain
<time> [ERR] FNDG: Unable to add new channel <chan_point> with peer <hex>: peer exiting <time> [ERR] FNDG: Unable to add new channel <chan_point> with peer <hex>: peer exiting
<time> [ERR] FNDG: Unable to add new channel <chan_point> with peer <hex>: unable to get best block: the client has been shutdown
<time> [ERR] FNDG: Unable to advance pending state of ChannelPoint(<chan_point>): error waiting for funding confirmation for ChannelPoint(<chan_point>): epoch client shutting down <time> [ERR] FNDG: Unable to advance pending state of ChannelPoint(<chan_point>): error waiting for funding confirmation for ChannelPoint(<chan_point>): epoch client shutting down
<time> [ERR] FNDG: Unable to advance pending state of ChannelPoint(<chan_point>): error waiting for funding confirmation for ChannelPoint(<chan_point>): funding manager shutting down <time> [ERR] FNDG: Unable to advance pending state of ChannelPoint(<chan_point>): error waiting for funding confirmation for ChannelPoint(<chan_point>): funding manager shutting down
<time> [ERR] FNDG: Unable to advance pending state of ChannelPoint(<chan_point>): error waiting for funding confirmation for ChannelPoint(<chan_point>): waiting for fundingconfirmation failed <time> [ERR] FNDG: Unable to advance pending state of ChannelPoint(<chan_point>): error waiting for funding confirmation for ChannelPoint(<chan_point>): waiting for fundingconfirmation failed
@ -55,6 +56,7 @@
<time> [ERR] FNDG: Unable to advance state(<chan_point>): error sending channel announcement: channel announcement failed: router shutting down <time> [ERR] FNDG: Unable to advance state(<chan_point>): error sending channel announcement: channel announcement failed: router shutting down
<time> [ERR] FNDG: Unable to advance state(<chan_point>): error sending channel announcement: channel announcement failed: unable add proof to the channel chanID=<hex>: edge marked as zombie <time> [ERR] FNDG: Unable to advance state(<chan_point>): error sending channel announcement: channel announcement failed: unable add proof to the channel chanID=<hex>: edge marked as zombie
<time> [ERR] FNDG: Unable to advance state(<chan_point>): error sending channel announcement: channel announcement failed: unable add proof to the channel chanID=<hex>: edge not found <time> [ERR] FNDG: Unable to advance state(<chan_point>): error sending channel announcement: channel announcement failed: unable add proof to the channel chanID=<hex>: edge not found
<time> [ERR] FNDG: Unable to advance state(<chan_point>): failed adding to router graph: error sending channel announcement: router shutting down
<time> [ERR] FNDG: Unable to advance state(<chan_point>): failed adding to router graph: funding manager shutting down <time> [ERR] FNDG: Unable to advance state(<chan_point>): failed adding to router graph: funding manager shutting down
<time> [ERR] FNDG: Unable to advance state(<chan_point>): failed sending fundingLocked: funding manager shutting down <time> [ERR] FNDG: Unable to advance state(<chan_point>): failed sending fundingLocked: funding manager shutting down
<time> [ERR] FNDG: unable to cancel reservation: no active reservations for peer(<hex>) <time> [ERR] FNDG: unable to cancel reservation: no active reservations for peer(<hex>)

@ -284,9 +284,9 @@ type RestrictParams struct {
// the source to the target. // the source to the target.
FeeLimit lnwire.MilliSatoshi FeeLimit lnwire.MilliSatoshi
// OutgoingChannelID is the channel that needs to be taken to the first // OutgoingChannelIDs is the list of channels that are allowed for the
// hop. If nil, any channel may be used. // first hop. If nil, any channel may be used.
OutgoingChannelID *uint64 OutgoingChannelIDs []uint64
// LastHop is the pubkey of the last node before the final destination // LastHop is the pubkey of the last node before the final destination
// is reached. If nil, any node may be used. // is reached. If nil, any node may be used.
@ -329,7 +329,7 @@ type PathFindingConfig struct {
// getOutgoingBalance returns the maximum available balance in any of the // getOutgoingBalance returns the maximum available balance in any of the
// channels of the given node. The second return parameters is the total // channels of the given node. The second return parameters is the total
// available balance. // available balance.
func getOutgoingBalance(node route.Vertex, outgoingChan *uint64, func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
bandwidthHints map[uint64]lnwire.MilliSatoshi, bandwidthHints map[uint64]lnwire.MilliSatoshi,
g routingGraph) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) { g routingGraph) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) {
@ -344,9 +344,11 @@ func getOutgoingBalance(node route.Vertex, outgoingChan *uint64,
chanID := outEdge.ChannelID chanID := outEdge.ChannelID
// Enforce outgoing channel restriction. // Enforce outgoing channel restriction.
if outgoingChan != nil && chanID != *outgoingChan { if outgoingChans != nil {
if _, ok := outgoingChans[chanID]; !ok {
return nil return nil
} }
}
bandwidth, ok := bandwidthHints[chanID] bandwidth, ok := bandwidthHints[chanID]
@ -447,13 +449,22 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
return nil, errNoPaymentAddr return nil, errNoPaymentAddr
} }
// Set up outgoing channel map for quicker access.
var outgoingChanMap map[uint64]struct{}
if len(r.OutgoingChannelIDs) > 0 {
outgoingChanMap = make(map[uint64]struct{})
for _, outChan := range r.OutgoingChannelIDs {
outgoingChanMap[outChan] = struct{}{}
}
}
// If we are routing from ourselves, check that we have enough local // If we are routing from ourselves, check that we have enough local
// balance available. // balance available.
self := g.graph.sourceNode() self := g.graph.sourceNode()
if source == self { if source == self {
max, total, err := getOutgoingBalance( max, total, err := getOutgoingBalance(
self, r.OutgoingChannelID, g.bandwidthHints, g.graph, self, outgoingChanMap, g.bandwidthHints, g.graph,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -763,7 +774,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
pivot := partialPath.node pivot := partialPath.node
// Create unified policies for all incoming connections. // Create unified policies for all incoming connections.
u := newUnifiedPolicies(self, pivot, r.OutgoingChannelID) u := newUnifiedPolicies(self, pivot, outgoingChanMap)
err := u.addGraphPolicies(g.graph) err := u.addGraphPolicies(g.graph)
if err != nil { if err != nil {

@ -2247,26 +2247,39 @@ func TestNewRouteFromEmptyHops(t *testing.T) {
func TestRestrictOutgoingChannel(t *testing.T) { func TestRestrictOutgoingChannel(t *testing.T) {
t.Parallel() t.Parallel()
// Define channel id constants
const (
chanSourceA = 1
chanATarget = 4
chanSourceB1 = 2
chanSourceB2 = 3
chanBTarget = 5
chanSourceTarget = 6
)
// Set up a test graph with three possible paths from roasbeef to // Set up a test graph with three possible paths from roasbeef to
// target. The path through channel 2 is the highest cost path. // target. The path through chanSourceB1 is the highest cost path.
testChannels := []*testChannel{ testChannels := []*testChannel{
symmetricTestChannel("roasbeef", "a", 100000, &testChannelPolicy{ symmetricTestChannel("roasbeef", "a", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
}, 1), }, chanSourceA),
symmetricTestChannel("a", "target", 100000, &testChannelPolicy{ symmetricTestChannel("a", "target", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
FeeRate: 400, FeeRate: 400,
}, 4), }, chanATarget),
symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{ symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
}, 2), }, chanSourceB1),
symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{ symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
}, 3), }, chanSourceB2),
symmetricTestChannel("b", "target", 100000, &testChannelPolicy{ symmetricTestChannel("b", "target", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
FeeRate: 800, FeeRate: 800,
}, 5), }, chanBTarget),
symmetricTestChannel("roasbeef", "target", 100000, &testChannelPolicy{
Expiry: 144,
}, chanSourceTarget),
} }
ctx := newPathFindingTestContext(t, testChannels, "roasbeef") ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
@ -2279,32 +2292,36 @@ func TestRestrictOutgoingChannel(t *testing.T) {
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := ctx.keyFromAlias("target") target := ctx.keyFromAlias("target")
outgoingChannelID := uint64(2) outgoingChannelID := uint64(chanSourceB1)
// Find the best path given the restriction to only use channel 2 as the // Find the best path given the restriction to only use channel 2 as the
// outgoing channel. // outgoing channel.
ctx.restrictParams.OutgoingChannelID = &outgoingChannelID ctx.restrictParams.OutgoingChannelIDs = []uint64{outgoingChannelID}
path, err := ctx.findPath(target, paymentAmt) path, err := ctx.findPath(target, paymentAmt)
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)
} }
route, err := newRoute(
ctx.source, path, startingHeight, // Assert that the route starts with channel chanSourceB1, in line with
finalHopParams{ // the specified restriction.
amt: paymentAmt, if path[0].ChannelID != chanSourceB1 {
cltvDelta: finalHopCLTV, t.Fatalf("expected route to pass through channel %v, "+
records: nil, "but channel %v was selected instead", chanSourceB1,
}, path[0].ChannelID)
)
if err != nil {
t.Fatalf("unable to create path: %v", err)
} }
// Assert that the route starts with channel 2, in line with the // If a direct channel to target is allowed as well, that channel is
// specified restriction. // expected to be selected because the routing fees are zero.
if route.Hops[0].ChannelID != 2 { ctx.restrictParams.OutgoingChannelIDs = []uint64{
t.Fatalf("expected route to pass through channel 2, "+ chanSourceB1, chanSourceTarget,
"but channel %v was selected instead", route.Hops[0].ChannelID) }
path, err = ctx.findPath(target, paymentAmt)
if err != nil {
t.Fatalf("unable to find path: %v", err)
}
if path[0].ChannelID != chanSourceTarget {
t.Fatalf("expected route to pass through channel %v",
chanSourceTarget)
} }
} }
@ -2767,7 +2784,7 @@ func TestRouteToSelf(t *testing.T) {
outgoingChanID := uint64(1) outgoingChanID := uint64(1)
lastHop := ctx.keyFromAlias("b") lastHop := ctx.keyFromAlias("b")
ctx.restrictParams.OutgoingChannelID = &outgoingChanID ctx.restrictParams.OutgoingChannelIDs = []uint64{outgoingChanID}
ctx.restrictParams.LastHop = &lastHop ctx.restrictParams.LastHop = &lastHop
// Find the best path to self given that we want to go out via channel 1 // Find the best path to self given that we want to go out via channel 1

@ -204,7 +204,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
restrictions := &RestrictParams{ restrictions := &RestrictParams{
ProbabilitySource: p.missionControl.GetProbability, ProbabilitySource: p.missionControl.GetProbability,
FeeLimit: feeLimit, FeeLimit: feeLimit,
OutgoingChannelID: p.payment.OutgoingChannelID, OutgoingChannelIDs: p.payment.OutgoingChannelIDs,
LastHop: p.payment.LastHop, LastHop: p.payment.LastHop,
CltvLimit: cltvLimit, CltvLimit: cltvLimit,
DestCustomRecords: p.payment.DestCustomRecords, DestCustomRecords: p.payment.DestCustomRecords,

@ -1594,9 +1594,9 @@ type LightningPayment struct {
// destination successfully. // destination successfully.
RouteHints [][]zpay32.HopHint RouteHints [][]zpay32.HopHint
// OutgoingChannelID is the channel that needs to be taken to the first // OutgoingChannelIDs is the list of channels that are allowed for the
// hop. If nil, any channel may be used. // first hop. If nil, any channel may be used.
OutgoingChannelID *uint64 OutgoingChannelIDs []uint64
// LastHop is the pubkey of the last node before the final destination // LastHop is the pubkey of the last node before the final destination
// is reached. If nil, any node may be used. // is reached. If nil, any node may be used.
@ -2379,6 +2379,13 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
log.Tracef("BuildRoute called: hopsCount=%v, amt=%v", log.Tracef("BuildRoute called: hopsCount=%v, amt=%v",
len(hops), amt) len(hops), amt)
var outgoingChans map[uint64]struct{}
if outgoingChan != nil {
outgoingChans = map[uint64]struct{}{
*outgoingChan: {},
}
}
// If no amount is specified, we need to build a route for the minimum // If no amount is specified, we need to build a route for the minimum
// amount that this route can carry. // amount that this route can carry.
useMinAmt := amt == nil useMinAmt := amt == nil
@ -2444,7 +2451,7 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
// Build unified policies for this hop based on the channels // Build unified policies for this hop based on the channels
// known in the graph. // known in the graph.
u := newUnifiedPolicies(source, toNode, outgoingChan) u := newUnifiedPolicies(source, toNode, outgoingChans)
err := u.addGraphPolicies(routingTx) err := u.addGraphPolicies(routingTx)
if err != nil { if err != nil {

@ -21,13 +21,13 @@ type unifiedPolicies struct {
// outChanRestr is an optional outgoing channel restriction for the // outChanRestr is an optional outgoing channel restriction for the
// local channel to use. // local channel to use.
outChanRestr *uint64 outChanRestr map[uint64]struct{}
} }
// newUnifiedPolicies instantiates a new unifiedPolicies object. Channel // newUnifiedPolicies instantiates a new unifiedPolicies object. Channel
// policies can be added to this object. // policies can be added to this object.
func newUnifiedPolicies(sourceNode, toNode route.Vertex, func newUnifiedPolicies(sourceNode, toNode route.Vertex,
outChanRestr *uint64) *unifiedPolicies { outChanRestr map[uint64]struct{}) *unifiedPolicies {
return &unifiedPolicies{ return &unifiedPolicies{
policies: make(map[route.Vertex]*unifiedPolicy), policies: make(map[route.Vertex]*unifiedPolicy),
@ -45,11 +45,11 @@ func (u *unifiedPolicies) addPolicy(fromNode route.Vertex,
localChan := fromNode == u.sourceNode localChan := fromNode == u.sourceNode
// Skip channels if there is an outgoing channel restriction. // Skip channels if there is an outgoing channel restriction.
if localChan && u.outChanRestr != nil && if localChan && u.outChanRestr != nil {
*u.outChanRestr != edge.ChannelID { if _, ok := u.outChanRestr[edge.ChannelID]; !ok {
return return
} }
}
// Update the policies map. // Update the policies map.
policy, ok := u.policies[fromNode] policy, ok := u.policies[fromNode]

@ -3694,7 +3694,7 @@ type rpcPaymentIntent struct {
rHash [32]byte rHash [32]byte
cltvDelta uint16 cltvDelta uint16
routeHints [][]zpay32.HopHint routeHints [][]zpay32.HopHint
outgoingChannelID *uint64 outgoingChannelIDs []uint64
lastHop *route.Vertex lastHop *route.Vertex
destFeatures *lnwire.FeatureVector destFeatures *lnwire.FeatureVector
paymentAddr *[32]byte paymentAddr *[32]byte
@ -3734,9 +3734,12 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme
} }
// If there are no routes specified, pass along a outgoing channel // If there are no routes specified, pass along a outgoing channel
// restriction if specified. // restriction if specified. The main server rpc does not support
// multiple channel restrictions.
if rpcPayReq.OutgoingChanId != 0 { if rpcPayReq.OutgoingChanId != 0 {
payIntent.outgoingChannelID = &rpcPayReq.OutgoingChanId payIntent.outgoingChannelIDs = []uint64{
rpcPayReq.OutgoingChanId,
}
} }
// Pass along a last hop restriction if specified. // Pass along a last hop restriction if specified.
@ -3958,7 +3961,7 @@ func (r *rpcServer) dispatchPaymentIntent(
CltvLimit: payIntent.cltvLimit, CltvLimit: payIntent.cltvLimit,
PaymentHash: payIntent.rHash, PaymentHash: payIntent.rHash,
RouteHints: payIntent.routeHints, RouteHints: payIntent.routeHints,
OutgoingChannelID: payIntent.outgoingChannelID, OutgoingChannelIDs: payIntent.outgoingChannelIDs,
LastHop: payIntent.lastHop, LastHop: payIntent.lastHop,
PaymentRequest: payIntent.payReq, PaymentRequest: payIntent.payReq,
PayAttemptTimeout: routing.DefaultPayAttemptTimeout, PayAttemptTimeout: routing.DefaultPayAttemptTimeout,