mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
multi: send to a blinded path in an invoice
Update the SendPayment flow so that it is able to send to an invoice containing a blinded path.
This commit is contained in:
parent
34d8fff5f9
commit
735d7d9738
4 changed files with 104 additions and 2 deletions
|
@ -999,6 +999,32 @@ func (r *RouterBackend) extractIntentFromSendRequest(
|
|||
payIntent.PaymentAddr = payAddr
|
||||
payIntent.PaymentRequest = []byte(rpcPayReq.PaymentRequest)
|
||||
payIntent.Metadata = payReq.Metadata
|
||||
|
||||
if len(payReq.BlindedPaymentPaths) > 0 {
|
||||
// NOTE: Currently we only choose a single payment path.
|
||||
// This will be updated in a future PR to handle
|
||||
// multiple blinded payment paths.
|
||||
path := payReq.BlindedPaymentPaths[0]
|
||||
if len(path.Hops) == 0 {
|
||||
return nil, fmt.Errorf("a blinded payment " +
|
||||
"must have at least 1 hop")
|
||||
}
|
||||
|
||||
finalHop := path.Hops[len(path.Hops)-1]
|
||||
|
||||
payIntent.BlindedPayment = MarshalBlindedPayment(path)
|
||||
|
||||
// Replace the target node with the blinded public key
|
||||
// of the blinded path's final node.
|
||||
copy(
|
||||
payIntent.Target[:],
|
||||
finalHop.BlindedNodePub.SerializeCompressed(),
|
||||
)
|
||||
|
||||
if !path.Features.IsEmpty() {
|
||||
payIntent.DestFeatures = path.Features.Clone()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise, If the payment request field was not specified
|
||||
// (and a custom route wasn't specified), construct the payment
|
||||
|
@ -1137,6 +1163,26 @@ func (r *RouterBackend) extractIntentFromSendRequest(
|
|||
return payIntent, nil
|
||||
}
|
||||
|
||||
// MarshalBlindedPayment marshals a zpay32.BLindedPaymentPath into a
|
||||
// routing.BlindedPayment.
|
||||
func MarshalBlindedPayment(
|
||||
path *zpay32.BlindedPaymentPath) *routing.BlindedPayment {
|
||||
|
||||
return &routing.BlindedPayment{
|
||||
BlindedPath: &sphinx.BlindedPath{
|
||||
IntroductionPoint: path.Hops[0].BlindedNodePub,
|
||||
BlindingPoint: path.FirstEphemeralBlindingPoint,
|
||||
BlindedHops: path.Hops,
|
||||
},
|
||||
BaseFee: path.FeeBaseMsat,
|
||||
ProportionalFeeRate: path.FeeRate,
|
||||
CltvExpiryDelta: path.CltvExpiryDelta,
|
||||
HtlcMinimum: path.HTLCMinMsat,
|
||||
HtlcMaximum: path.HTLCMaxMsat,
|
||||
Features: path.Features,
|
||||
}
|
||||
}
|
||||
|
||||
// unmarshallRouteHints unmarshalls a list of route hints.
|
||||
func unmarshallRouteHints(rpcRouteHints []*lnrpc.RouteHint) (
|
||||
[][]zpay32.HopHint, error) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btclog"
|
||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
|
@ -205,6 +206,18 @@ func newPaymentSession(p *LightningPayment, selfNode route.Vertex,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if p.BlindedPayment != nil {
|
||||
if len(edges) != 0 {
|
||||
return nil, fmt.Errorf("cannot have both route hints " +
|
||||
"and blinded path")
|
||||
}
|
||||
|
||||
edges, err = p.BlindedPayment.toRouteHints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
logPrefix := fmt.Sprintf("PaymentSession(%x):", p.Identifier())
|
||||
|
||||
return &paymentSession{
|
||||
|
@ -389,6 +402,11 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var blindedPath *sphinx.BlindedPath
|
||||
if p.payment.BlindedPayment != nil {
|
||||
blindedPath = p.payment.BlindedPayment.BlindedPath
|
||||
}
|
||||
|
||||
// With the next candidate path found, we'll attempt to turn
|
||||
// this into a route by applying the time-lock and fee
|
||||
// requirements.
|
||||
|
@ -401,7 +419,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|||
records: p.payment.DestCustomRecords,
|
||||
paymentAddr: p.payment.PaymentAddr,
|
||||
metadata: p.payment.Metadata,
|
||||
}, nil,
|
||||
}, blindedPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -922,9 +922,19 @@ type LightningPayment struct {
|
|||
// NOTE: This is optional unless required by the payment. When providing
|
||||
// multiple routes, ensure the hop hints within each route are chained
|
||||
// together and sorted in forward order in order to reach the
|
||||
// destination successfully.
|
||||
// destination successfully. This is mutually exclusive to the
|
||||
// BlindedPayment field.
|
||||
RouteHints [][]zpay32.HopHint
|
||||
|
||||
// BlindedPayment holds the information about a blinded path to the
|
||||
// payment recipient. This is mutually exclusive to the RouteHints
|
||||
// field.
|
||||
//
|
||||
// NOTE: a recipient may provide multiple blinded payment paths in the
|
||||
// same invoice. Currently, LND will only attempt to use the first one.
|
||||
// A future PR will handle multiple blinded payment paths.
|
||||
BlindedPayment *BlindedPayment
|
||||
|
||||
// OutgoingChannelIDs is the list of channels that are allowed for the
|
||||
// first hop. If nil, any channel may be used.
|
||||
OutgoingChannelIDs []uint64
|
||||
|
|
28
rpcserver.go
28
rpcserver.go
|
@ -5110,6 +5110,7 @@ type rpcPaymentIntent struct {
|
|||
paymentAddr *[32]byte
|
||||
payReq []byte
|
||||
metadata []byte
|
||||
blindedPayment *routing.BlindedPayment
|
||||
|
||||
destCustomRecords record.CustomSet
|
||||
|
||||
|
@ -5245,6 +5246,32 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme
|
|||
payIntent.paymentAddr = payReq.PaymentAddr
|
||||
payIntent.metadata = payReq.Metadata
|
||||
|
||||
if len(payReq.BlindedPaymentPaths) > 0 {
|
||||
// NOTE: Currently we only choose a single payment path.
|
||||
// This will be updated in a future PR to handle
|
||||
// multiple blinded payment paths.
|
||||
path := payReq.BlindedPaymentPaths[0]
|
||||
if len(path.Hops) == 0 {
|
||||
return payIntent, fmt.Errorf("a blinded " +
|
||||
"payment must have at least 1 hop")
|
||||
}
|
||||
|
||||
finalHop := path.Hops[len(path.Hops)-1]
|
||||
payIntent.blindedPayment =
|
||||
routerrpc.MarshalBlindedPayment(path)
|
||||
|
||||
// Replace the target node with the blinded public key
|
||||
// of the blinded path's final node.
|
||||
copy(
|
||||
payIntent.dest[:],
|
||||
finalHop.BlindedNodePub.SerializeCompressed(),
|
||||
)
|
||||
|
||||
if !payReq.BlindedPaymentPaths[0].Features.IsEmpty() {
|
||||
payIntent.destFeatures = path.Features.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateDest(payIntent.dest); err != nil {
|
||||
return payIntent, err
|
||||
}
|
||||
|
@ -5399,6 +5426,7 @@ func (r *rpcServer) dispatchPaymentIntent(
|
|||
DestFeatures: payIntent.destFeatures,
|
||||
PaymentAddr: payIntent.paymentAddr,
|
||||
Metadata: payIntent.metadata,
|
||||
BlindedPayment: payIntent.blindedPayment,
|
||||
|
||||
// Don't enable multi-part payments on the main rpc.
|
||||
// Users need to use routerrpc for that.
|
||||
|
|
Loading…
Add table
Reference in a new issue