From b4c6c89d9bf3dc0d5de157464d55420ffcdde3fc Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 22 Mar 2019 09:00:16 +0100 Subject: [PATCH 1/6] lnrpc: extract send request parsing --- rpcserver.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rpcserver.go b/rpcserver.go index ea84f2cba52..9d5b171ab9f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2898,6 +2898,17 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error return payIntent, nil } + return extractIntentFromSendRequest(rpcPayReq.SendRequest) +} + +// extractIntentFromSendRequest attempts to parse the SendRequest details +// required to dispatch a client from the information presented by an RPC +// client. +func extractIntentFromSendRequest(rpcPayReq *lnrpc.SendRequest) ( + rpcPaymentIntent, error) { + + payIntent := rpcPaymentIntent{} + // If there are no routes specified, pass along a outgoing channel // restriction if specified. if rpcPayReq.OutgoingChanId != 0 { From 901593caf412429fc4192eab1010cfac7a55ecbe Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 22 Mar 2019 10:01:54 +0100 Subject: [PATCH 2/6] lnrpc: reuse LightningPayment in rpcPaymentIntent struct --- rpcserver.go | 95 ++++++++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index 9d5b171ab9f..087ab65831a 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2858,14 +2858,7 @@ func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest, // hints), or we'll get a fully populated route from the user that we'll pass // directly to the channel router for dispatching. type rpcPaymentIntent struct { - msat lnwire.MilliSatoshi - feeLimit lnwire.MilliSatoshi - cltvLimit *uint32 - dest routing.Vertex - rHash [32]byte - cltvDelta uint16 - routeHints [][]zpay32.HopHint - outgoingChannelID *uint64 + routing.LightningPayment routes []*routing.Route } @@ -2889,9 +2882,9 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error return payIntent, err } - copy(payIntent.rHash[:], paymentHash) + copy(payIntent.PaymentHash[:], paymentHash) } else { - copy(payIntent.rHash[:], rpcPayReq.PaymentHash) + copy(payIntent.PaymentHash[:], rpcPayReq.PaymentHash) } payIntent.routes = rpcPayReq.routes @@ -2912,12 +2905,12 @@ func extractIntentFromSendRequest(rpcPayReq *lnrpc.SendRequest) ( // If there are no routes specified, pass along a outgoing channel // restriction if specified. if rpcPayReq.OutgoingChanId != 0 { - payIntent.outgoingChannelID = &rpcPayReq.OutgoingChanId + payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId } // Take cltv limit from request if set. if rpcPayReq.CltvLimit != 0 { - payIntent.cltvLimit = &rpcPayReq.CltvLimit + payIntent.CltvLimit = &rpcPayReq.CltvLimit } // If the payment request field isn't blank, then the details of the @@ -2948,23 +2941,28 @@ func extractIntentFromSendRequest(rpcPayReq *lnrpc.SendRequest) ( "invoice") } - payIntent.msat = lnwire.NewMSatFromSatoshis( + payIntent.Amount = lnwire.NewMSatFromSatoshis( btcutil.Amount(rpcPayReq.Amt), ) } else { - payIntent.msat = *payReq.MilliSat + payIntent.Amount = *payReq.MilliSat } // Calculate the fee limit that should be used for this payment. - payIntent.feeLimit = calculateFeeLimit( - rpcPayReq.FeeLimit, payIntent.msat, + payIntent.FeeLimit = calculateFeeLimit( + rpcPayReq.FeeLimit, payIntent.Amount, ) - copy(payIntent.rHash[:], payReq.PaymentHash[:]) + copy(payIntent.PaymentHash[:], payReq.PaymentHash[:]) destKey := payReq.Destination.SerializeCompressed() - copy(payIntent.dest[:], destKey) - payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry()) - payIntent.routeHints = payReq.RouteHints + copy(payIntent.Target[:], destKey) + + cltvDelta := uint16(payReq.MinFinalCLTVExpiry()) + if cltvDelta != 0 { + payIntent.FinalCLTVDelta = &cltvDelta + } + + payIntent.RouteHints = payReq.RouteHints return payIntent, nil } @@ -2985,21 +2983,24 @@ func extractIntentFromSendRequest(rpcPayReq *lnrpc.SendRequest) ( if len(pubBytes) != 33 { return payIntent, errors.New("invalid key length") } - copy(payIntent.dest[:], pubBytes) + copy(payIntent.Target[:], pubBytes) // Otherwise, If the payment request field was not specified // (and a custom route wasn't specified), construct the payment // from the other fields. - payIntent.msat = lnwire.NewMSatFromSatoshis( + payIntent.Amount = lnwire.NewMSatFromSatoshis( btcutil.Amount(rpcPayReq.Amt), ) // Calculate the fee limit that should be used for this payment. - payIntent.feeLimit = calculateFeeLimit( - rpcPayReq.FeeLimit, payIntent.msat, + payIntent.FeeLimit = calculateFeeLimit( + rpcPayReq.FeeLimit, payIntent.Amount, ) - payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta) + cltvDelta := uint16(rpcPayReq.FinalCltvDelta) + if cltvDelta != 0 { + payIntent.FinalCLTVDelta = &cltvDelta + } // If the user is manually specifying payment details, then the payment // hash may be encoded as a string. @@ -3012,26 +3013,26 @@ func extractIntentFromSendRequest(rpcPayReq *lnrpc.SendRequest) ( return payIntent, err } - copy(payIntent.rHash[:], paymentHash) + copy(payIntent.PaymentHash[:], paymentHash) // If we're in debug HTLC mode, then all outgoing HTLCs will pay to the // same debug rHash. Otherwise, we pay to the rHash specified within // the RPC request. - case cfg.DebugHTLC && bytes.Equal(payIntent.rHash[:], zeroHash[:]): - copy(payIntent.rHash[:], invoices.DebugHash[:]) + case cfg.DebugHTLC && bytes.Equal(payIntent.PaymentHash[:], zeroHash[:]): + copy(payIntent.PaymentHash[:], invoices.DebugHash[:]) default: - copy(payIntent.rHash[:], rpcPayReq.PaymentHash) + copy(payIntent.PaymentHash[:], rpcPayReq.PaymentHash) } // Currently, within the bootstrap phase of the network, we limit the // largest payment size allotted to (2^32) - 1 mSAT or 4.29 million // satoshis. - if payIntent.msat > maxPaymentMSat { + if payIntent.Amount > maxPaymentMSat { // In this case, we'll send an error to the caller, but // continue our loop for the next payment. return payIntent, fmt.Errorf("payment of %v is too large, "+ - "max payment allowed is %v", payIntent.msat, + "max payment allowed is %v", payIntent.Amount, maxPaymentMSat) } @@ -3065,28 +3066,12 @@ func (r *rpcServer) dispatchPaymentIntent( // If a route was specified, then we'll pass the route directly to the // router, otherwise we'll create a payment session to execute it. if len(payIntent.routes) == 0 { - payment := &routing.LightningPayment{ - Target: payIntent.dest, - Amount: payIntent.msat, - FeeLimit: payIntent.feeLimit, - CltvLimit: payIntent.cltvLimit, - PaymentHash: payIntent.rHash, - RouteHints: payIntent.routeHints, - OutgoingChannelID: payIntent.outgoingChannelID, - } - - // If the final CLTV value was specified, then we'll use that - // rather than the default. - if payIntent.cltvDelta != 0 { - payment.FinalCLTVDelta = &payIntent.cltvDelta - } - preImage, route, routerErr = r.server.chanRouter.SendPayment( - payment, + &payIntent.LightningPayment, ) } else { payment := &routing.LightningPayment{ - PaymentHash: payIntent.rHash, + PaymentHash: payIntent.PaymentHash, } preImage, route, routerErr = r.server.chanRouter.SendToRoute( @@ -3108,7 +3093,7 @@ func (r *rpcServer) dispatchPaymentIntent( if len(payIntent.routes) > 0 { amt = route.TotalAmount - route.TotalFees } else { - amt = payIntent.msat + amt = payIntent.Amount } // Save the completed payment to the database for record keeping @@ -3200,7 +3185,7 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error { if err != nil { if err := stream.send(&lnrpc.SendResponse{ PaymentError: err.Error(), - PaymentHash: payIntent.rHash[:], + PaymentHash: payIntent.PaymentHash[:], }); err != nil { select { case errChan <- err: @@ -3259,7 +3244,7 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error { case resp.Err != nil: err := stream.send(&lnrpc.SendResponse{ PaymentError: resp.Err.Error(), - PaymentHash: payIntent.rHash[:], + PaymentHash: payIntent.PaymentHash[:], }) if err != nil { errChan <- err @@ -3271,7 +3256,7 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error { MarshallRoute(resp.Route) err := stream.send(&lnrpc.SendResponse{ - PaymentHash: payIntent.rHash[:], + PaymentHash: payIntent.PaymentHash[:], PaymentPreimage: resp.Preimage[:], PaymentRoute: marshalledRouted, }) @@ -3347,12 +3332,12 @@ func (r *rpcServer) sendPaymentSync(ctx context.Context, case resp.Err != nil: return &lnrpc.SendResponse{ PaymentError: resp.Err.Error(), - PaymentHash: payIntent.rHash[:], + PaymentHash: payIntent.PaymentHash[:], }, nil } return &lnrpc.SendResponse{ - PaymentHash: payIntent.rHash[:], + PaymentHash: payIntent.PaymentHash[:], PaymentPreimage: resp.Preimage[:], PaymentRoute: r.routerBackend.MarshallRoute(resp.Route), }, nil From f316f0cb4f964259caf0cffcf8234683d8c4b7f6 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 22 Mar 2019 09:41:05 +0100 Subject: [PATCH 3/6] routerrpc: move payment intent extraction into package --- lnrpc/routerrpc/config_active.go | 7 -- lnrpc/routerrpc/router_backend.go | 176 ++++++++++++++++++++++++++++++ lnrpc/routerrpc/router_server.go | 4 +- rpcserver.go | 175 ++--------------------------- subrpcserver_config.go | 3 - 5 files changed, 191 insertions(+), 174 deletions(-) diff --git a/lnrpc/routerrpc/config_active.go b/lnrpc/routerrpc/config_active.go index edf9803555a..345770390ff 100644 --- a/lnrpc/routerrpc/config_active.go +++ b/lnrpc/routerrpc/config_active.go @@ -3,7 +3,6 @@ package routerrpc import ( - "github.com/btcsuite/btcd/chaincfg" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/routing" ) @@ -23,12 +22,6 @@ type Config struct { // server will find the macaroon named DefaultRouterMacFilename. NetworkDir string - // ActiveNetParams are the network parameters of the primary network - // that the route is operating on. This is necessary so we can ensure - // that we receive payment requests that send to destinations on our - // network. - ActiveNetParams *chaincfg.Params - // MacService is the main macaroon service that we'll use to handle // authentication for the Router rpc server. MacService *macaroons.Service diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 6a1ffea3558..eac0db1907f 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -1,17 +1,26 @@ package routerrpc import ( + "bytes" "encoding/hex" "errors" "fmt" + "time" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" + "github.com/lightningnetwork/lnd/zpay32" context "golang.org/x/net/context" ) +var ( + zeroHash [32]byte +) + // RouterBackend contains the backend implementation of the router rpc sub // server calls. type RouterBackend struct { @@ -31,6 +40,16 @@ type RouterBackend struct { amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams, numPaths uint32, finalExpiry ...uint16) ( []*routing.Route, error) + + // Activate the debug htlc mode. With the debug HTLC mode, all payments + // sent use a pre-determined payment hash. + DebugHTLC bool + + // ActiveNetParams are the network parameters of the primary network + // that the route is operating on. This is necessary so we can ensure + // that we receive payment requests that send to destinations on our + // network. + ActiveNetParams *chaincfg.Params } // QueryRoutes attempts to query the daemons' Channel Router for a possible @@ -236,3 +255,160 @@ func (r *RouterBackend) MarshallRoute(route *routing.Route) *lnrpc.Route { return resp } + +// ExtractIntentFromSendRequest attempts to parse the SendRequest details +// required to dispatch a client from the information presented by an RPC +// client. +func (r *RouterBackend) ExtractIntentFromSendRequest(rpcPayReq *lnrpc.SendRequest) ( + *routing.LightningPayment, error) { + + payIntent := &routing.LightningPayment{} + + // If there are no routes specified, pass along a outgoing channel + // restriction if specified. + if rpcPayReq.OutgoingChanId != 0 { + payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId + } + + // Take cltv limit from request if set. + if rpcPayReq.CltvLimit != 0 { + payIntent.CltvLimit = &rpcPayReq.CltvLimit + } + + // If the payment request field isn't blank, then the details of the + // invoice are encoded entirely within the encoded payReq. So we'll + // attempt to decode it, populating the payment accordingly. + if rpcPayReq.PaymentRequest != "" { + payReq, err := zpay32.Decode( + rpcPayReq.PaymentRequest, r.ActiveNetParams, + ) + if err != nil { + return payIntent, err + } + + // Next, we'll ensure that this payreq hasn't already expired. + err = validatePayReqExpiry(payReq) + if err != nil { + return payIntent, err + } + + // If the amount was not included in the invoice, then we let + // the payee specify the amount of satoshis they wish to send. + // We override the amount to pay with the amount provided from + // the payment request. + if payReq.MilliSat == nil { + if rpcPayReq.Amt == 0 { + return payIntent, errors.New("amount must be " + + "specified when paying a zero amount " + + "invoice") + } + + payIntent.Amount = lnwire.NewMSatFromSatoshis( + btcutil.Amount(rpcPayReq.Amt), + ) + } else { + payIntent.Amount = *payReq.MilliSat + } + + // Calculate the fee limit that should be used for this payment. + payIntent.FeeLimit = calculateFeeLimit( + rpcPayReq.FeeLimit, payIntent.Amount, + ) + + copy(payIntent.PaymentHash[:], payReq.PaymentHash[:]) + destKey := payReq.Destination.SerializeCompressed() + copy(payIntent.Target[:], destKey) + + cltvDelta := uint16(payReq.MinFinalCLTVExpiry()) + if cltvDelta != 0 { + payIntent.FinalCLTVDelta = &cltvDelta + } + payIntent.RouteHints = payReq.RouteHints + + return payIntent, nil + } + + // At this point, a destination MUST be specified, so we'll convert it + // into the proper representation now. The destination will either be + // encoded as raw bytes, or via a hex string. + var pubBytes []byte + if len(rpcPayReq.Dest) != 0 { + pubBytes = rpcPayReq.Dest + } else { + var err error + pubBytes, err = hex.DecodeString(rpcPayReq.DestString) + if err != nil { + return payIntent, err + } + } + if len(pubBytes) != 33 { + return payIntent, errors.New("invalid key length") + } + copy(payIntent.Target[:], pubBytes) + + // Otherwise, If the payment request field was not specified + // (and a custom route wasn't specified), construct the payment + // from the other fields. + payIntent.Amount = lnwire.NewMSatFromSatoshis( + btcutil.Amount(rpcPayReq.Amt), + ) + + // Calculate the fee limit that should be used for this payment. + payIntent.FeeLimit = calculateFeeLimit( + rpcPayReq.FeeLimit, payIntent.Amount, + ) + + cltvDelta := uint16(rpcPayReq.FinalCltvDelta) + if cltvDelta != 0 { + payIntent.FinalCLTVDelta = &cltvDelta + } + + // If the user is manually specifying payment details, then the payment + // hash may be encoded as a string. + switch { + case rpcPayReq.PaymentHashString != "": + paymentHash, err := hex.DecodeString( + rpcPayReq.PaymentHashString, + ) + if err != nil { + return payIntent, err + } + + copy(payIntent.PaymentHash[:], paymentHash) + + // If we're in debug HTLC mode, then all outgoing HTLCs will pay to the + // same debug rHash. Otherwise, we pay to the rHash specified within + // the RPC request. + case r.DebugHTLC && bytes.Equal(payIntent.PaymentHash[:], zeroHash[:]): + copy(payIntent.PaymentHash[:], invoices.DebugHash[:]) + + default: + copy(payIntent.PaymentHash[:], rpcPayReq.PaymentHash) + } + + // Currently, within the bootstrap phase of the network, we limit the + // largest payment size allotted to (2^32) - 1 mSAT or 4.29 million + // satoshis. + if payIntent.Amount > r.MaxPaymentMSat { + // In this case, we'll send an error to the caller, but + // continue our loop for the next payment. + return payIntent, fmt.Errorf("payment of %v is too large, "+ + "max payment allowed is %v", payIntent.Amount, + r.MaxPaymentMSat) + + } + + return payIntent, nil +} + +// validatePayReqExpiry checks if the passed payment request has expired. In +// the case it has expired, an error will be returned. +func validatePayReqExpiry(payReq *zpay32.Invoice) error { + expiry := payReq.Expiry() + validUntil := payReq.Timestamp.Add(expiry) + if time.Now().After(validUntil) { + return fmt.Errorf("invoice expired. Valid until %v", validUntil) + } + + return nil +} diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 5e21e7337b1..87c3bf048f5 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -179,7 +179,9 @@ func (s *Server) SendPayment(ctx context.Context, // Now that we know the payment request is present, we'll attempt to // decode it in order to parse out all the parameters for the route. - payReq, err := zpay32.Decode(req.PayReq, s.cfg.ActiveNetParams) + payReq, err := zpay32.Decode( + req.PayReq, s.cfg.RouterBackend.ActiveNetParams, + ) if err != nil { return nil, err } diff --git a/rpcserver.go b/rpcserver.go index 087ab65831a..ccabef32f51 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "crypto/sha256" "crypto/tls" "encoding/hex" @@ -66,8 +65,6 @@ const ( ) var ( - zeroHash [32]byte - // maxPaymentMSat is the maximum allowed payment currently permitted as // defined in BOLT-002. This value depends on which chain is active. // It is set to the value under the Bitcoin chain as default. @@ -457,7 +454,9 @@ func newRPCServer(s *server, macService *macaroons.Service, } return info.Capacity, nil }, - FindRoutes: s.chanRouter.FindRoutes, + FindRoutes: s.chanRouter.FindRoutes, + DebugHTLC: cfg.DebugHTLC, + ActiveNetParams: activeNetParams.Params, } var ( @@ -2710,18 +2709,6 @@ func (r *rpcServer) savePayment(route *routing.Route, return r.server.chanDB.AddPayment(payment) } -// validatePayReqExpiry checks if the passed payment request has expired. In -// the case it has expired, an error will be returned. -func validatePayReqExpiry(payReq *zpay32.Invoice) error { - expiry := payReq.Expiry() - validUntil := payReq.Timestamp.Add(expiry) - if time.Now().After(validUntil) { - return fmt.Errorf("invoice expired. Valid until %v", validUntil) - } - - return nil -} - // paymentStream enables different types of payment streams, such as: // lnrpc.Lightning_SendPaymentServer and lnrpc.Lightning_SendToRouteServer to // execute sendPayment. We use this struct as a sort of bridge to enable code @@ -2867,7 +2854,8 @@ type rpcPaymentIntent struct { // dispatch a client from the information presented by an RPC client. There are // three ways a client can specify their payment details: a payment request, // via manual details, or via a complete route. -func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) { +func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) ( + rpcPaymentIntent, error) { payIntent := rpcPaymentIntent{} // If a route was specified, then we can use that directly. @@ -2891,152 +2879,13 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error return payIntent, nil } - return extractIntentFromSendRequest(rpcPayReq.SendRequest) -} - -// extractIntentFromSendRequest attempts to parse the SendRequest details -// required to dispatch a client from the information presented by an RPC -// client. -func extractIntentFromSendRequest(rpcPayReq *lnrpc.SendRequest) ( - rpcPaymentIntent, error) { - - payIntent := rpcPaymentIntent{} - - // If there are no routes specified, pass along a outgoing channel - // restriction if specified. - if rpcPayReq.OutgoingChanId != 0 { - payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId - } - - // Take cltv limit from request if set. - if rpcPayReq.CltvLimit != 0 { - payIntent.CltvLimit = &rpcPayReq.CltvLimit - } - - // If the payment request field isn't blank, then the details of the - // invoice are encoded entirely within the encoded payReq. So we'll - // attempt to decode it, populating the payment accordingly. - if rpcPayReq.PaymentRequest != "" { - payReq, err := zpay32.Decode( - rpcPayReq.PaymentRequest, activeNetParams.Params, - ) - if err != nil { - return payIntent, err - } - - // Next, we'll ensure that this payreq hasn't already expired. - err = validatePayReqExpiry(payReq) - if err != nil { - return payIntent, err - } - - // If the amount was not included in the invoice, then we let - // the payee specify the amount of satoshis they wish to send. - // We override the amount to pay with the amount provided from - // the payment request. - if payReq.MilliSat == nil { - if rpcPayReq.Amt == 0 { - return payIntent, errors.New("amount must be " + - "specified when paying a zero amount " + - "invoice") - } - - payIntent.Amount = lnwire.NewMSatFromSatoshis( - btcutil.Amount(rpcPayReq.Amt), - ) - } else { - payIntent.Amount = *payReq.MilliSat - } - - // Calculate the fee limit that should be used for this payment. - payIntent.FeeLimit = calculateFeeLimit( - rpcPayReq.FeeLimit, payIntent.Amount, - ) - - copy(payIntent.PaymentHash[:], payReq.PaymentHash[:]) - destKey := payReq.Destination.SerializeCompressed() - copy(payIntent.Target[:], destKey) - - cltvDelta := uint16(payReq.MinFinalCLTVExpiry()) - if cltvDelta != 0 { - payIntent.FinalCLTVDelta = &cltvDelta - } - - payIntent.RouteHints = payReq.RouteHints - - return payIntent, nil - } - - // At this point, a destination MUST be specified, so we'll convert it - // into the proper representation now. The destination will either be - // encoded as raw bytes, or via a hex string. - var pubBytes []byte - if len(rpcPayReq.Dest) != 0 { - pubBytes = rpcPayReq.Dest - } else { - var err error - pubBytes, err = hex.DecodeString(rpcPayReq.DestString) - if err != nil { - return payIntent, err - } - } - if len(pubBytes) != 33 { - return payIntent, errors.New("invalid key length") - } - copy(payIntent.Target[:], pubBytes) - - // Otherwise, If the payment request field was not specified - // (and a custom route wasn't specified), construct the payment - // from the other fields. - payIntent.Amount = lnwire.NewMSatFromSatoshis( - btcutil.Amount(rpcPayReq.Amt), - ) - - // Calculate the fee limit that should be used for this payment. - payIntent.FeeLimit = calculateFeeLimit( - rpcPayReq.FeeLimit, payIntent.Amount, + payment, err := r.routerBackend.ExtractIntentFromSendRequest( + rpcPayReq.SendRequest, ) - - cltvDelta := uint16(rpcPayReq.FinalCltvDelta) - if cltvDelta != 0 { - payIntent.FinalCLTVDelta = &cltvDelta - } - - // If the user is manually specifying payment details, then the payment - // hash may be encoded as a string. - switch { - case rpcPayReq.PaymentHashString != "": - paymentHash, err := hex.DecodeString( - rpcPayReq.PaymentHashString, - ) - if err != nil { - return payIntent, err - } - - copy(payIntent.PaymentHash[:], paymentHash) - - // If we're in debug HTLC mode, then all outgoing HTLCs will pay to the - // same debug rHash. Otherwise, we pay to the rHash specified within - // the RPC request. - case cfg.DebugHTLC && bytes.Equal(payIntent.PaymentHash[:], zeroHash[:]): - copy(payIntent.PaymentHash[:], invoices.DebugHash[:]) - - default: - copy(payIntent.PaymentHash[:], rpcPayReq.PaymentHash) - } - - // Currently, within the bootstrap phase of the network, we limit the - // largest payment size allotted to (2^32) - 1 mSAT or 4.29 million - // satoshis. - if payIntent.Amount > maxPaymentMSat { - // In this case, we'll send an error to the caller, but - // continue our loop for the next payment. - return payIntent, fmt.Errorf("payment of %v is too large, "+ - "max payment allowed is %v", payIntent.Amount, - maxPaymentMSat) - + if err != nil { + return payIntent, err } - + payIntent.LightningPayment = *payment return payIntent, nil } @@ -3181,7 +3030,7 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error { // fields. If the payment proto wasn't well // formed, then we'll send an error reply and // wait for the next payment. - payIntent, err := extractPaymentIntent(nextPayment) + payIntent, err := r.extractPaymentIntent(nextPayment) if err != nil { if err := stream.send(&lnrpc.SendResponse{ PaymentError: err.Error(), @@ -3317,7 +3166,7 @@ func (r *rpcServer) sendPaymentSync(ctx context.Context, // First we'll attempt to map the proto describing the next payment to // an intent that we can pass to local sub-systems. - payIntent, err := extractPaymentIntent(nextPayment) + payIntent, err := r.extractPaymentIntent(nextPayment) if err != nil { return nil, err } diff --git a/subrpcserver_config.go b/subrpcserver_config.go index c697b03e5c7..e45ab062fba 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -191,9 +191,6 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl, subCfgValue.FieldByName("NetworkDir").Set( reflect.ValueOf(networkDir), ) - subCfgValue.FieldByName("ActiveNetParams").Set( - reflect.ValueOf(activeNetParams), - ) subCfgValue.FieldByName("MacService").Set( reflect.ValueOf(macService), ) From f12674d15773da4636b7578563624f5f9536afc1 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 22 Mar 2019 10:21:11 +0100 Subject: [PATCH 4/6] routerrpc: reuse payment parse logic --- lnrpc/routerrpc/router.pb.go | 167 +++++++------------------------ lnrpc/routerrpc/router.proto | 42 +------- lnrpc/routerrpc/router_server.go | 50 +-------- 3 files changed, 44 insertions(+), 215 deletions(-) diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index cd7bcfe4399..416971c8b8a 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -1,11 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: routerrpc/router.proto -package routerrpc +package routerrpc // import "github.com/lightningnetwork/lnd/lnrpc/routerrpc" import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" +import lnrpc "github.com/lightningnetwork/lnd/lnrpc" import ( context "golang.org/x/net/context" @@ -23,97 +24,6 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package -type PaymentRequest struct { - // * - // A serialized BOLT-11 payment request that contains all information - // required to dispatch the payment. If the pay req is invalid, or expired, - // an error will be returned. - PayReq string `protobuf:"bytes,1,opt,name=pay_req,json=payReq,proto3" json:"pay_req,omitempty"` - // * - // An absolute limit on the highest fee we should pay when looking for a route - // to the destination. Routes with fees higher than this will be ignored, if - // there are no routes with a fee below this amount, an error will be - // returned. - FeeLimitSat int64 `protobuf:"varint,2,opt,name=fee_limit_sat,json=feeLimitSat,proto3" json:"fee_limit_sat,omitempty"` - // * - // An absolute limit on the cumulative CLTV value along the route for this - // payment. Routes with total CLTV values higher than this will be ignored, - // if there are no routes with a CLTV value below this amount, an error will - // be returned. - CltvLimit int32 `protobuf:"varint,3,opt,name=cltv_limit,json=cltvLimit,proto3" json:"cltv_limit,omitempty"` - // * - // An upper limit on the amount of time we should spend when attempting to - // fulfill the payment. This is expressed in seconds. If we cannot make a - // successful payment within this time frame, an error will be returned. - TimeoutSeconds int32 `protobuf:"varint,4,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` - // * - // The channel id of the channel that must be taken to the first hop. If zero, - // any channel may be used. - OutgoingChannelId int64 `protobuf:"varint,5,opt,name=outgoing_channel_id,json=outgoingChannelId,proto3" json:"outgoing_channel_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PaymentRequest) Reset() { *m = PaymentRequest{} } -func (m *PaymentRequest) String() string { return proto.CompactTextString(m) } -func (*PaymentRequest) ProtoMessage() {} -func (*PaymentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_router_e218e7c710fe8172, []int{0} -} -func (m *PaymentRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PaymentRequest.Unmarshal(m, b) -} -func (m *PaymentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PaymentRequest.Marshal(b, m, deterministic) -} -func (dst *PaymentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PaymentRequest.Merge(dst, src) -} -func (m *PaymentRequest) XXX_Size() int { - return xxx_messageInfo_PaymentRequest.Size(m) -} -func (m *PaymentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PaymentRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_PaymentRequest proto.InternalMessageInfo - -func (m *PaymentRequest) GetPayReq() string { - if m != nil { - return m.PayReq - } - return "" -} - -func (m *PaymentRequest) GetFeeLimitSat() int64 { - if m != nil { - return m.FeeLimitSat - } - return 0 -} - -func (m *PaymentRequest) GetCltvLimit() int32 { - if m != nil { - return m.CltvLimit - } - return 0 -} - -func (m *PaymentRequest) GetTimeoutSeconds() int32 { - if m != nil { - return m.TimeoutSeconds - } - return 0 -} - -func (m *PaymentRequest) GetOutgoingChannelId() int64 { - if m != nil { - return m.OutgoingChannelId - } - return 0 -} - type PaymentResponse struct { // * // The payment hash that we paid to. Provided so callers are able to map @@ -134,7 +44,7 @@ func (m *PaymentResponse) Reset() { *m = PaymentResponse{} } func (m *PaymentResponse) String() string { return proto.CompactTextString(m) } func (*PaymentResponse) ProtoMessage() {} func (*PaymentResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_router_e218e7c710fe8172, []int{1} + return fileDescriptor_router_73724cf9711cb3ac, []int{0} } func (m *PaymentResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PaymentResponse.Unmarshal(m, b) @@ -191,7 +101,7 @@ func (m *RouteFeeRequest) Reset() { *m = RouteFeeRequest{} } func (m *RouteFeeRequest) String() string { return proto.CompactTextString(m) } func (*RouteFeeRequest) ProtoMessage() {} func (*RouteFeeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_router_e218e7c710fe8172, []int{2} + return fileDescriptor_router_73724cf9711cb3ac, []int{1} } func (m *RouteFeeRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RouteFeeRequest.Unmarshal(m, b) @@ -244,7 +154,7 @@ func (m *RouteFeeResponse) Reset() { *m = RouteFeeResponse{} } func (m *RouteFeeResponse) String() string { return proto.CompactTextString(m) } func (*RouteFeeResponse) ProtoMessage() {} func (*RouteFeeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_router_e218e7c710fe8172, []int{3} + return fileDescriptor_router_73724cf9711cb3ac, []int{2} } func (m *RouteFeeResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RouteFeeResponse.Unmarshal(m, b) @@ -279,7 +189,6 @@ func (m *RouteFeeResponse) GetTimeLockDelay() int64 { } func init() { - proto.RegisterType((*PaymentRequest)(nil), "routerrpc.PaymentRequest") proto.RegisterType((*PaymentResponse)(nil), "routerrpc.PaymentResponse") proto.RegisterType((*RouteFeeRequest)(nil), "routerrpc.RouteFeeRequest") proto.RegisterType((*RouteFeeResponse)(nil), "routerrpc.RouteFeeResponse") @@ -303,7 +212,7 @@ type RouterClient interface { // payment, or cannot find a route that satisfies the constraints in the // PaymentRequest, then an error will be returned. Otherwise, the payment // pre-image, along with the final route will be returned. - SendPayment(ctx context.Context, in *PaymentRequest, opts ...grpc.CallOption) (*PaymentResponse, error) + SendPayment(ctx context.Context, in *lnrpc.SendRequest, opts ...grpc.CallOption) (*PaymentResponse, error) // * // EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it // may cost to send an HTLC to the target end destination. @@ -318,7 +227,7 @@ func NewRouterClient(cc *grpc.ClientConn) RouterClient { return &routerClient{cc} } -func (c *routerClient) SendPayment(ctx context.Context, in *PaymentRequest, opts ...grpc.CallOption) (*PaymentResponse, error) { +func (c *routerClient) SendPayment(ctx context.Context, in *lnrpc.SendRequest, opts ...grpc.CallOption) (*PaymentResponse, error) { out := new(PaymentResponse) err := c.cc.Invoke(ctx, "/routerrpc.Router/SendPayment", in, out, opts...) if err != nil { @@ -344,7 +253,7 @@ type RouterServer interface { // payment, or cannot find a route that satisfies the constraints in the // PaymentRequest, then an error will be returned. Otherwise, the payment // pre-image, along with the final route will be returned. - SendPayment(context.Context, *PaymentRequest) (*PaymentResponse, error) + SendPayment(context.Context, *lnrpc.SendRequest) (*PaymentResponse, error) // * // EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it // may cost to send an HTLC to the target end destination. @@ -356,7 +265,7 @@ func RegisterRouterServer(s *grpc.Server, srv RouterServer) { } func _Router_SendPayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PaymentRequest) + in := new(lnrpc.SendRequest) if err := dec(in); err != nil { return nil, err } @@ -368,7 +277,7 @@ func _Router_SendPayment_Handler(srv interface{}, ctx context.Context, dec func( FullMethod: "/routerrpc.Router/SendPayment", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RouterServer).SendPayment(ctx, req.(*PaymentRequest)) + return srv.(RouterServer).SendPayment(ctx, req.(*lnrpc.SendRequest)) } return interceptor(ctx, in, info, handler) } @@ -408,34 +317,30 @@ var _Router_serviceDesc = grpc.ServiceDesc{ Metadata: "routerrpc/router.proto", } -func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_router_e218e7c710fe8172) } - -var fileDescriptor_router_e218e7c710fe8172 = []byte{ - // 409 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0xdf, 0x6e, 0xd3, 0x30, - 0x14, 0xc6, 0x15, 0xb6, 0x85, 0xe5, 0x74, 0x6b, 0x8b, 0x91, 0x20, 0xeb, 0x84, 0xa8, 0x72, 0x01, - 0xb9, 0x0a, 0x12, 0xdc, 0x73, 0xc3, 0x36, 0x31, 0x31, 0x24, 0xe4, 0x3e, 0x80, 0x65, 0x92, 0xb3, - 0x26, 0x34, 0x8e, 0x5d, 0xdb, 0x41, 0xca, 0xb3, 0xf0, 0x3c, 0xbc, 0x17, 0xb2, 0xe3, 0x96, 0x82, - 0x7a, 0xe7, 0x7c, 0xe7, 0xef, 0xf7, 0x3b, 0x81, 0x17, 0x5a, 0xf6, 0x16, 0xb5, 0x56, 0xe5, 0xbb, - 0xf1, 0x55, 0x28, 0x2d, 0xad, 0x24, 0xc9, 0x5e, 0xcf, 0x7e, 0x47, 0x30, 0xfd, 0xc6, 0x07, 0x81, - 0x9d, 0xa5, 0xb8, 0xed, 0xd1, 0x58, 0xf2, 0x12, 0x9e, 0x2a, 0x3e, 0x30, 0x8d, 0xdb, 0x34, 0x5a, - 0x46, 0x79, 0x42, 0x63, 0xc5, 0x07, 0x8a, 0x5b, 0x92, 0xc1, 0xe5, 0x23, 0x22, 0x6b, 0x1b, 0xd1, - 0x58, 0x66, 0xb8, 0x4d, 0x9f, 0x2c, 0xa3, 0xfc, 0x84, 0x4e, 0x1e, 0x11, 0x1f, 0x9c, 0xb6, 0xe2, - 0x96, 0xbc, 0x02, 0x28, 0x5b, 0xfb, 0x73, 0x4c, 0x4a, 0x4f, 0x96, 0x51, 0x7e, 0x46, 0x13, 0xa7, - 0xf8, 0x0c, 0xf2, 0x16, 0x66, 0xb6, 0x11, 0x28, 0x7b, 0xcb, 0x0c, 0x96, 0xb2, 0xab, 0x4c, 0x7a, - 0xea, 0x73, 0xa6, 0x41, 0x5e, 0x8d, 0x2a, 0x29, 0xe0, 0xb9, 0xec, 0xed, 0x5a, 0x36, 0xdd, 0x9a, - 0x95, 0x35, 0xef, 0x3a, 0x6c, 0x59, 0x53, 0xa5, 0x67, 0x7e, 0xe2, 0xb3, 0x5d, 0xe8, 0xd3, 0x18, - 0xb9, 0xaf, 0xb2, 0x1f, 0x30, 0xdb, 0xdb, 0x30, 0x4a, 0x76, 0x06, 0xc9, 0x15, 0x9c, 0x3b, 0x1f, - 0x35, 0x37, 0xb5, 0x37, 0x72, 0x41, 0x9d, 0xaf, 0xcf, 0xdc, 0xd4, 0xe4, 0x1a, 0x12, 0xa5, 0x91, - 0x35, 0x82, 0xaf, 0xd1, 0xbb, 0xb8, 0xa0, 0xe7, 0x4a, 0xe3, 0xbd, 0xfb, 0x26, 0xaf, 0x61, 0xa2, - 0xc6, 0x56, 0x0c, 0xb5, 0xf6, 0x1e, 0x12, 0x0a, 0x41, 0xba, 0xd5, 0x3a, 0xfb, 0x08, 0x33, 0xea, - 0x00, 0xde, 0x21, 0xee, 0x98, 0x11, 0x38, 0xad, 0xd0, 0xd8, 0x30, 0xc7, 0xbf, 0x1d, 0x47, 0x2e, - 0x0e, 0x41, 0xc5, 0x5c, 0x38, 0x46, 0x59, 0x05, 0xf3, 0xbf, 0xf5, 0x61, 0xd9, 0x1c, 0xe6, 0xee, - 0x28, 0xce, 0xae, 0x63, 0x2c, 0x5c, 0x55, 0xe4, 0xab, 0xa6, 0x41, 0xbf, 0x43, 0xfc, 0x6a, 0xb8, - 0x25, 0x6f, 0x46, 0x84, 0xac, 0x95, 0xe5, 0x86, 0x55, 0xd8, 0xf2, 0x21, 0xb4, 0xbf, 0x74, 0xf2, - 0x83, 0x2c, 0x37, 0x37, 0x4e, 0x7c, 0xff, 0x2b, 0x82, 0xd8, 0x8f, 0xd1, 0xe4, 0x06, 0x26, 0x2b, - 0xec, 0xaa, 0x00, 0x88, 0x5c, 0x15, 0xfb, 0xfb, 0x17, 0xff, 0xde, 0x7e, 0xb1, 0x38, 0x16, 0x0a, - 0x2b, 0x7e, 0x81, 0xf9, 0xad, 0xb1, 0x8d, 0xe0, 0x16, 0x77, 0xeb, 0x93, 0xc3, 0xfc, 0xff, 0x98, - 0x2c, 0xae, 0x8f, 0xc6, 0xc6, 0x66, 0xdf, 0x63, 0xff, 0x27, 0x7e, 0xf8, 0x13, 0x00, 0x00, 0xff, - 0xff, 0x95, 0xc2, 0xf8, 0xec, 0xa3, 0x02, 0x00, 0x00, +func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_router_73724cf9711cb3ac) } + +var fileDescriptor_router_73724cf9711cb3ac = []byte{ + // 348 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0x4f, 0x4f, 0xf2, 0x40, + 0x10, 0xc6, 0xd3, 0x97, 0x37, 0x40, 0x97, 0xf7, 0x15, 0xb2, 0x07, 0xc5, 0x72, 0x90, 0x70, 0x30, + 0x9c, 0xda, 0xa8, 0x67, 0x3d, 0x18, 0x21, 0x1a, 0x35, 0x31, 0xe5, 0xe6, 0x65, 0xb3, 0xb4, 0x63, + 0x5b, 0xe9, 0xfe, 0x71, 0x77, 0x88, 0xe9, 0xf7, 0xf0, 0x03, 0x9b, 0x6d, 0x2b, 0x1a, 0xe2, 0x6d, + 0xfa, 0x9b, 0xce, 0x33, 0xcf, 0x3c, 0x4b, 0x0e, 0x8d, 0xda, 0x22, 0x18, 0xa3, 0x93, 0xa8, 0xa9, + 0x42, 0x6d, 0x14, 0x2a, 0xea, 0xef, 0x78, 0xe0, 0x1b, 0x9d, 0x34, 0x74, 0xf6, 0x4a, 0x86, 0x4f, + 0xbc, 0x12, 0x20, 0x31, 0x06, 0xab, 0x95, 0xb4, 0x40, 0x8f, 0x49, 0x5f, 0xf3, 0x8a, 0xe5, 0xdc, + 0xe6, 0x63, 0x6f, 0xea, 0xcd, 0xff, 0xc5, 0x3d, 0xcd, 0xab, 0x5b, 0x6e, 0x73, 0x3a, 0x21, 0xbe, + 0x36, 0xc0, 0x0a, 0xc1, 0x33, 0x18, 0xff, 0xa9, 0x7b, 0x7d, 0x6d, 0xe0, 0xce, 0x7d, 0xd3, 0x13, + 0x32, 0xd0, 0x8d, 0x14, 0x03, 0x63, 0xc6, 0x9d, 0xa9, 0x37, 0xf7, 0x63, 0xd2, 0xa2, 0x85, 0x31, + 0xb3, 0x2b, 0x32, 0x8c, 0x9d, 0x87, 0x25, 0x40, 0x0c, 0x6f, 0x5b, 0xb0, 0x48, 0x29, 0xf9, 0x9b, + 0x82, 0xc5, 0x76, 0x4f, 0x5d, 0xd3, 0x23, 0xd2, 0xe3, 0x02, 0x99, 0xe5, 0x58, 0xaf, 0xe8, 0xc4, + 0x5d, 0x2e, 0x70, 0xc5, 0x71, 0x96, 0x92, 0xd1, 0xf7, 0x7c, 0x6b, 0x76, 0x4e, 0x46, 0xee, 0xae, + 0x42, 0x66, 0xec, 0x05, 0x80, 0x09, 0x37, 0xe5, 0xd5, 0x53, 0x07, 0x2d, 0x5f, 0x02, 0x3c, 0x5a, + 0x8e, 0xf4, 0x94, 0x0c, 0xb1, 0x10, 0xc0, 0x4a, 0x95, 0x6c, 0x58, 0x0a, 0x25, 0xaf, 0x5a, 0xf9, + 0xff, 0x0e, 0x3f, 0xa8, 0x64, 0x73, 0xe3, 0xe0, 0xf9, 0x87, 0x47, 0xba, 0xf5, 0x1a, 0x43, 0x2f, + 0xc9, 0x60, 0x05, 0x32, 0x6d, 0x03, 0xa2, 0x34, 0x2c, 0xa5, 0x4b, 0xce, 0xb1, 0xf6, 0x80, 0x20, + 0x08, 0x77, 0xb1, 0x86, 0xfb, 0x41, 0xde, 0x93, 0xd1, 0xc2, 0x62, 0x21, 0x38, 0xc2, 0x97, 0x6f, + 0xfa, 0xf3, 0xff, 0xbd, 0x30, 0x82, 0xc9, 0xaf, 0xbd, 0x46, 0xec, 0xfa, 0xec, 0x39, 0xca, 0x0a, + 0xcc, 0xb7, 0xeb, 0x30, 0x51, 0x22, 0x2a, 0x8b, 0x2c, 0x47, 0x59, 0xc8, 0x4c, 0x02, 0xbe, 0x2b, + 0xb3, 0x89, 0x4a, 0x99, 0x46, 0xb5, 0xbb, 0x68, 0xa7, 0xb1, 0xee, 0xd6, 0x4f, 0x7c, 0xf1, 0x19, + 0x00, 0x00, 0xff, 0xff, 0xfe, 0xde, 0x29, 0x78, 0x12, 0x02, 0x00, 0x00, } diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 4ab4af29e48..37181f4e33f 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -1,44 +1,10 @@ syntax = "proto3"; -package routerrpc; - -message PaymentRequest { - /** - A serialized BOLT-11 payment request that contains all information - required to dispatch the payment. If the pay req is invalid, or expired, - an error will be returned. - */ - string pay_req = 1; - - /** - An absolute limit on the highest fee we should pay when looking for a route - to the destination. Routes with fees higher than this will be ignored, if - there are no routes with a fee below this amount, an error will be - returned. - */ - int64 fee_limit_sat = 2; - - /** - An absolute limit on the cumulative CLTV value along the route for this - payment. Routes with total CLTV values higher than this will be ignored, - if there are no routes with a CLTV value below this amount, an error will - be returned. - */ - int32 cltv_limit = 3; +import "rpc.proto"; - /** - An upper limit on the amount of time we should spend when attempting to - fulfill the payment. This is expressed in seconds. If we cannot make a - successful payment within this time frame, an error will be returned. - */ - int32 timeout_seconds = 4; +package routerrpc; - /** - The channel id of the channel that must be taken to the first hop. If zero, - any channel may be used. - */ - int64 outgoing_channel_id = 5; -} +option go_package = "github.com/lightningnetwork/lnd/lnrpc/routerrpc"; message PaymentResponse { /** @@ -93,7 +59,7 @@ service Router { PaymentRequest, then an error will be returned. Otherwise, the payment pre-image, along with the final route will be returned. */ - rpc SendPayment(PaymentRequest) returns (PaymentResponse); + rpc SendPayment(lnrpc.SendRequest) returns (PaymentResponse); /** EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 87c3bf048f5..62547c40e88 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -9,13 +9,11 @@ import ( "io/ioutil" "os" "path/filepath" - "time" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" - "github.com/lightningnetwork/lnd/zpay32" "google.golang.org/grpc" "gopkg.in/macaroon-bakery.v2/bakery" ) @@ -168,60 +166,20 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // PaymentRequest, then an error will be returned. Otherwise, the payment // pre-image, along with the final route will be returned. func (s *Server) SendPayment(ctx context.Context, - req *PaymentRequest) (*PaymentResponse, error) { + req *lnrpc.SendRequest) (*PaymentResponse, error) { - switch { - // If the payment request isn't populated, then we won't be able to - // even attempt a payment. - case req.PayReq == "": - return nil, fmt.Errorf("a valid payment request MUST be specified") - } - - // Now that we know the payment request is present, we'll attempt to - // decode it in order to parse out all the parameters for the route. - payReq, err := zpay32.Decode( - req.PayReq, s.cfg.RouterBackend.ActiveNetParams, - ) + payment, err := s.cfg.RouterBackend.ExtractIntentFromSendRequest(req) if err != nil { return nil, err } - // Atm, this service does not support invoices that don't have their - // value fully specified. - if payReq.MilliSat == nil { - return nil, fmt.Errorf("zero value invoices are not supported") - } - - var destination routing.Vertex - copy(destination[:], payReq.Destination.SerializeCompressed()) - - // Now that all the information we need has been parsed, we'll map this - // proto request into a proper request that our backing router can - // understand. - finalDelta := uint16(payReq.MinFinalCLTVExpiry()) - payment := routing.LightningPayment{ - Target: destination, - Amount: *payReq.MilliSat, - FeeLimit: lnwire.MilliSatoshi(req.FeeLimitSat), - PaymentHash: *payReq.PaymentHash, - FinalCLTVDelta: &finalDelta, - PayAttemptTimeout: time.Second * time.Duration(req.TimeoutSeconds), - RouteHints: payReq.RouteHints, - } - - // Pin to an outgoing channel if specified. - if req.OutgoingChannelId != 0 { - chanID := uint64(req.OutgoingChannelId) - payment.OutgoingChannelID = &chanID - } - - preImage, _, err := s.cfg.Router.SendPayment(&payment) + preImage, _, err := s.cfg.Router.SendPayment(payment) if err != nil { return nil, err } return &PaymentResponse{ - PayHash: (*payReq.PaymentHash)[:], + PayHash: payment.PaymentHash[:], PreImage: preImage[:], }, nil } From f021398029c53f4694c79bd8c521d60a705bb620 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Thu, 28 Mar 2019 17:24:19 +0100 Subject: [PATCH 5/6] routing: concentrate final cltv default logic --- lnrpc/routerrpc/router_backend.go | 10 ++++++---- routing/payment_session_test.go | 2 +- routing/router.go | 15 +++------------ 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index eac0db1907f..5eed16154f4 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -320,9 +320,10 @@ func (r *RouterBackend) ExtractIntentFromSendRequest(rpcPayReq *lnrpc.SendReques copy(payIntent.Target[:], destKey) cltvDelta := uint16(payReq.MinFinalCLTVExpiry()) - if cltvDelta != 0 { - payIntent.FinalCLTVDelta = &cltvDelta + if cltvDelta == 0 { + cltvDelta = zpay32.DefaultFinalCLTVDelta } + payIntent.FinalCLTVDelta = cltvDelta payIntent.RouteHints = payReq.RouteHints return payIntent, nil @@ -359,9 +360,10 @@ func (r *RouterBackend) ExtractIntentFromSendRequest(rpcPayReq *lnrpc.SendReques ) cltvDelta := uint16(rpcPayReq.FinalCltvDelta) - if cltvDelta != 0 { - payIntent.FinalCLTVDelta = &cltvDelta + if cltvDelta == 0 { + cltvDelta = zpay32.DefaultFinalCLTVDelta } + payIntent.FinalCLTVDelta = cltvDelta // If the user is manually specifying payment details, then the payment // hash may be encoded as a string. diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index 6904d7f9940..4316369c44e 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -44,7 +44,7 @@ func TestRequestRoute(t *testing.T) { payment := &LightningPayment{ CltvLimit: &cltvLimit, - FinalCLTVDelta: &finalCltvDelta, + FinalCLTVDelta: finalCltvDelta, } route, err := session.RequestRoute(payment, height, finalCltvDelta) diff --git a/routing/router.go b/routing/router.go index 1025a4023ea..d895a155e6d 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1558,10 +1558,8 @@ type LightningPayment struct { // FinalCLTVDelta is the CTLV expiry delta to use for the _final_ hop // in the route. This means that the final hop will have a CLTV delta - // of at least: currentHeight + FinalCLTVDelta. If this value is - // unspecified, then a default value of DefaultFinalCLTVDelta will be - // used. - FinalCLTVDelta *uint16 + // of at least: currentHeight + FinalCLTVDelta. + FinalCLTVDelta uint16 // PayAttemptTimeout is a timeout value that we'll use to determine // when we should should abandon the payment attempt after consecutive @@ -1652,13 +1650,6 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, return [32]byte{}, nil, err } - var finalCLTVDelta uint16 - if payment.FinalCLTVDelta == nil { - finalCLTVDelta = zpay32.DefaultFinalCLTVDelta - } else { - finalCLTVDelta = *payment.FinalCLTVDelta - } - var payAttemptTimeout time.Duration if payment.PayAttemptTimeout == time.Duration(0) { payAttemptTimeout = defaultPayAttemptTimeout @@ -1694,7 +1685,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, } route, err := paySession.RequestRoute( - payment, uint32(currentHeight), finalCLTVDelta, + payment, uint32(currentHeight), payment.FinalCLTVDelta, ) if err != nil { // If we're unable to successfully make a payment using From 7e65e01f9ec681450b046d154b2bbf30f2db6a0d Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 22 Mar 2019 10:21:25 +0100 Subject: [PATCH 6/6] lncli: use routerrpc send payment [DEBUG] --- cmd/lncli/commands.go | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index c77306c4d7c..ad3e055a96f 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -16,6 +16,8 @@ import ( "sync" "syscall" + "github.com/lightningnetwork/lnd/lnrpc/routerrpc" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/golang/protobuf/jsonpb" @@ -2341,8 +2343,11 @@ var payInvoiceCommand = cli.Command{ func payInvoice(ctx *cli.Context) error { args := ctx.Args() - client, cleanUp := getClient(ctx) - defer cleanUp() + + conn := getClientConn(ctx, false) + defer conn.Close() + + client := routerrpc.NewRouterClient(conn) var payReq string switch { @@ -2359,12 +2364,12 @@ func payInvoice(ctx *cli.Context) error { return err } - if !ctx.Bool("force") { - err = confirmPayReq(ctx, client, payReq) - if err != nil { - return err - } - } + // if !ctx.Bool("force") { + // err = confirmPayReq(ctx, client, payReq) + // if err != nil { + // return err + // } + // } req := &lnrpc.SendRequest{ PaymentRequest: payReq, @@ -2373,8 +2378,28 @@ func payInvoice(ctx *cli.Context) error { OutgoingChanId: ctx.Uint64("outgoing_chan_id"), CltvLimit: uint32(ctx.Int(cltvLimitFlag.Name)), } + resp, err := client.SendPayment(context.Background(), req) + if err != nil { + return err + } - return sendPaymentRequest(client, req) + printJSON(struct { + E string `json:"payment_error"` + P string `json:"payment_preimage"` + R *lnrpc.Route `json:"payment_route"` + }{ + E: resp.PaymentErr, + P: hex.EncodeToString(resp.PreImage), + }) + + // If we get a payment error back, we pass an error + // up to main which eventually calls fatal() and returns + // with a non-zero exit code. + if resp.PaymentErr != "" { + return errors.New(resp.PaymentErr) + } + + return nil } var sendToRouteCommand = cli.Command{