Merge pull request #3739 from joostjager/incoming-restriction

routing: last hop restriction
This commit is contained in:
Joost Jager authored and GitHub committed 2019-11-19 08:38:33 +01:00
commit a00a360018
12 files changed
+928 -828

No files matched your search

+24 -5
View File
@@ -24,6 +24,7 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/walletunlocker"
"github.com/urfave/cli" "github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
@@ -2040,11 +2041,18 @@ func closedChannels(ctx *cli.Context) error {
return nil return nil
} }
var cltvLimitFlag = cli.UintFlag{ var (
Name: "cltv_limit", cltvLimitFlag = cli.UintFlag{
Usage: "the maximum time lock that may be used for " + Name: "cltv_limit",
"this payment", Usage: "the maximum time lock that may be used for " +
} "this payment",
}
lastHopFlag = cli.StringFlag{
Name: "last_hop",
Usage: "pubkey of the last hop to use for this payment",
}
)
// paymentFlags returns common flags for sendpayment and payinvoice. // paymentFlags returns common flags for sendpayment and payinvoice.
func paymentFlags() []cli.Flag { func paymentFlags() []cli.Flag {
@@ -2065,6 +2073,7 @@ func paymentFlags() []cli.Flag {
"payment", "payment",
}, },
cltvLimitFlag, cltvLimitFlag,
lastHopFlag,
cli.Uint64Flag{ cli.Uint64Flag{
Name: "outgoing_chan_id", Name: "outgoing_chan_id",
Usage: "short channel id of the outgoing channel to " + Usage: "short channel id of the outgoing channel to " +
@@ -2281,6 +2290,16 @@ func sendPaymentRequest(ctx *cli.Context, req *lnrpc.SendRequest) error {
req.FeeLimit = feeLimit req.FeeLimit = feeLimit
req.OutgoingChanId = ctx.Uint64("outgoing_chan_id") req.OutgoingChanId = ctx.Uint64("outgoing_chan_id")
if ctx.IsSet(lastHopFlag.Name) {
lastHop, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name),
)
if err != nil {
return err
}
req.LastHopPubkey = lastHop[:]
}
req.CltvLimit = uint32(ctx.Int(cltvLimitFlag.Name)) req.CltvLimit = uint32(ctx.Int(cltvLimitFlag.Name))
amt := req.Amt amt := req.Amt
+135 -124
View File
@@ -231,6 +231,9 @@ type SendPaymentRequest struct {
//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"` OutgoingChanId uint64 `protobuf:"varint,8,opt,name=outgoing_chan_id,json=outgoingChanId,proto3" json:"outgoing_chan_id,omitempty"`
//* //*
//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"`
//*
//An optional maximum total time lock for the route. This should not exceed //An optional maximum total time lock for the route. This should not exceed
//lnd's `--max-cltv-expiry` setting. If zero, then the value of //lnd's `--max-cltv-expiry` setting. If zero, then the value of
//`--max-cltv-expiry` is enforced. //`--max-cltv-expiry` is enforced.
@@ -343,6 +346,13 @@ func (m *SendPaymentRequest) GetOutgoingChanId() uint64 {
return 0 return 0
} }
func (m *SendPaymentRequest) GetLastHopPubkey() []byte {
if m != nil {
return m.LastHopPubkey
}
return nil
}
func (m *SendPaymentRequest) GetCltvLimit() int32 { func (m *SendPaymentRequest) GetCltvLimit() int32 {
if m != nil { if m != nil {
return m.CltvLimit return m.CltvLimit
@@ -1445,130 +1455,131 @@ 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{
// 1957 bytes of a gzipped FileDescriptorProto // 1975 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0xdd, 0x72, 0x1a, 0xc9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0x4f, 0x73, 0x1a, 0xc9,
0x15, 0xde, 0xe1, 0x47, 0xc0, 0x01, 0xa4, 0x51, 0x4b, 0x96, 0x31, 0x92, 0xd6, 0xda, 0xb1, 0xb3, 0x15, 0xdf, 0x11, 0x20, 0xe0, 0x01, 0xd2, 0xa8, 0xa5, 0x95, 0xc7, 0x48, 0x5a, 0x6b, 0xc7, 0x8e,
0xab, 0x72, 0x79, 0x25, 0x87, 0xd4, 0x6e, 0xb9, 0xf6, 0x22, 0x29, 0x0c, 0xc3, 0x6a, 0x64, 0x18, 0x57, 0xe5, 0xf2, 0x4a, 0x8e, 0x52, 0xbb, 0xe5, 0xda, 0x43, 0x52, 0x18, 0x86, 0xd5, 0xc8, 0x30,
0xe4, 0x06, 0xbc, 0xeb, 0xec, 0x45, 0x57, 0x0b, 0x5a, 0x62, 0x4a, 0xc3, 0x0c, 0x3b, 0xd3, 0x38, 0xc8, 0x0d, 0x78, 0xd7, 0xd9, 0x43, 0x57, 0x0b, 0x5a, 0x62, 0x4a, 0xc3, 0x0c, 0x3b, 0xd3, 0x38,
0x56, 0x2e, 0xf2, 0x04, 0xc9, 0x6d, 0x5e, 0x21, 0x37, 0xc9, 0x55, 0xde, 0x20, 0x0f, 0x93, 0x3c, 0x56, 0x0e, 0xf9, 0x04, 0xc9, 0x35, 0x5f, 0x21, 0x97, 0xe4, 0x94, 0xef, 0x94, 0x54, 0xe5, 0x9e,
0x41, 0xee, 0x53, 0xdd, 0x3d, 0x03, 0x03, 0x42, 0xda, 0x5c, 0x89, 0xfe, 0xce, 0xe9, 0xd3, 0xa7, 0x7b, 0xaa, 0xbb, 0x67, 0x60, 0x40, 0x48, 0x9b, 0x93, 0xe8, 0xdf, 0x7b, 0xfd, 0x5e, 0xcf, 0xfb,
0xcf, 0xcf, 0xd7, 0x67, 0x04, 0x7b, 0x81, 0x3f, 0xe3, 0x2c, 0x08, 0xa6, 0xc3, 0x53, 0xf5, 0xeb, 0xf3, 0xeb, 0xd7, 0x82, 0xdd, 0x30, 0x98, 0x72, 0x16, 0x86, 0x93, 0xc1, 0x89, 0xfa, 0x75, 0x3c,
0x64, 0x1a, 0xf8, 0xdc, 0x47, 0x85, 0x39, 0x5e, 0x2d, 0x04, 0xd3, 0xa1, 0x42, 0x8d, 0x7f, 0x65, 0x09, 0x03, 0x1e, 0xa0, 0xe2, 0x0c, 0xaf, 0x16, 0xc3, 0xc9, 0x40, 0xa1, 0xe6, 0x7f, 0xb2, 0x80,
0x00, 0xf5, 0x98, 0x37, 0xba, 0xa0, 0xb7, 0x13, 0xe6, 0x71, 0xcc, 0x7e, 0x9e, 0xb1, 0x90, 0x23, 0xba, 0xcc, 0x1f, 0x5e, 0xd0, 0xdb, 0x31, 0xf3, 0x39, 0x66, 0x3f, 0x4f, 0x59, 0xc4, 0x11, 0x82,
0x04, 0x99, 0x11, 0x0b, 0x79, 0x45, 0x3b, 0xd2, 0x8e, 0x4b, 0x58, 0xfe, 0x46, 0x3a, 0xa4, 0xe9, 0xec, 0x90, 0x45, 0xdc, 0xd0, 0x0e, 0xb5, 0xa3, 0x32, 0x96, 0xbf, 0x91, 0x0e, 0x19, 0x3a, 0xe6,
0x84, 0x57, 0x52, 0x47, 0xda, 0x71, 0x1a, 0x8b, 0x9f, 0xe8, 0x09, 0xe4, 0xe9, 0x84, 0x93, 0x49, 0xc6, 0xda, 0xa1, 0x76, 0x94, 0xc1, 0xe2, 0x27, 0x7a, 0x0c, 0x05, 0x3a, 0xe6, 0x64, 0x1c, 0x51,
0x48, 0x79, 0xa5, 0x24, 0xe1, 0x1c, 0x9d, 0xf0, 0x4e, 0x48, 0x39, 0xfa, 0x02, 0x4a, 0x53, 0x65, 0x6e, 0x94, 0x25, 0x9c, 0xa7, 0x63, 0xde, 0x8e, 0x28, 0x47, 0x5f, 0x42, 0x79, 0xa2, 0x4c, 0x92,
0x92, 0x8c, 0x69, 0x38, 0xae, 0xa4, 0xa5, 0xa1, 0x62, 0x84, 0x9d, 0xd1, 0x70, 0x8c, 0x8e, 0x41, 0x11, 0x8d, 0x46, 0x46, 0x46, 0x1a, 0x2a, 0xc5, 0xd8, 0x19, 0x8d, 0x46, 0xe8, 0x08, 0xf4, 0x2b,
0xbf, 0x72, 0x3c, 0xea, 0x92, 0xa1, 0xcb, 0x3f, 0x92, 0x11, 0x73, 0x39, 0xad, 0x64, 0x8e, 0xb4, 0xd7, 0xa7, 0x1e, 0x19, 0x78, 0xfc, 0x23, 0x19, 0x32, 0x8f, 0x53, 0x23, 0x7b, 0xa8, 0x1d, 0xe5,
0xe3, 0x2c, 0xde, 0x94, 0x78, 0xc3, 0xe5, 0x1f, 0x9b, 0x02, 0x45, 0x5f, 0xc1, 0x56, 0x6c, 0x2c, 0xf0, 0x86, 0xc4, 0xeb, 0x1e, 0xff, 0xd8, 0x10, 0x28, 0xfa, 0x0a, 0x36, 0x13, 0x63, 0xa1, 0x3a,
0x50, 0x0e, 0x56, 0xb2, 0x47, 0xda, 0x71, 0x01, 0x6f, 0x4e, 0x97, 0xdd, 0xfe, 0x0a, 0xb6, 0xb8, 0xa0, 0x91, 0x3b, 0xd4, 0x8e, 0x8a, 0x78, 0x63, 0xb2, 0x78, 0xec, 0xaf, 0x60, 0x93, 0xbb, 0x63,
0x33, 0x61, 0xfe, 0x8c, 0x93, 0x90, 0x0d, 0x7d, 0x6f, 0x14, 0x56, 0x36, 0x94, 0xc5, 0x08, 0xee, 0x16, 0x4c, 0x39, 0x89, 0xd8, 0x20, 0xf0, 0x87, 0x91, 0xb1, 0xae, 0x2c, 0xc6, 0x70, 0x57, 0xa1,
0x29, 0x14, 0x19, 0x50, 0xbe, 0x62, 0x8c, 0xb8, 0xce, 0xc4, 0xe1, 0x44, 0xb8, 0x9f, 0x93, 0xee, 0xc8, 0x84, 0xca, 0x15, 0x63, 0xc4, 0x73, 0xc7, 0x2e, 0x27, 0xe2, 0xf8, 0x79, 0x79, 0xfc, 0xd2,
0x17, 0xaf, 0x18, 0x6b, 0x0b, 0xac, 0x47, 0x39, 0x7a, 0x0e, 0x9b, 0x0b, 0x1d, 0x79, 0xc7, 0xb2, 0x15, 0x63, 0x2d, 0x81, 0x75, 0x29, 0x47, 0xcf, 0x60, 0x63, 0xae, 0x23, 0xbf, 0xb1, 0x22, 0x95,
0x54, 0x2a, 0xc5, 0x4a, 0xf2, 0xa2, 0x2f, 0x41, 0xf7, 0x67, 0xfc, 0xda, 0x77, 0xbc, 0x6b, 0x32, 0xca, 0x89, 0x92, 0xfc, 0xd0, 0x97, 0xa0, 0x07, 0x53, 0x7e, 0x1d, 0xb8, 0xfe, 0x35, 0x19, 0x8c,
0x1c, 0x53, 0x8f, 0x38, 0xa3, 0x4a, 0xfe, 0x48, 0x3b, 0xce, 0xbc, 0x49, 0xbd, 0xd2, 0xf0, 0x66, 0xa8, 0x4f, 0xdc, 0xa1, 0x51, 0x38, 0xd4, 0x8e, 0xb2, 0x6f, 0xd6, 0x5e, 0x69, 0x78, 0x23, 0x91,
0x2c, 0x6b, 0x8c, 0xa9, 0x67, 0x8d, 0xd0, 0x21, 0x80, 0xbc, 0xad, 0x34, 0x5a, 0x29, 0x48, 0xdf, 0xd5, 0x47, 0xd4, 0xb7, 0x87, 0xe8, 0x39, 0x6c, 0x7a, 0x34, 0xe2, 0x64, 0x14, 0x4c, 0xc8, 0x64,
0x0a, 0x02, 0x91, 0x06, 0x51, 0x0d, 0x8a, 0x32, 0x4b, 0x64, 0xec, 0x78, 0x3c, 0xac, 0xc0, 0x51, 0x7a, 0x79, 0xc3, 0x6e, 0x8d, 0x0d, 0x19, 0x99, 0x8a, 0x80, 0xcf, 0x82, 0xc9, 0x85, 0x04, 0xd1,
0xfa, 0xb8, 0x58, 0xd3, 0x4f, 0x5c, 0x4f, 0x24, 0x0c, 0x0b, 0xc9, 0x99, 0xe3, 0x71, 0x9c, 0x54, 0x01, 0x80, 0x8c, 0x8a, 0x74, 0x6e, 0x14, 0xe5, 0x37, 0x14, 0x05, 0x22, 0x1d, 0xa3, 0x53, 0x28,
0x42, 0x26, 0xe4, 0x45, 0x7a, 0x08, 0x77, 0x3f, 0x56, 0x8a, 0x72, 0xc3, 0x8b, 0x93, 0x79, 0xaa, 0xc9, 0x6c, 0x92, 0x91, 0xeb, 0xf3, 0xc8, 0x80, 0xc3, 0xcc, 0x51, 0xe9, 0x54, 0x3f, 0xf6, 0x7c,
0x4f, 0xee, 0xe6, 0xf6, 0xa4, 0xc9, 0x42, 0xde, 0x77, 0x3f, 0x9a, 0x1e, 0x0f, 0x6e, 0x71, 0x6e, 0x91, 0x58, 0x2c, 0x24, 0x67, 0xae, 0xcf, 0x71, 0x5a, 0x09, 0x59, 0x50, 0x10, 0x69, 0x24, 0xdc,
0xa4, 0x56, 0xd5, 0xef, 0xa0, 0x94, 0x14, 0x88, 0x6c, 0xdf, 0xb0, 0x5b, 0x59, 0x00, 0x19, 0x2c, 0xfb, 0x68, 0x94, 0xe4, 0x86, 0x17, 0xc7, 0xb3, 0x92, 0x38, 0xbe, 0x5b, 0x03, 0xc7, 0x0d, 0x16,
0x7e, 0xa2, 0x5d, 0xc8, 0x7e, 0xa4, 0xee, 0x8c, 0xc9, 0x0a, 0x28, 0x61, 0xb5, 0xf8, 0x2e, 0xf5, 0xf1, 0x9e, 0xf7, 0xd1, 0xf2, 0x79, 0x78, 0x8b, 0xf3, 0x43, 0xb5, 0xaa, 0x7e, 0x07, 0xe5, 0xb4,
0x5a, 0x33, 0x5e, 0xc3, 0x4e, 0x3f, 0xa0, 0xc3, 0x9b, 0x95, 0x22, 0x5a, 0xad, 0x01, 0xed, 0x4e, 0x40, 0x54, 0x85, 0xf8, 0x0a, 0x51, 0x28, 0x59, 0x2c, 0x7e, 0xa2, 0x1d, 0xc8, 0x7d, 0xa4, 0xde,
0x0d, 0x18, 0x7f, 0x82, 0x72, 0xb4, 0xa9, 0xc7, 0x29, 0x9f, 0x85, 0xe8, 0x6b, 0xc8, 0x86, 0x9c, 0x94, 0xc9, 0x4a, 0x29, 0x63, 0xb5, 0xf8, 0x6e, 0xed, 0xb5, 0x66, 0xbe, 0x86, 0xed, 0x5e, 0x48,
0x72, 0x26, 0x95, 0x37, 0x6b, 0x8f, 0x13, 0x57, 0x49, 0x28, 0x32, 0xac, 0xb4, 0x50, 0x15, 0xf2, 0x07, 0x37, 0x4b, 0xc5, 0xb6, 0x5c, 0x2b, 0xda, 0x9d, 0x5a, 0x31, 0xff, 0x04, 0x95, 0x78, 0x53,
0xd3, 0x80, 0x39, 0x13, 0x7a, 0x1d, 0xbb, 0x35, 0x5f, 0x23, 0x03, 0xb2, 0x72, 0xb3, 0xac, 0xbd, 0x97, 0x53, 0x3e, 0x8d, 0xd0, 0xd7, 0x90, 0x8b, 0x38, 0xe5, 0x4c, 0x2a, 0x6f, 0x9c, 0x3e, 0x4a,
0x62, 0xad, 0x94, 0x0c, 0x23, 0x56, 0x22, 0xe3, 0xb7, 0xb0, 0x25, 0xd7, 0x2d, 0xc6, 0x1e, 0x2a, 0x7d, 0x4a, 0x4a, 0x91, 0x61, 0xa5, 0x85, 0xaa, 0x50, 0x98, 0x84, 0xcc, 0x1d, 0xd3, 0xeb, 0xe4,
0xfd, 0xc7, 0x20, 0x0a, 0x5b, 0x16, 0x8a, 0x2a, 0xff, 0x0d, 0x3a, 0x11, 0x35, 0x62, 0x8c, 0x40, 0x58, 0xb3, 0x35, 0x32, 0x21, 0x27, 0x37, 0xcb, 0x1a, 0x2d, 0x9d, 0x96, 0xd3, 0x61, 0xc4, 0x4a,
0x5f, 0xec, 0x0f, 0xa7, 0xbe, 0x17, 0x32, 0x51, 0xd7, 0xc2, 0xb8, 0x28, 0x08, 0x51, 0x3f, 0xb2, 0x64, 0xfe, 0x16, 0x36, 0xe5, 0xba, 0xc9, 0xd8, 0x43, 0x2d, 0xf2, 0x08, 0x44, 0x03, 0xc8, 0x82,
0x72, 0x34, 0xb9, 0x6b, 0x33, 0xc2, 0x5b, 0x8c, 0xc9, 0xda, 0xf9, 0x52, 0x95, 0x2b, 0x71, 0xfd, 0x52, 0x6d, 0xb2, 0x4e, 0xc7, 0xa2, 0x96, 0xcc, 0x21, 0xe8, 0xf3, 0xfd, 0xd1, 0x24, 0xf0, 0x23,
0xe1, 0x8d, 0x68, 0x00, 0x7a, 0x1b, 0x99, 0x2f, 0x0b, 0xb8, 0xed, 0x0f, 0x6f, 0x9a, 0x02, 0x34, 0x26, 0xea, 0x5f, 0x18, 0x17, 0x85, 0x23, 0xea, 0x4c, 0x56, 0x98, 0x26, 0x77, 0x6d, 0xc4, 0x78,
0x7e, 0x52, 0x3d, 0xda, 0xf7, 0x95, 0xef, 0xff, 0x77, 0x78, 0x17, 0x21, 0x48, 0xdd, 0x1f, 0x02, 0x93, 0x31, 0x59, 0x63, 0xcf, 0x55, 0x59, 0x13, 0x2f, 0x18, 0xdc, 0x88, 0x46, 0xa1, 0xb7, 0xb1,
0x02, 0x3b, 0x4b, 0xc6, 0xa3, 0x5b, 0x24, 0x23, 0xab, 0xad, 0x44, 0xf6, 0x25, 0xe4, 0xae, 0xa8, 0xf9, 0x8a, 0x80, 0x5b, 0xc1, 0xe0, 0xa6, 0x21, 0x40, 0xf3, 0x27, 0xd5, 0xcb, 0xbd, 0x40, 0x9d,
0xe3, 0xce, 0x82, 0xd8, 0x30, 0x4a, 0xa4, 0xa9, 0xa5, 0x24, 0x38, 0x56, 0x31, 0xfe, 0x9b, 0x83, 0xfd, 0xff, 0x0e, 0xef, 0x3c, 0x04, 0x6b, 0xf7, 0x87, 0x80, 0xc0, 0xf6, 0x82, 0xf1, 0xf8, 0x2b,
0x5c, 0x04, 0xa2, 0x1a, 0x64, 0x86, 0xfe, 0x28, 0xce, 0xee, 0xe7, 0x77, 0xb7, 0xc5, 0x7f, 0x1b, 0xd2, 0x91, 0xd5, 0x96, 0x22, 0xfb, 0x12, 0xf2, 0x57, 0xd4, 0xf5, 0xa6, 0x61, 0x62, 0x18, 0xa5,
0xfe, 0x88, 0x61, 0xa9, 0x8b, 0x7e, 0x07, 0x9b, 0xa2, 0xb1, 0x3c, 0xe6, 0x92, 0xd9, 0x74, 0x44, 0xd2, 0xd4, 0x54, 0x12, 0x9c, 0xa8, 0x98, 0xff, 0xcd, 0x43, 0x3e, 0x06, 0xd1, 0x29, 0x64, 0x07,
0xe7, 0x09, 0xad, 0x24, 0x76, 0x37, 0x94, 0xc2, 0x40, 0xca, 0x71, 0x79, 0x98, 0x5c, 0xa2, 0x7d, 0xc1, 0x30, 0xc9, 0xee, 0x17, 0x77, 0xb7, 0x25, 0x7f, 0xeb, 0xc1, 0x90, 0x61, 0xa9, 0x8b, 0x7e,
0x28, 0x8c, 0xb9, 0x3b, 0x54, 0x99, 0xc8, 0xc8, 0x82, 0xce, 0x0b, 0x40, 0xe6, 0xc0, 0x80, 0xb2, 0x07, 0x1b, 0xa2, 0x01, 0x7d, 0xe6, 0x91, 0xe9, 0x64, 0x48, 0x67, 0x09, 0x35, 0x52, 0xbb, 0xeb,
0xef, 0x39, 0xbe, 0x47, 0xc2, 0x31, 0x25, 0xb5, 0x6f, 0xbe, 0x95, 0xcc, 0x52, 0xc2, 0x45, 0x09, 0x4a, 0xa1, 0x2f, 0xe5, 0xb8, 0x32, 0x48, 0x2f, 0xd1, 0x1e, 0x14, 0x47, 0xdc, 0x1b, 0xa8, 0x4c,
0xf6, 0xc6, 0xb4, 0xf6, 0xcd, 0xb7, 0xe8, 0x29, 0x14, 0x65, 0xd7, 0xb2, 0x4f, 0x53, 0x27, 0xb8, 0x64, 0x65, 0x41, 0x17, 0x04, 0x20, 0x73, 0x60, 0x42, 0x25, 0xf0, 0xdd, 0xc0, 0x27, 0xd1, 0x88,
0x95, 0x94, 0x52, 0xc6, 0xb2, 0x91, 0x4d, 0x89, 0x88, 0xd6, 0xb8, 0x72, 0xe9, 0x75, 0x28, 0x69, 0x92, 0xd3, 0x6f, 0xbe, 0x95, 0x0c, 0x54, 0xc6, 0x25, 0x09, 0x76, 0x47, 0xf4, 0xf4, 0x9b, 0x6f,
0xa4, 0x8c, 0xd5, 0x02, 0xbd, 0x82, 0xdd, 0x28, 0x06, 0x24, 0xf4, 0x67, 0xc1, 0x90, 0x11, 0xc7, 0xd1, 0x13, 0x28, 0xc9, 0xae, 0x65, 0x9f, 0x26, 0x6e, 0x78, 0x2b, 0xa9, 0xa7, 0x82, 0x65, 0x23,
0x1b, 0xb1, 0x4f, 0x92, 0x1e, 0xca, 0x18, 0x45, 0xb2, 0x9e, 0x14, 0x59, 0x42, 0x82, 0xf6, 0x60, 0x5b, 0x12, 0x11, 0xad, 0x71, 0xe5, 0xd1, 0xeb, 0x48, 0xd2, 0x4d, 0x05, 0xab, 0x05, 0x7a, 0x05,
0x63, 0xcc, 0x9c, 0xeb, 0xb1, 0xa2, 0x86, 0x32, 0x8e, 0x56, 0xc6, 0xdf, 0xb3, 0x50, 0x4c, 0x04, 0x3b, 0x71, 0x0c, 0x48, 0x14, 0x4c, 0xc3, 0x01, 0x23, 0xae, 0x3f, 0x64, 0x9f, 0x24, 0x8d, 0x54,
0x06, 0x95, 0x20, 0x8f, 0xcd, 0x9e, 0x89, 0xdf, 0x9b, 0x4d, 0xfd, 0x33, 0x74, 0x0c, 0xcf, 0x2d, 0x30, 0x8a, 0x65, 0x5d, 0x29, 0xb2, 0x85, 0x04, 0xed, 0xc2, 0xfa, 0x88, 0xb9, 0xd7, 0x23, 0x45,
0xbb, 0xd1, 0xc5, 0xd8, 0x6c, 0xf4, 0x49, 0x17, 0x93, 0x81, 0xfd, 0xd6, 0xee, 0xfe, 0x60, 0x93, 0x0d, 0x15, 0x1c, 0xaf, 0xcc, 0xbf, 0xe7, 0xa0, 0x94, 0x0a, 0x0c, 0x2a, 0x43, 0x01, 0x5b, 0x5d,
0x8b, 0xfa, 0x87, 0x8e, 0x69, 0xf7, 0x49, 0xd3, 0xec, 0xd7, 0xad, 0x76, 0x4f, 0xd7, 0xd0, 0x01, 0x0b, 0xbf, 0xb7, 0x1a, 0xfa, 0x67, 0xe8, 0x08, 0x9e, 0xd9, 0x4e, 0xbd, 0x83, 0xb1, 0x55, 0xef,
0x54, 0x16, 0x9a, 0xb1, 0xb8, 0xde, 0xe9, 0x0e, 0xec, 0xbe, 0x9e, 0x42, 0x4f, 0x61, 0xbf, 0x65, 0x91, 0x0e, 0x26, 0x7d, 0xe7, 0xad, 0xd3, 0xf9, 0xc1, 0x21, 0x17, 0xb5, 0x0f, 0x6d, 0xcb, 0xe9,
0xd9, 0xf5, 0x36, 0x59, 0xe8, 0x34, 0xda, 0xfd, 0xf7, 0xc4, 0xfc, 0xf1, 0xc2, 0xc2, 0x1f, 0xf4, 0x91, 0x86, 0xd5, 0xab, 0xd9, 0xad, 0xae, 0xae, 0xa1, 0x7d, 0x30, 0xe6, 0x9a, 0x89, 0xb8, 0xd6,
0xf4, 0x3a, 0x85, 0xb3, 0x7e, 0xbb, 0x11, 0x5b, 0xc8, 0xa0, 0x27, 0xf0, 0x48, 0x29, 0xa8, 0x2d, 0xee, 0xf4, 0x9d, 0x9e, 0xbe, 0x86, 0x9e, 0xc0, 0x5e, 0xd3, 0x76, 0x6a, 0x2d, 0x32, 0xd7, 0xa9,
0xa4, 0xdf, 0xed, 0x92, 0x5e, 0xb7, 0x6b, 0xeb, 0x59, 0xb4, 0x0d, 0x65, 0xcb, 0x7e, 0x5f, 0x6f, 0xb7, 0x7a, 0xef, 0x89, 0xf5, 0xe3, 0x85, 0x8d, 0x3f, 0xe8, 0x99, 0x55, 0x0a, 0x67, 0xbd, 0x56,
0x5b, 0x4d, 0x82, 0xcd, 0x7a, 0xbb, 0xa3, 0x6f, 0xa0, 0x1d, 0xd8, 0x5a, 0xd5, 0xcb, 0x09, 0x13, 0x3d, 0xb1, 0x90, 0x45, 0x8f, 0xe1, 0x73, 0xa5, 0xa0, 0xb6, 0x90, 0x5e, 0xa7, 0x43, 0xba, 0x9d,
0xb1, 0x5e, 0xd7, 0xb6, 0xba, 0x36, 0x79, 0x6f, 0xe2, 0x9e, 0xd5, 0xb5, 0xf5, 0x3c, 0xda, 0x03, 0x8e, 0xa3, 0xe7, 0xd0, 0x16, 0x54, 0x6c, 0xe7, 0x7d, 0xad, 0x65, 0x37, 0x08, 0xb6, 0x6a, 0xad,
0xb4, 0x2c, 0x3a, 0xeb, 0xd4, 0x1b, 0x7a, 0x01, 0x3d, 0x82, 0xed, 0x65, 0xfc, 0xad, 0xf9, 0x41, 0xb6, 0xbe, 0x8e, 0xb6, 0x61, 0x73, 0x59, 0x2f, 0x2f, 0x4c, 0x24, 0x7a, 0x1d, 0xc7, 0xee, 0x38,
0x07, 0x54, 0x81, 0x5d, 0xe5, 0x18, 0x79, 0x63, 0xb6, 0xbb, 0x3f, 0x90, 0x8e, 0x65, 0x5b, 0x9d, 0xe4, 0xbd, 0x85, 0xbb, 0x76, 0xc7, 0xd1, 0x0b, 0x68, 0x17, 0xd0, 0xa2, 0xe8, 0xac, 0x5d, 0xab,
0x41, 0x47, 0x2f, 0xa2, 0x5d, 0xd0, 0x5b, 0xa6, 0x49, 0x2c, 0xbb, 0x37, 0x68, 0xb5, 0xac, 0x86, 0xeb, 0x45, 0xf4, 0x39, 0x6c, 0x2d, 0xe2, 0x6f, 0xad, 0x0f, 0x3a, 0x20, 0x03, 0x76, 0xd4, 0xc1,
0x65, 0xda, 0x7d, 0xbd, 0xa4, 0x4e, 0x5e, 0x77, 0xf1, 0xb2, 0xd8, 0xd0, 0x38, 0xab, 0xdb, 0xb6, 0xc8, 0x1b, 0xab, 0xd5, 0xf9, 0x81, 0xb4, 0x6d, 0xc7, 0x6e, 0xf7, 0xdb, 0x7a, 0x09, 0xed, 0x80,
0xd9, 0x26, 0x4d, 0xab, 0x57, 0x7f, 0xd3, 0x36, 0x9b, 0xfa, 0x26, 0x3a, 0x84, 0x27, 0x7d, 0xb3, 0xde, 0xb4, 0x2c, 0x62, 0x3b, 0xdd, 0x7e, 0xb3, 0x69, 0xd7, 0x6d, 0xcb, 0xe9, 0xe9, 0x65, 0xe5,
0x73, 0xd1, 0xc5, 0x75, 0xfc, 0x81, 0xc4, 0xf2, 0x56, 0xdd, 0x6a, 0x0f, 0xb0, 0xa9, 0x6f, 0xa1, 0x79, 0xd5, 0x87, 0x57, 0xc4, 0x86, 0xfa, 0x59, 0xcd, 0x71, 0xac, 0x16, 0x69, 0xd8, 0xdd, 0xda,
0x2f, 0xe0, 0x10, 0x9b, 0xef, 0x06, 0x16, 0x36, 0x9b, 0xc4, 0xee, 0x36, 0x4d, 0xd2, 0x32, 0xeb, 0x9b, 0x96, 0xd5, 0xd0, 0x37, 0xd0, 0x01, 0x3c, 0xee, 0x59, 0xed, 0x8b, 0x0e, 0xae, 0xe1, 0x0f,
0xfd, 0x01, 0x36, 0x49, 0xc7, 0xea, 0xf5, 0x2c, 0xfb, 0x7b, 0x5d, 0x47, 0xcf, 0xe1, 0x68, 0xae, 0x24, 0x91, 0x37, 0x6b, 0x76, 0xab, 0x8f, 0x2d, 0x7d, 0x13, 0x7d, 0x09, 0x07, 0xd8, 0x7a, 0xd7,
0x32, 0x37, 0xb0, 0xa2, 0xb5, 0x2d, 0xee, 0x17, 0xa7, 0xd4, 0x36, 0x7f, 0xec, 0x93, 0x0b, 0xd3, 0xb7, 0xb1, 0xd5, 0x20, 0x4e, 0xa7, 0x61, 0x91, 0xa6, 0x55, 0xeb, 0xf5, 0xb1, 0x45, 0xda, 0x76,
0xc4, 0x3a, 0x42, 0x55, 0xd8, 0x5b, 0x1c, 0xaf, 0x0e, 0x88, 0xce, 0xde, 0x11, 0xb2, 0x0b, 0x13, 0xb7, 0x6b, 0x3b, 0xdf, 0xeb, 0x3a, 0x7a, 0x06, 0x87, 0x33, 0x95, 0x99, 0x81, 0x25, 0xad, 0x2d,
0x77, 0xea, 0xb6, 0x48, 0xf0, 0x92, 0x6c, 0x57, 0xb8, 0xbd, 0x90, 0xad, 0xba, 0xfd, 0x08, 0x21, 0xf1, 0x7d, 0x49, 0x4a, 0x1d, 0xeb, 0xc7, 0x1e, 0xb9, 0xb0, 0x2c, 0xac, 0x23, 0x54, 0x85, 0xdd,
0xd8, 0x4c, 0x64, 0xa5, 0x55, 0xc7, 0xfa, 0x1e, 0xda, 0x85, 0xad, 0xd8, 0x83, 0x58, 0xf1, 0xdf, 0xb9, 0x7b, 0xe5, 0x20, 0xf6, 0xbd, 0x2d, 0x64, 0x17, 0x16, 0x6e, 0xd7, 0x1c, 0x91, 0xe0, 0x05,
0x39, 0xf4, 0x18, 0xd0, 0xc0, 0xc6, 0x66, 0xbd, 0x29, 0x02, 0x32, 0x17, 0xfc, 0x27, 0x77, 0x9e, 0xd9, 0x8e, 0x38, 0xf6, 0x5c, 0xb6, 0x7c, 0xec, 0xcf, 0x11, 0x82, 0x8d, 0x54, 0x56, 0x9a, 0x35,
0xc9, 0xa7, 0xf4, 0xb4, 0xf1, 0xcf, 0x34, 0x94, 0x97, 0xfa, 0x12, 0x1d, 0x40, 0x21, 0x74, 0xae, 0xac, 0xef, 0xa2, 0x1d, 0xd8, 0x4c, 0x4e, 0x90, 0x28, 0xfe, 0x2b, 0x8f, 0x1e, 0x01, 0xea, 0x3b,
0x3d, 0xca, 0x05, 0x73, 0x28, 0x52, 0x59, 0x00, 0xf2, 0x6d, 0x1c, 0x53, 0xc7, 0x53, 0x6c, 0xa6, 0xd8, 0xaa, 0x35, 0x44, 0x40, 0x66, 0x82, 0x7f, 0xe7, 0xcf, 0xb3, 0x85, 0x35, 0x3d, 0x63, 0xfe,
0xd8, 0xbc, 0x20, 0x11, 0xc9, 0x65, 0xfb, 0x90, 0x8b, 0xdf, 0xd7, 0xf4, 0xfc, 0x7d, 0xdd, 0x18, 0x33, 0x03, 0x95, 0x85, 0xbe, 0x44, 0xfb, 0x50, 0x8c, 0xdc, 0x6b, 0x9f, 0x72, 0xc1, 0x1c, 0x8a,
0xaa, 0x77, 0xf5, 0x00, 0x0a, 0x82, 0x32, 0x43, 0x4e, 0x27, 0x53, 0xd9, 0xe2, 0x65, 0xbc, 0x00, 0x54, 0xe6, 0x80, 0xbc, 0x1b, 0x47, 0xd4, 0xf5, 0x15, 0x9b, 0x29, 0x36, 0x2f, 0x4a, 0x44, 0x72,
0xd0, 0x33, 0x28, 0x4f, 0x58, 0x18, 0xd2, 0x6b, 0x46, 0x54, 0x9b, 0x82, 0xd4, 0x28, 0x45, 0x60, 0xd9, 0x1e, 0xe4, 0x93, 0x7b, 0x38, 0x33, 0xbb, 0x87, 0xd7, 0x07, 0xea, 0xfe, 0xdd, 0x87, 0xa2,
0x4b, 0x76, 0xeb, 0x33, 0x88, 0x69, 0x23, 0x52, 0xca, 0x2a, 0xa5, 0x08, 0x54, 0x4a, 0xab, 0x8c, 0xa0, 0xcc, 0x88, 0xd3, 0xf1, 0x44, 0xb6, 0x78, 0x05, 0xcf, 0x01, 0xf4, 0x14, 0x2a, 0x63, 0x16,
0xcd, 0x69, 0xc4, 0x06, 0x49, 0xc6, 0xe6, 0x14, 0xbd, 0x80, 0x6d, 0x45, 0x39, 0x8e, 0xe7, 0x4c, 0x45, 0xf4, 0x9a, 0x11, 0xd5, 0xa6, 0x20, 0x35, 0xca, 0x31, 0xd8, 0x94, 0xdd, 0xfa, 0x14, 0x12,
0x66, 0x13, 0x45, 0x3d, 0x39, 0x49, 0x3d, 0x5b, 0x92, 0x7a, 0x14, 0x2e, 0x19, 0xe8, 0x09, 0xe4, 0xda, 0x88, 0x95, 0x72, 0x4a, 0x29, 0x06, 0x95, 0xd2, 0x32, 0x63, 0x73, 0x1a, 0xb3, 0x41, 0x9a,
0x2f, 0x69, 0xc8, 0xc4, 0x63, 0x11, 0x51, 0x43, 0x4e, 0xac, 0x5b, 0x8c, 0x09, 0x91, 0x78, 0x42, 0xb1, 0x39, 0x45, 0x2f, 0x60, 0x4b, 0x51, 0x8e, 0xeb, 0xbb, 0xe3, 0xe9, 0x58, 0x51, 0x4f, 0x5e,
0x02, 0x41, 0x7a, 0x8a, 0x11, 0x72, 0x57, 0x8c, 0x61, 0x11, 0xcb, 0xf9, 0x09, 0xf4, 0xd3, 0xe2, 0x52, 0xcf, 0xa6, 0xa4, 0x1e, 0x85, 0x4b, 0x06, 0x7a, 0x0c, 0x85, 0x4b, 0x1a, 0x31, 0x71, 0x59,
0x84, 0x62, 0xe2, 0x04, 0x85, 0xcb, 0x13, 0x5e, 0xc0, 0x36, 0xfb, 0xc4, 0x03, 0x4a, 0xfc, 0x29, 0xc4, 0xd4, 0x90, 0x17, 0xeb, 0x26, 0x63, 0x42, 0x24, 0xae, 0x90, 0x50, 0x90, 0x9e, 0x62, 0x84,
0xfd, 0x79, 0xc6, 0xc8, 0x88, 0x72, 0x2a, 0x07, 0xb6, 0x12, 0xde, 0x92, 0x82, 0xae, 0xc4, 0x9b, 0xfc, 0x15, 0x63, 0x58, 0xc4, 0x72, 0xe6, 0x81, 0x7e, 0x9a, 0x7b, 0x28, 0xa5, 0x3c, 0x28, 0x5c,
0x94, 0x53, 0xe3, 0x00, 0xaa, 0x98, 0x85, 0x8c, 0x77, 0x9c, 0x30, 0x74, 0x7c, 0xaf, 0xe1, 0x7b, 0x7a, 0x78, 0x01, 0x5b, 0xec, 0x13, 0x0f, 0x29, 0x09, 0x26, 0xf4, 0xe7, 0x29, 0x23, 0x43, 0xca,
0x3c, 0xf0, 0xdd, 0xe8, 0xcd, 0x31, 0x0e, 0x61, 0x7f, 0xad, 0x54, 0x3d, 0x1a, 0x62, 0xf3, 0xbb, 0xa9, 0x1c, 0xec, 0xca, 0x78, 0x53, 0x0a, 0x3a, 0x12, 0x6f, 0x50, 0x4e, 0xcd, 0x7d, 0xa8, 0x62,
0x19, 0x0b, 0x6e, 0xd7, 0x6f, 0x7e, 0x07, 0xfb, 0x6b, 0xa5, 0xd1, 0x8b, 0xf3, 0x12, 0xb2, 0x53, 0x16, 0x31, 0xde, 0x76, 0xa3, 0xc8, 0x0d, 0xfc, 0x7a, 0xe0, 0xf3, 0x30, 0xf0, 0xe2, 0x3b, 0xc7,
0xea, 0x04, 0x61, 0x25, 0x25, 0xa7, 0x98, 0xbd, 0xa5, 0xa7, 0xdf, 0x09, 0xce, 0x9c, 0x90, 0xfb, 0x3c, 0x80, 0xbd, 0x95, 0x52, 0x75, 0x69, 0x88, 0xcd, 0xef, 0xa6, 0x2c, 0xbc, 0x5d, 0xbd, 0xf9,
0xc1, 0x2d, 0x56, 0x4a, 0xe7, 0x99, 0xbc, 0xa6, 0xa7, 0x8c, 0x3f, 0x6b, 0x50, 0x4c, 0x08, 0x45, 0x1d, 0xec, 0xad, 0x94, 0xc6, 0x37, 0xce, 0x4b, 0xc8, 0x4d, 0xa8, 0x1b, 0x46, 0xc6, 0x9a, 0x9c,
0x1d, 0x78, 0xfe, 0x88, 0x91, 0xab, 0xc0, 0x9f, 0xc4, 0x15, 0x36, 0x07, 0x50, 0x05, 0x72, 0x72, 0x62, 0x76, 0x17, 0xae, 0x7e, 0x37, 0x3c, 0x73, 0x23, 0x1e, 0x84, 0xb7, 0x58, 0x29, 0x9d, 0x67,
0xc1, 0xfd, 0xa8, 0xbc, 0xe2, 0x25, 0xfa, 0x1a, 0x72, 0x63, 0x65, 0x42, 0x66, 0xa9, 0x58, 0xdb, 0x0b, 0x9a, 0xbe, 0x66, 0xfe, 0x59, 0x83, 0x52, 0x4a, 0x28, 0xea, 0xc0, 0x0f, 0x86, 0x8c, 0x5c,
0x59, 0x39, 0x5d, 0xc4, 0x06, 0xc7, 0x3a, 0xe7, 0x99, 0x7c, 0x5a, 0xcf, 0x9c, 0x67, 0xf2, 0x19, 0x85, 0xc1, 0x38, 0xa9, 0xb0, 0x19, 0x80, 0x0c, 0xc8, 0xcb, 0x05, 0x0f, 0xe2, 0xf2, 0x4a, 0x96,
0x3d, 0x7b, 0x9e, 0xc9, 0x67, 0xf5, 0x8d, 0xf3, 0x4c, 0x7e, 0x43, 0xcf, 0x19, 0x7f, 0xd5, 0x20, 0xe8, 0x6b, 0xc8, 0x8f, 0x94, 0x09, 0x99, 0xa5, 0xd2, 0xe9, 0xf6, 0x92, 0x77, 0x11, 0x1b, 0x9c,
0x1f, 0x6b, 0x2f, 0xd7, 0xa4, 0x1a, 0x00, 0x12, 0x35, 0x59, 0x83, 0xdd, 0x89, 0xe3, 0x91, 0x29, 0xe8, 0x9c, 0x67, 0x0b, 0x19, 0x3d, 0x7b, 0x9e, 0x2d, 0x64, 0xf5, 0xdc, 0x79, 0xb6, 0x90, 0xd3,
0xf3, 0xa8, 0xeb, 0xfc, 0x91, 0x91, 0xe5, 0xf9, 0x62, 0xad, 0x0c, 0xbd, 0x86, 0xc7, 0x2e, 0x0d, 0xd7, 0xcf, 0xb3, 0x85, 0x75, 0x3d, 0x6f, 0xfe, 0x55, 0x83, 0x42, 0xa2, 0xbd, 0x58, 0x93, 0x6a,
0x39, 0xa1, 0x9c, 0xb3, 0xc9, 0x94, 0x93, 0x70, 0x36, 0x1c, 0xb2, 0x30, 0xbc, 0x9a, 0xb9, 0xb2, 0x00, 0x48, 0xd5, 0xe4, 0x29, 0xec, 0x8c, 0x5d, 0x9f, 0x4c, 0x98, 0x4f, 0x3d, 0xf7, 0x8f, 0x8c,
0x25, 0xf2, 0xf8, 0x3e, 0xb1, 0x31, 0x81, 0xc7, 0x32, 0xf4, 0x17, 0x81, 0x7f, 0x49, 0x2f, 0x1d, 0x2c, 0xce, 0x17, 0x2b, 0x65, 0xe8, 0x35, 0x3c, 0x92, 0x53, 0x26, 0xe5, 0x9c, 0x8d, 0x27, 0x9c,
0xd7, 0xe1, 0xb7, 0xf1, 0x18, 0x71, 0x00, 0x05, 0x11, 0x1c, 0xe2, 0xc5, 0xef, 0x72, 0x09, 0x2f, 0x44, 0xd3, 0xc1, 0x80, 0x45, 0xd1, 0xd5, 0xd4, 0x93, 0x2d, 0x51, 0xc0, 0xf7, 0x89, 0xcd, 0x31,
0x00, 0x11, 0x32, 0xee, 0x2b, 0x59, 0x14, 0xb2, 0x68, 0x29, 0x06, 0x84, 0xf9, 0xf0, 0x9f, 0x96, 0x3c, 0x92, 0xa1, 0xbf, 0x08, 0x83, 0x4b, 0x7a, 0xe9, 0x7a, 0x2e, 0xbf, 0x4d, 0xc6, 0x88, 0x7d,
0x4e, 0xcf, 0xd7, 0xc6, 0x0d, 0x54, 0xee, 0x1e, 0x17, 0xa5, 0xf9, 0x08, 0x8a, 0xd3, 0x05, 0x2c, 0x28, 0x8a, 0xe0, 0x10, 0x3f, 0xb9, 0x97, 0xcb, 0x78, 0x0e, 0x88, 0x90, 0xf1, 0x40, 0xc9, 0xe2,
0x4f, 0xd4, 0x70, 0x12, 0x4a, 0x26, 0x23, 0xf5, 0xcb, 0xc9, 0x30, 0xfe, 0xa6, 0xc1, 0xf6, 0x9b, 0x90, 0xc5, 0x4b, 0x31, 0x20, 0xcc, 0x1e, 0x09, 0x19, 0x79, 0xe8, 0xd9, 0xda, 0xbc, 0x01, 0xe3,
0x99, 0xe3, 0x8e, 0x96, 0xa6, 0xa3, 0xe4, 0xb7, 0x89, 0xb6, 0xfc, 0x6d, 0xb2, 0xee, 0xc3, 0x23, 0xae, 0xbb, 0x38, 0xcd, 0x87, 0x50, 0x9a, 0xcc, 0x61, 0xe9, 0x51, 0xc3, 0x69, 0x28, 0x9d, 0x8c,
0xb5, 0xf6, 0xc3, 0x63, 0xdd, 0x70, 0x9f, 0xbe, 0x77, 0xb8, 0x7f, 0x0a, 0xc5, 0xb1, 0x3f, 0x25, 0xb5, 0x5f, 0x4e, 0x86, 0xf9, 0x37, 0x0d, 0xb6, 0xde, 0x4c, 0x5d, 0x6f, 0xb8, 0x30, 0x1d, 0xa5,
0xd3, 0xd9, 0xe5, 0x0d, 0xbb, 0x0d, 0x2b, 0x99, 0xa3, 0xf4, 0x71, 0x09, 0xc3, 0xd8, 0x9f, 0x5e, 0xdf, 0x30, 0xda, 0xe2, 0x1b, 0x66, 0xd5, 0x03, 0x65, 0x6d, 0xe5, 0x03, 0x65, 0xd5, 0x23, 0x20,
0x28, 0xc4, 0x78, 0x0d, 0x28, 0xe9, 0x68, 0x14, 0x90, 0xf9, 0x90, 0xa6, 0xdd, 0x3b, 0xa4, 0xbd, 0x73, 0xef, 0x23, 0xe0, 0x09, 0x94, 0xe6, 0xf3, 0x7f, 0x64, 0x64, 0x0f, 0x33, 0x47, 0x65, 0x0c,
0xf8, 0x8b, 0x06, 0xa5, 0xe4, 0xfc, 0x8b, 0xca, 0x50, 0xb0, 0x6c, 0xd2, 0x6a, 0x5b, 0xdf, 0x9f, 0xa3, 0x64, 0xf8, 0x8f, 0xcc, 0xd7, 0x80, 0xd2, 0x07, 0x8d, 0x03, 0x32, 0x1b, 0xd2, 0xb4, 0x7b,
0xf5, 0xf5, 0xcf, 0xc4, 0xb2, 0x37, 0x68, 0x34, 0x4c, 0xb3, 0x69, 0x36, 0x75, 0x4d, 0x70, 0xb8, 0x87, 0xb4, 0x17, 0x7f, 0xd1, 0xa0, 0x9c, 0x9e, 0x7f, 0x51, 0x05, 0x8a, 0xb6, 0x43, 0x9a, 0x2d,
0xa0, 0x63, 0xb3, 0x49, 0xfa, 0x56, 0xc7, 0xec, 0x0e, 0xc4, 0xeb, 0xbe, 0x03, 0x5b, 0x11, 0x66, 0xfb, 0xfb, 0xb3, 0x9e, 0xfe, 0x99, 0x58, 0x76, 0xfb, 0xf5, 0xba, 0x65, 0x35, 0xac, 0x86, 0xae,
0x77, 0x09, 0xee, 0x0e, 0xfa, 0xa6, 0x9e, 0x46, 0x3a, 0x94, 0x22, 0xd0, 0xc4, 0xb8, 0x8b, 0xf5, 0x09, 0x0e, 0x17, 0x74, 0x6c, 0x35, 0x48, 0xcf, 0x6e, 0x5b, 0x9d, 0xbe, 0xb8, 0xdd, 0xb7, 0x61,
0x8c, 0x78, 0x92, 0x22, 0xe4, 0xee, 0xa4, 0x10, 0x0f, 0x12, 0xd9, 0xda, 0x3f, 0xb2, 0xb0, 0x21, 0x33, 0xc6, 0x9c, 0x0e, 0xc1, 0x9d, 0x7e, 0xcf, 0xd2, 0x33, 0x48, 0x87, 0x72, 0x0c, 0x5a, 0x18,
0x1d, 0x0c, 0xd0, 0x19, 0x14, 0x13, 0x1f, 0x19, 0xe8, 0xf0, 0xc1, 0x8f, 0x8f, 0x6a, 0x65, 0xfd, 0x77, 0xb0, 0x9e, 0x15, 0x57, 0x52, 0x8c, 0xdc, 0x9d, 0x14, 0x92, 0x41, 0x22, 0x77, 0xfa, 0x8f,
0x40, 0x3f, 0x0b, 0x5f, 0x69, 0xe8, 0x1c, 0x4a, 0xc9, 0xcf, 0x08, 0x94, 0x1c, 0x0f, 0xd7, 0x7c, 0x1c, 0xac, 0xcb, 0x03, 0x86, 0xe8, 0x0c, 0x4a, 0xa9, 0x47, 0x06, 0x3a, 0x78, 0xf0, 0xf1, 0x51,
0x5f, 0x3c, 0x68, 0xeb, 0x2d, 0xe8, 0x66, 0xc8, 0x9d, 0x89, 0x18, 0x07, 0xa3, 0x01, 0x1d, 0x55, 0x35, 0x56, 0x0f, 0xf4, 0xd3, 0xe8, 0x95, 0x86, 0xce, 0xa1, 0x9c, 0x7e, 0x46, 0xa0, 0xf4, 0x78,
0x13, 0xfa, 0x2b, 0x53, 0x7f, 0x75, 0x7f, 0xad, 0x2c, 0xca, 0x50, 0x5b, 0x5d, 0x31, 0x1a, 0x91, 0xb8, 0xe2, 0x7d, 0xf1, 0xa0, 0xad, 0xb7, 0xa0, 0x5b, 0x11, 0x77, 0xc7, 0x62, 0x1c, 0x8c, 0x07,
0xef, 0x5c, 0x71, 0x79, 0x2e, 0xaf, 0x7e, 0x7e, 0x9f, 0x38, 0xb2, 0x36, 0x82, 0x9d, 0x35, 0x1c, 0x74, 0x54, 0x4d, 0xe9, 0x2f, 0x4d, 0xfd, 0xd5, 0xbd, 0x95, 0xb2, 0x38, 0x43, 0x2d, 0xf5, 0x89,
0x8a, 0x7e, 0x95, 0xf4, 0xe0, 0x5e, 0x06, 0xae, 0x7e, 0xf9, 0x4b, 0x6a, 0x8b, 0x53, 0xd6, 0x90, 0xf1, 0x88, 0x7c, 0xe7, 0x13, 0x17, 0xe7, 0xf2, 0xea, 0x17, 0xf7, 0x89, 0x63, 0x6b, 0x43, 0xd8,
0xed, 0xd2, 0x29, 0xf7, 0x53, 0xf5, 0xd2, 0x29, 0x0f, 0x71, 0xf6, 0x4f, 0xa0, 0xaf, 0x36, 0x3a, 0x5e, 0xc1, 0xa1, 0xe8, 0x57, 0xe9, 0x13, 0xdc, 0xcb, 0xc0, 0xd5, 0xe7, 0xbf, 0xa4, 0x36, 0xf7,
0x32, 0x56, 0xf7, 0xde, 0x25, 0x9d, 0xea, 0xb3, 0x07, 0x75, 0x22, 0xe3, 0x16, 0xc0, 0xa2, 0x5d, 0xb2, 0x82, 0x6c, 0x17, 0xbc, 0xdc, 0x4f, 0xd5, 0x0b, 0x5e, 0x1e, 0xe2, 0xec, 0x9f, 0x40, 0x5f,
0xd0, 0x41, 0x62, 0xcb, 0x9d, 0x76, 0xaf, 0x1e, 0xde, 0x23, 0x55, 0xa6, 0xde, 0xfc, 0xfa, 0xf7, 0x6e, 0x74, 0x64, 0x2e, 0xef, 0xbd, 0x4b, 0x3a, 0xd5, 0xa7, 0x0f, 0xea, 0xc4, 0xc6, 0x6d, 0x80,
0xa7, 0xd7, 0x0e, 0x1f, 0xcf, 0x2e, 0x4f, 0x86, 0xfe, 0xe4, 0xd4, 0x15, 0x43, 0xb5, 0xe7, 0x78, 0x79, 0xbb, 0xa0, 0xfd, 0xd4, 0x96, 0x3b, 0xed, 0x5e, 0x3d, 0xb8, 0x47, 0xaa, 0x4c, 0xbd, 0xf9,
0xd7, 0x1e, 0xe3, 0x7f, 0xf0, 0x83, 0x9b, 0x53, 0xd7, 0x1b, 0x9d, 0xca, 0xae, 0x3b, 0x9d, 0x5b, 0xf5, 0xef, 0x4f, 0xae, 0x5d, 0x3e, 0x9a, 0x5e, 0x1e, 0x0f, 0x82, 0xf1, 0x89, 0x27, 0x86, 0x6a,
0xb9, 0xdc, 0x90, 0xff, 0x20, 0xf9, 0xcd, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x21, 0xc7, 0x78, 0xdf, 0xf5, 0xaf, 0x7d, 0xc6, 0xff, 0x10, 0x84, 0x37, 0x27, 0x9e, 0x3f, 0x3c, 0x91, 0x5d, 0x77,
0xa5, 0x50, 0x11, 0x00, 0x00, 0x32, 0xb3, 0x72, 0xb9, 0x2e, 0xff, 0x91, 0xf2, 0x9b, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x1f,
0xb3, 0x05, 0x24, 0x78, 0x11, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
+5
View File
@@ -77,6 +77,11 @@ message SendPaymentRequest {
*/ */
uint64 outgoing_chan_id = 8 [jstype = JS_STRING]; uint64 outgoing_chan_id = 8 [jstype = JS_STRING];
/**
The pubkey of the last hop of the route. If empty, any hop may be used.
*/
bytes last_hop_pubkey = 14;
/** /**
An optional maximum total time lock for the route. This should not exceed An optional maximum total time lock for the route. This should not exceed
lnd's `--max-cltv-expiry` setting. If zero, then the value of lnd's `--max-cltv-expiry` setting. If zero, then the value of
+11
View File
@@ -497,6 +497,17 @@ func (r *RouterBackend) extractIntentFromSendRequest(
payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId
} }
// Pass along a last hop restriction if specified.
if len(rpcPayReq.LastHopPubkey) > 0 {
lastHop, err := route.NewVertexFromBytes(
rpcPayReq.LastHopPubkey,
)
if err != nil {
return nil, err
}
payIntent.LastHop = &lastHop
}
// Take the CLTV limit from the request if set, otherwise use the max. // Take the CLTV limit from the request if set, otherwise use the max.
cltvLimit, err := ValidateCLTVLimit( cltvLimit, err := ValidateCLTVLimit(
uint32(rpcPayReq.CltvLimit), r.MaxTotalTimelock, uint32(rpcPayReq.CltvLimit), r.MaxTotalTimelock,
+557 -546
View File
File diff suppressed because it is too large. Load diff
+5
View File
@@ -937,6 +937,11 @@ message SendRequest {
*/ */
uint64 outgoing_chan_id = 9 [jstype = JS_STRING]; uint64 outgoing_chan_id = 9 [jstype = JS_STRING];
/**
The pubkey of the last hop of the route. If empty, any hop may be used.
*/
bytes last_hop_pubkey = 13;
/** /**
An optional maximum total time lock for the route. This should not exceed An optional maximum total time lock for the route. This should not exceed
lnd's `--max-cltv-expiry` setting. If zero, then the value of lnd's `--max-cltv-expiry` setting. If zero, then the value of
+5
View File
@@ -3661,6 +3661,11 @@
"format": "uint64", "format": "uint64",
"description": "*\nThe channel id of the channel that must be taken to the first hop. If zero,\nany channel may be used." "description": "*\nThe channel id of the channel that must be taken to the first hop. If zero,\nany channel may be used."
}, },
"last_hop_pubkey": {
"type": "string",
"format": "byte",
"description": "*\nThe pubkey of the last hop of the route. If empty, any hop may be used."
},
"cltv_limit": { "cltv_limit": {
"type": "integer", "type": "integer",
"format": "int64", "format": "int64",
+11
View File
@@ -263,6 +263,10 @@ type RestrictParams struct {
// hop. If nil, any channel may be used. // hop. If nil, any channel may be used.
OutgoingChannelID *uint64 OutgoingChannelID *uint64
// LastHop is the pubkey of the last node before the final destination
// is reached. If nil, any node may be used.
LastHop *route.Vertex
// CltvLimit is the maximum time lock of the route excluding the final // CltvLimit is the maximum time lock of the route excluding the final
// ctlv. After path finding is complete, the caller needs to increase // ctlv. After path finding is complete, the caller needs to increase
// all cltv expiry heights with the required final cltv delta. // all cltv expiry heights with the required final cltv delta.
@@ -562,6 +566,13 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// Expand all connections using the optimal policy for each // Expand all connections using the optimal policy for each
// connection. // connection.
for fromNode, unifiedPolicy := range u.policies { for fromNode, unifiedPolicy := range u.policies {
// Apply last hop restriction if set.
if r.LastHop != nil &&
pivot == target && fromNode != *r.LastHop {
continue
}
policy := unifiedPolicy.getPolicy( policy := unifiedPolicy.getPolicy(
amtToSend, g.bandwidthHints, amtToSend, g.bandwidthHints,
) )
+157 -153
View File
@@ -622,19 +622,8 @@ func TestFindLowestFeePath(t *testing.T) {
}), }),
} }
testGraphInstance, err := createTestGraphFromChannels( ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
testChannels, "roasbeef", defer ctx.cleanup()
)
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraphInstance.cleanUp()
sourceNode, err := testGraphInstance.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
const ( const (
startingHeight = 100 startingHeight = 100
@@ -642,20 +631,13 @@ func TestFindLowestFeePath(t *testing.T) {
) )
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := testGraphInstance.aliasMap["target"] target := ctx.keyFromAlias("target")
path, err := findPath( path, err := ctx.findPath(target, paymentAmt)
&graphParams{
graph: testGraphInstance.graph,
},
noRestrictions,
testPathFindingConfig,
sourceNode.PubKeyBytes, 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( route, err := newRoute(
paymentAmt, sourceVertex, path, startingHeight, paymentAmt, ctx.source, path, startingHeight,
finalHopCLTV, nil, finalHopCLTV, nil,
) )
if err != nil { if err != nil {
@@ -663,11 +645,10 @@ func TestFindLowestFeePath(t *testing.T) {
} }
// Assert that the lowest fee route is returned. // Assert that the lowest fee route is returned.
if route.Hops[1].PubKeyBytes != testGraphInstance.aliasMap["b"] { if route.Hops[1].PubKeyBytes != ctx.keyFromAlias("b") {
t.Fatalf("expected route to pass through b, "+ t.Fatalf("expected route to pass through b, "+
"but got a route through %v", "but got a route through %v",
getAliasFromPubKey(route.Hops[1].PubKeyBytes, ctx.aliasFromKey(route.Hops[1].PubKeyBytes))
testGraphInstance.aliasMap))
} }
} }
@@ -1394,53 +1375,34 @@ func TestRouteFailMaxHTLC(t *testing.T) {
}), }),
} }
graph, err := createTestGraphFromChannels(testChannels, "roasbeef") ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
if err != nil { defer ctx.cleanup()
t.Fatalf("unable to create graph: %v", err)
}
defer graph.cleanUp()
sourceNode, err := graph.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
// First, attempt to send a payment greater than the max HTLC we are // First, attempt to send a payment greater than the max HTLC we are
// about to set, which should succeed. // about to set, which should succeed.
target := graph.aliasMap["target"] target := ctx.keyFromAlias("target")
payAmt := lnwire.MilliSatoshi(100001) payAmt := lnwire.MilliSatoshi(100001)
_, err = findPath( _, err := ctx.findPath(target, payAmt)
&graphParams{
graph: graph.graph,
},
noRestrictions, testPathFindingConfig,
sourceNode.PubKeyBytes, target, payAmt,
)
if err != nil { if err != nil {
t.Fatalf("graph should've been able to support payment: %v", err) t.Fatalf("graph should've been able to support payment: %v", err)
} }
// Next, update the middle edge policy to only allow payments up to 100k // Next, update the middle edge policy to only allow payments up to 100k
// msat. // msat.
_, midEdge, _, err := graph.graph.FetchChannelEdgesByID(firstToSecondID) graph := ctx.testGraphInstance.graph
_, midEdge, _, err := graph.FetchChannelEdgesByID(firstToSecondID)
if err != nil { if err != nil {
t.Fatalf("unable to fetch channel edges by ID: %v", err) t.Fatalf("unable to fetch channel edges by ID: %v", err)
} }
midEdge.MessageFlags = 1 midEdge.MessageFlags = 1
midEdge.MaxHTLC = payAmt - 1 midEdge.MaxHTLC = payAmt - 1
if err := graph.graph.UpdateEdgePolicy(midEdge); err != nil { if err := graph.UpdateEdgePolicy(midEdge); err != nil {
t.Fatalf("unable to update edge: %v", err) t.Fatalf("unable to update edge: %v", err)
} }
// We'll now attempt to route through that edge with a payment above // We'll now attempt to route through that edge with a payment above
// 100k msat, which should fail. // 100k msat, which should fail.
_, err = findPath( _, err = ctx.findPath(target, payAmt)
&graphParams{
graph: graph.graph,
},
noRestrictions, testPathFindingConfig,
sourceNode.PubKeyBytes, target, payAmt,
)
if !IsError(err, ErrNoPathFound) { if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err) t.Fatalf("graph shouldn't be able to support payment: %v", err)
} }
@@ -1859,45 +1821,26 @@ func TestRestrictOutgoingChannel(t *testing.T) {
// target. The path through channel 2 is the highest cost path. // target. The path through channel 2 is the highest cost path.
testChannels := []*testChannel{ testChannels := []*testChannel{
symmetricTestChannel("roasbeef", "a", 100000, &testChannelPolicy{ symmetricTestChannel("roasbeef", "a", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
}, 1), }, 1),
symmetricTestChannel("a", "target", 100000, &testChannelPolicy{ symmetricTestChannel("a", "target", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
FeeRate: 400, FeeRate: 400,
MinHTLC: 1, }, 4),
}),
symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{ symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
FeeRate: 800,
MinHTLC: 1,
}, 2), }, 2),
symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{ symmetricTestChannel("roasbeef", "b", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
FeeRate: 600,
MinHTLC: 1,
}, 3), }, 3),
symmetricTestChannel("b", "target", 100000, &testChannelPolicy{ symmetricTestChannel("b", "target", 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
FeeRate: 400, FeeRate: 800,
MinHTLC: 1, }, 5),
}),
} }
testGraphInstance, err := createTestGraphFromChannels( ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
testChannels, "roasbeef", defer ctx.cleanup()
)
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraphInstance.cleanUp()
sourceNode, err := testGraphInstance.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
const ( const (
startingHeight = 100 startingHeight = 100
@@ -1905,29 +1848,18 @@ func TestRestrictOutgoingChannel(t *testing.T) {
) )
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := testGraphInstance.aliasMap["target"] target := ctx.keyFromAlias("target")
outgoingChannelID := uint64(2) outgoingChannelID := uint64(2)
// 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.
path, err := findPath( ctx.restrictParams.OutgoingChannelID = &outgoingChannelID
&graphParams{ path, err := ctx.findPath(target, paymentAmt)
graph: testGraphInstance.graph,
},
&RestrictParams{
FeeLimit: noFeeLimit,
OutgoingChannelID: &outgoingChannelID,
ProbabilitySource: noProbabilitySource,
CltvLimit: math.MaxUint32,
},
testPathFindingConfig,
sourceVertex, 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( route, err := newRoute(
paymentAmt, sourceVertex, path, startingHeight, paymentAmt, ctx.source, path, startingHeight,
finalHopCLTV, nil, finalHopCLTV, nil,
) )
if err != nil { if err != nil {
@@ -1942,6 +1874,51 @@ func TestRestrictOutgoingChannel(t *testing.T) {
} }
} }
// TestRestrictLastHop asserts that a last hop restriction is obeyed by the path
// finding algorithm.
func TestRestrictLastHop(t *testing.T) {
t.Parallel()
// Set up a test graph with three possible paths from roasbeef to
// target. The path via channel 1 and 2 is the lowest cost path.
testChannels := []*testChannel{
symmetricTestChannel("source", "a", 100000, &testChannelPolicy{
Expiry: 144,
}, 1),
symmetricTestChannel("a", "target", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
}, 2),
symmetricTestChannel("source", "b", 100000, &testChannelPolicy{
Expiry: 144,
}, 3),
symmetricTestChannel("b", "target", 100000, &testChannelPolicy{
Expiry: 144,
FeeRate: 800,
}, 4),
}
ctx := newPathFindingTestContext(t, testChannels, "source")
defer ctx.cleanup()
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := ctx.keyFromAlias("target")
lastHop := ctx.keyFromAlias("b")
// Find the best path given the restriction to use b as the last hop.
// This should force pathfinding to not take the lowest cost option.
ctx.restrictParams.LastHop = &lastHop
path, err := ctx.findPath(target, paymentAmt)
if err != nil {
t.Fatalf("unable to find path: %v", err)
}
if path[0].ChannelID != 3 {
t.Fatalf("expected route to pass through channel 3, "+
"but channel %v was selected instead",
path[0].ChannelID)
}
}
// TestCltvLimit asserts that a cltv limit is obeyed by the path finding // TestCltvLimit asserts that a cltv limit is obeyed by the path finding
// algorithm. // algorithm.
func TestCltvLimit(t *testing.T) { func TestCltvLimit(t *testing.T) {
@@ -1984,35 +1961,14 @@ func testCltvLimit(t *testing.T, limit uint32, expectedChannel uint64) {
}), }),
} }
testGraphInstance, err := createTestGraphFromChannels( ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
testChannels, "roasbeef", defer ctx.cleanup()
)
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraphInstance.cleanUp()
sourceNode, err := testGraphInstance.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := testGraphInstance.aliasMap["target"] target := ctx.keyFromAlias("target")
path, err := findPath( ctx.restrictParams.CltvLimit = limit
&graphParams{ path, err := ctx.findPath(target, paymentAmt)
graph: testGraphInstance.graph,
},
&RestrictParams{
FeeLimit: noFeeLimit,
CltvLimit: limit,
ProbabilitySource: noProbabilitySource,
},
testPathFindingConfig,
sourceVertex, target, paymentAmt,
)
if expectedChannel == 0 { if expectedChannel == 0 {
// Finish test if we expect no route. // Finish test if we expect no route.
if IsError(err, ErrNoPathFound) { if IsError(err, ErrNoPathFound) {
@@ -2029,7 +1985,7 @@ func testCltvLimit(t *testing.T, limit uint32, expectedChannel uint64) {
finalHopCLTV = 1 finalHopCLTV = 1
) )
route, err := newRoute( route, err := newRoute(
paymentAmt, sourceVertex, path, startingHeight, finalHopCLTV, paymentAmt, ctx.source, path, startingHeight, finalHopCLTV,
nil, nil,
) )
if err != nil { if err != nil {
@@ -2146,27 +2102,16 @@ func testProbabilityRouting(t *testing.T, p10, p11, p20, minProbability float64,
}, 20), }, 20),
} }
testGraphInstance, err := createTestGraphFromChannels( ctx := newPathFindingTestContext(t, testChannels, "roasbeef")
testChannels, "roasbeef", defer ctx.cleanup()
)
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraphInstance.cleanUp()
alias := testGraphInstance.aliasMap alias := ctx.testGraphInstance.aliasMap
sourceNode, err := testGraphInstance.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := testGraphInstance.aliasMap["target"] target := ctx.testGraphInstance.aliasMap["target"]
// Configure a probability source with the test parameters. // Configure a probability source with the test parameters.
probabilitySource := func(fromNode, toNode route.Vertex, ctx.restrictParams.ProbabilitySource = func(fromNode, toNode route.Vertex,
amt lnwire.MilliSatoshi) float64 { amt lnwire.MilliSatoshi) float64 {
if amt == 0 { if amt == 0 {
@@ -2185,21 +2130,12 @@ func testProbabilityRouting(t *testing.T, p10, p11, p20, minProbability float64,
} }
} }
path, err := findPath( ctx.pathFindingConfig = PathFindingConfig{
&graphParams{ PaymentAttemptPenalty: lnwire.NewMSatFromSatoshis(10),
graph: testGraphInstance.graph, MinProbability: minProbability,
}, }
&RestrictParams{
FeeLimit: noFeeLimit, path, err := ctx.findPath(target, paymentAmt)
ProbabilitySource: probabilitySource,
CltvLimit: math.MaxUint32,
},
&PathFindingConfig{
PaymentAttemptPenalty: lnwire.NewMSatFromSatoshis(10),
MinProbability: minProbability,
},
sourceVertex, target, paymentAmt,
)
if expectedChan == 0 { if expectedChan == 0 {
if err == nil || !IsError(err, ErrNoPathFound) { if err == nil || !IsError(err, ErrNoPathFound) {
t.Fatalf("expected no path found, but got %v", err) t.Fatalf("expected no path found, but got %v", err)
@@ -2217,3 +2153,71 @@ func testProbabilityRouting(t *testing.T, p10, p11, p20, minProbability float64,
path[1].ChannelID) path[1].ChannelID)
} }
} }
type pathFindingTestContext struct {
t *testing.T
graphParams graphParams
restrictParams RestrictParams
pathFindingConfig PathFindingConfig
testGraphInstance *testGraphInstance
source route.Vertex
}
func newPathFindingTestContext(t *testing.T, testChannels []*testChannel,
source string) *pathFindingTestContext {
testGraphInstance, err := createTestGraphFromChannels(
testChannels, source,
)
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
sourceNode, err := testGraphInstance.graph.SourceNode()
if err != nil {
t.Fatalf("unable to fetch source node: %v", err)
}
ctx := &pathFindingTestContext{
t: t,
testGraphInstance: testGraphInstance,
source: route.Vertex(sourceNode.PubKeyBytes),
}
ctx.pathFindingConfig = *testPathFindingConfig
ctx.graphParams.graph = testGraphInstance.graph
ctx.restrictParams.FeeLimit = noFeeLimit
ctx.restrictParams.ProbabilitySource = noProbabilitySource
ctx.restrictParams.CltvLimit = math.MaxUint32
return ctx
}
func (c *pathFindingTestContext) keyFromAlias(alias string) route.Vertex {
return c.testGraphInstance.aliasMap[alias]
}
func (c *pathFindingTestContext) aliasFromKey(pubKey route.Vertex) string {
for alias, key := range c.testGraphInstance.aliasMap {
if key == pubKey {
return alias
}
}
return ""
}
func (c *pathFindingTestContext) cleanup() {
c.testGraphInstance.cleanUp()
}
func (c *pathFindingTestContext) findPath(target route.Vertex,
amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy,
error) {
return findPath(
&c.graphParams, &c.restrictParams, &c.pathFindingConfig,
c.source, target, amt,
)
}
+1
View File
@@ -89,6 +89,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
ProbabilitySource: ss.MissionControl.GetProbability, ProbabilitySource: ss.MissionControl.GetProbability,
FeeLimit: payment.FeeLimit, FeeLimit: payment.FeeLimit,
OutgoingChannelID: payment.OutgoingChannelID, OutgoingChannelID: payment.OutgoingChannelID,
LastHop: payment.LastHop,
CltvLimit: cltvLimit, CltvLimit: cltvLimit,
} }
+4
View File
@@ -1596,6 +1596,10 @@ type LightningPayment struct {
// hop. If nil, any channel may be used. // hop. If nil, any channel may be used.
OutgoingChannelID *uint64 OutgoingChannelID *uint64
// LastHop is the pubkey of the last node before the final destination
// is reached. If nil, any node may be used.
LastHop *route.Vertex
// PaymentRequest is an optional payment request that this payment is // PaymentRequest is an optional payment request that this payment is
// attempting to complete. // attempting to complete.
PaymentRequest []byte PaymentRequest []byte
+13
View File
@@ -3017,6 +3017,7 @@ type rpcPaymentIntent struct {
cltvDelta uint16 cltvDelta uint16
routeHints [][]zpay32.HopHint routeHints [][]zpay32.HopHint
outgoingChannelID *uint64 outgoingChannelID *uint64
lastHop *route.Vertex
payReq []byte payReq []byte
destTLV []tlv.Record destTLV []tlv.Record
@@ -3058,6 +3059,17 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
payIntent.outgoingChannelID = &rpcPayReq.OutgoingChanId payIntent.outgoingChannelID = &rpcPayReq.OutgoingChanId
} }
// Pass along a last hop restriction if specified.
if len(rpcPayReq.LastHopPubkey) > 0 {
lastHop, err := route.NewVertexFromBytes(
rpcPayReq.LastHopPubkey,
)
if err != nil {
return payIntent, err
}
payIntent.lastHop = &lastHop
}
// Take the CLTV limit from the request if set, otherwise use the max. // Take the CLTV limit from the request if set, otherwise use the max.
cltvLimit, err := routerrpc.ValidateCLTVLimit( cltvLimit, err := routerrpc.ValidateCLTVLimit(
rpcPayReq.CltvLimit, cfg.MaxOutgoingCltvExpiry, rpcPayReq.CltvLimit, cfg.MaxOutgoingCltvExpiry,
@@ -3237,6 +3249,7 @@ func (r *rpcServer) dispatchPaymentIntent(
PaymentHash: payIntent.rHash, PaymentHash: payIntent.rHash,
RouteHints: payIntent.routeHints, RouteHints: payIntent.routeHints,
OutgoingChannelID: payIntent.outgoingChannelID, OutgoingChannelID: payIntent.outgoingChannelID,
LastHop: payIntent.lastHop,
PaymentRequest: payIntent.payReq, PaymentRequest: payIntent.payReq,
PayAttemptTimeout: routing.DefaultPayAttemptTimeout, PayAttemptTimeout: routing.DefaultPayAttemptTimeout,
FinalDestRecords: payIntent.destTLV, FinalDestRecords: payIntent.destTLV,