Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rpcserver: refactor invoice parsing for re-use
Browse files Browse the repository at this point in the history
We'll want to be able to parse the amount and expiry of an invoice in
other places, so we extract that into its own method.
guggero committed Dec 13, 2024
1 parent 8da9505 commit ae58049
Showing 1 changed file with 60 additions and 35 deletions.
95 changes: 60 additions & 35 deletions rpcserver.go
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@ import (
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@@ -7024,13 +7025,25 @@ func (r *rpcServer) SendPayment(req *tchrpc.SendPaymentRequest,
return fmt.Errorf("payment request and keysend custom " +
"records cannot be set at the same time")

// RFQ ID and keysend is set, which isn't a supported combination.
case req.RfqId != nil && isKeysend:
return fmt.Errorf("RFQ ID and keysend custom records " +
"cannot be set at the same time")

// A payment must either be a keysend payment or pay an invoice.
case !isKeysend && pReq.PaymentRequest == "":
return fmt.Errorf("payment request or keysend custom records " +
"must be set")

// There are custom records for the first hop set, which means RFQ
// negotiation has already happened (or this is a keysend payment and
// the correct asset amount is already encoded). So we don't need to do
// anything special and can just forward the payment to lnd.
case len(firstHopRecords) > 0:
// Continue below.

// The user specified a custom RFQ ID for a quote that should be used
// for the payment.
case req.RfqId != nil:
// Check if the provided RFQ ID matches the expected length.
if len(req.RfqId) != 32 {
@@ -7090,14 +7103,6 @@ func (r *rpcServer) SendPayment(req *tchrpc.SendPaymentRequest,

// The request wants to pay a specific invoice.
case pReq.PaymentRequest != "":
invoice, err := zpay32.Decode(
pReq.PaymentRequest, r.cfg.Lnd.ChainParams,
)
if err != nil {
return fmt.Errorf("error decoding payment request: %w",
err)
}

// The peer public key is optional if there is only a single
// asset channel.
var peerPubKey *route.Vertex
@@ -7128,35 +7133,11 @@ func (r *rpcServer) SendPayment(req *tchrpc.SendPaymentRequest,
// paymentMaxAmt is the maximum amount that the counterparty is
// expected to pay. This is the amount that the invoice is
// asking for plus the fee limit in milli-satoshis.
var paymentMaxAmt lnwire.MilliSatoshi
if invoice.MilliSat == nil {
amt, err := lnrpc.UnmarshallAmt(pReq.Amt, pReq.AmtMsat)
if err != nil {
return fmt.Errorf("error unmarshalling "+
"amount: %w", err)
}
if amt == 0 {
return errors.New("amount must be specified " +
"when paying a zero amount invoice")
}

paymentMaxAmt = amt
} else {
paymentMaxAmt = *invoice.MilliSat
}

// Calculate the fee limit that should be used for this payment.
feeLimit, err := lnrpc.UnmarshallAmt(
pReq.FeeLimitSat, pReq.FeeLimitMsat,
)
paymentMaxAmt, expiry, err := r.parseRequest(pReq)
if err != nil {
return fmt.Errorf("error unmarshalling fee limit: %w",
err)
return err
}

paymentMaxAmt += feeLimit

expiryTimestamp := invoice.Timestamp.Add(invoice.Expiry())
resp, err := r.AddAssetSellOrder(
ctx, &rfqrpc.AddAssetSellOrderRequest{
AssetSpecifier: &rfqrpc.AssetSpecifier{
@@ -7165,7 +7146,7 @@ func (r *rpcServer) SendPayment(req *tchrpc.SendPaymentRequest,
},
},
PaymentMaxAmt: uint64(paymentMaxAmt),
Expiry: uint64(expiryTimestamp.Unix()),
Expiry: uint64(expiry.Unix()),
PeerPubKey: peerPubKey[:],
TimeoutSeconds: uint32(
rfq.DefaultTimeout.Seconds(),
@@ -7295,6 +7276,50 @@ func (r *rpcServer) SendPayment(req *tchrpc.SendPaymentRequest,
}
}

// parseRequest parses the payment request and returns the payment maximum
// amount and the expiry time.
func (r *rpcServer) parseRequest(
req *routerrpc.SendPaymentRequest) (lnwire.MilliSatoshi, time.Time,
error) {

invoice, err := zpay32.Decode(req.PaymentRequest, r.cfg.Lnd.ChainParams)
if err != nil {
return 0, time.Time{}, fmt.Errorf("error decoding payment "+
"request: %w", err)
}

var paymentMaxAmt lnwire.MilliSatoshi
if invoice.MilliSat == nil {
amt, err := lnrpc.UnmarshallAmt(req.Amt, req.AmtMsat)
if err != nil {
return 0, time.Time{}, fmt.Errorf("error "+
"unmarshalling amount: %w", err)
}
if amt == 0 {
return 0, time.Time{}, errors.New("amount must be " +
"specified when paying a zero amount invoice")
}

paymentMaxAmt = amt
} else {
paymentMaxAmt = *invoice.MilliSat
}

// Calculate the fee limit that should be used for this payment.
feeLimit, err := lnrpc.UnmarshallAmt(
req.FeeLimitSat, req.FeeLimitMsat,
)
if err != nil {
return 0, time.Time{}, fmt.Errorf("error unmarshalling fee "+
"limit: %w", err)
}

paymentMaxAmt += feeLimit

expiry := invoice.Timestamp.Add(invoice.Expiry())
return paymentMaxAmt, expiry, nil
}

// AddInvoice is a wrapper around lnd's lnrpc.AddInvoice method with asset
// specific parameters. It allows RPC users to create invoices that correspond
// to the specified asset amount.

0 comments on commit ae58049

Please sign in to comment.