mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 17:55:36 +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.PaymentAddr = payAddr
|
||||||
payIntent.PaymentRequest = []byte(rpcPayReq.PaymentRequest)
|
payIntent.PaymentRequest = []byte(rpcPayReq.PaymentRequest)
|
||||||
payIntent.Metadata = payReq.Metadata
|
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 {
|
} else {
|
||||||
// Otherwise, If the payment request field was not specified
|
// Otherwise, If the payment request field was not specified
|
||||||
// (and a custom route wasn't specified), construct the payment
|
// (and a custom route wasn't specified), construct the payment
|
||||||
|
@ -1137,6 +1163,26 @@ func (r *RouterBackend) extractIntentFromSendRequest(
|
||||||
return payIntent, nil
|
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.
|
// unmarshallRouteHints unmarshalls a list of route hints.
|
||||||
func unmarshallRouteHints(rpcRouteHints []*lnrpc.RouteHint) (
|
func unmarshallRouteHints(rpcRouteHints []*lnrpc.RouteHint) (
|
||||||
[][]zpay32.HopHint, error) {
|
[][]zpay32.HopHint, error) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btclog"
|
"github.com/btcsuite/btclog"
|
||||||
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/build"
|
"github.com/lightningnetwork/lnd/build"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
|
@ -205,6 +206,18 @@ func newPaymentSession(p *LightningPayment, selfNode route.Vertex,
|
||||||
return nil, err
|
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())
|
logPrefix := fmt.Sprintf("PaymentSession(%x):", p.Identifier())
|
||||||
|
|
||||||
return &paymentSession{
|
return &paymentSession{
|
||||||
|
@ -389,6 +402,11 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
||||||
return nil, err
|
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
|
// With the next candidate path found, we'll attempt to turn
|
||||||
// this into a route by applying the time-lock and fee
|
// this into a route by applying the time-lock and fee
|
||||||
// requirements.
|
// requirements.
|
||||||
|
@ -401,7 +419,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
||||||
records: p.payment.DestCustomRecords,
|
records: p.payment.DestCustomRecords,
|
||||||
paymentAddr: p.payment.PaymentAddr,
|
paymentAddr: p.payment.PaymentAddr,
|
||||||
metadata: p.payment.Metadata,
|
metadata: p.payment.Metadata,
|
||||||
}, nil,
|
}, blindedPath,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -922,9 +922,19 @@ type LightningPayment struct {
|
||||||
// NOTE: This is optional unless required by the payment. When providing
|
// NOTE: This is optional unless required by the payment. When providing
|
||||||
// multiple routes, ensure the hop hints within each route are chained
|
// multiple routes, ensure the hop hints within each route are chained
|
||||||
// together and sorted in forward order in order to reach the
|
// 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
|
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
|
// OutgoingChannelIDs is the list of channels that are allowed for the
|
||||||
// first hop. If nil, any channel may be used.
|
// first hop. If nil, any channel may be used.
|
||||||
OutgoingChannelIDs []uint64
|
OutgoingChannelIDs []uint64
|
||||||
|
|
28
rpcserver.go
28
rpcserver.go
|
@ -5110,6 +5110,7 @@ type rpcPaymentIntent struct {
|
||||||
paymentAddr *[32]byte
|
paymentAddr *[32]byte
|
||||||
payReq []byte
|
payReq []byte
|
||||||
metadata []byte
|
metadata []byte
|
||||||
|
blindedPayment *routing.BlindedPayment
|
||||||
|
|
||||||
destCustomRecords record.CustomSet
|
destCustomRecords record.CustomSet
|
||||||
|
|
||||||
|
@ -5245,6 +5246,32 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme
|
||||||
payIntent.paymentAddr = payReq.PaymentAddr
|
payIntent.paymentAddr = payReq.PaymentAddr
|
||||||
payIntent.metadata = payReq.Metadata
|
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 {
|
if err := validateDest(payIntent.dest); err != nil {
|
||||||
return payIntent, err
|
return payIntent, err
|
||||||
}
|
}
|
||||||
|
@ -5399,6 +5426,7 @@ func (r *rpcServer) dispatchPaymentIntent(
|
||||||
DestFeatures: payIntent.destFeatures,
|
DestFeatures: payIntent.destFeatures,
|
||||||
PaymentAddr: payIntent.paymentAddr,
|
PaymentAddr: payIntent.paymentAddr,
|
||||||
Metadata: payIntent.metadata,
|
Metadata: payIntent.metadata,
|
||||||
|
BlindedPayment: payIntent.blindedPayment,
|
||||||
|
|
||||||
// Don't enable multi-part payments on the main rpc.
|
// Don't enable multi-part payments on the main rpc.
|
||||||
// Users need to use routerrpc for that.
|
// Users need to use routerrpc for that.
|
||||||
|
|
Loading…
Add table
Reference in a new issue