routerrpc: extend payment request parameters

Add missing parameters to routerrpc version of SendPayment.
This commit is contained in:
Joost Jager
2019-04-18 09:34:28 +02:00
parent 19d5f8f82c
commit eb700d35e1
7 changed files with 376 additions and 215 deletions

View File

@@ -4,12 +4,15 @@ import (
"encoding/hex"
"errors"
"fmt"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32"
context "golang.org/x/net/context"
)
@@ -38,6 +41,12 @@ type RouterBackend struct {
finalExpiry ...uint16) (*route.Route, error)
MissionControl *routing.MissionControl
// 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
@@ -336,3 +345,158 @@ func (r *RouterBackend) UnmarshallRoute(rpcroute *lnrpc.Route) (
return route, nil
}
// 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 *PaymentRequest) (
*routing.LightningPayment, error) {
payIntent := &routing.LightningPayment{}
// Pass along an outgoing channel restriction if specified.
if rpcPayReq.OutgoingChanId != 0 {
payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId
}
// Take cltv limit from request if set.
if rpcPayReq.CltvLimit != 0 {
cltvLimit := uint32(rpcPayReq.CltvLimit)
payIntent.CltvLimit = &cltvLimit
}
// Take fee limit from request.
payIntent.FeeLimit = lnwire.NewMSatFromSatoshis(
btcutil.Amount(rpcPayReq.FeeLimitSat),
)
// Set payment attempt timeout.
if rpcPayReq.TimeoutSeconds == 0 {
return nil, errors.New("timeout_seconds must be specified")
}
payIntent.PayAttemptTimeout = time.Second *
time.Duration(rpcPayReq.TimeoutSeconds)
// 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 != "" {
switch {
case len(rpcPayReq.Dest) > 0:
return nil, errors.New("dest and payment_request " +
"cannot appear together")
case len(rpcPayReq.PaymentHash) > 0:
return nil, errors.New("dest and payment_hash " +
"cannot appear together")
case rpcPayReq.FinalCltvDelta != 0:
return nil, errors.New("dest and final_cltv_delta " +
"cannot appear together")
}
payReq, err := zpay32.Decode(
rpcPayReq.PaymentRequest, r.ActiveNetParams,
)
if err != nil {
return nil, err
}
// Next, we'll ensure that this payreq hasn't already expired.
err = ValidatePayReqExpiry(payReq)
if err != nil {
return nil, 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 nil, errors.New("amount must be " +
"specified when paying a zero amount " +
"invoice")
}
payIntent.Amount = lnwire.NewMSatFromSatoshis(
btcutil.Amount(rpcPayReq.Amt),
)
} else {
if rpcPayReq.Amt != 0 {
return nil, errors.New("amount must not be " +
"specified when paying a non-zero " +
" amount invoice")
}
payIntent.Amount = *payReq.MilliSat
}
copy(payIntent.PaymentHash[:], payReq.PaymentHash[:])
destKey := payReq.Destination.SerializeCompressed()
copy(payIntent.Target[:], destKey)
payIntent.FinalCLTVDelta = uint16(payReq.MinFinalCLTVExpiry())
payIntent.RouteHints = payReq.RouteHints
} else {
// Otherwise, If the payment request field was not specified
// (and a custom route wasn't specified), construct the payment
// from the other fields.
// Payment destination.
if len(rpcPayReq.Dest) != 33 {
return nil, errors.New("invalid key length")
}
pubBytes := rpcPayReq.Dest
copy(payIntent.Target[:], pubBytes)
// Final payment CLTV delta.
if rpcPayReq.FinalCltvDelta != 0 {
payIntent.FinalCLTVDelta =
uint16(rpcPayReq.FinalCltvDelta)
} else {
payIntent.FinalCLTVDelta = zpay32.DefaultFinalCLTVDelta
}
// Amount.
if rpcPayReq.Amt == 0 {
return nil, errors.New("amount must be specified")
}
payIntent.Amount = lnwire.NewMSatFromSatoshis(
btcutil.Amount(rpcPayReq.Amt),
)
// Payment hash.
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
}