Skip to content

Commit 9decf80

Browse files
authored
Merge pull request #8735 from ellemouton/rb-receives
[2/4] Route Blinding Receives: Receive and send to a single blinded path in an invoice.
2 parents b7c59b3 + c490279 commit 9decf80

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+5615
-1865
lines changed

channeldb/payment_control.go

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ var (
6767

6868
// ErrValueMismatch is returned if we try to register a non-MPP attempt
6969
// with an amount that doesn't match the payment amount.
70-
ErrValueMismatch = errors.New("attempted value doesn't match payment" +
70+
ErrValueMismatch = errors.New("attempted value doesn't match payment " +
7171
"amount")
7272

7373
// ErrValueExceedsAmt is returned if we try to register an attempt that
7474
// would take the total sent amount above the payment amount.
75-
ErrValueExceedsAmt = errors.New("attempted value exceeds payment" +
75+
ErrValueExceedsAmt = errors.New("attempted value exceeds payment " +
7676
"amount")
7777

7878
// ErrNonMPPayment is returned if we try to register an MPP attempt for
@@ -83,6 +83,17 @@ var (
8383
// a payment that already has an MPP attempt registered.
8484
ErrMPPayment = errors.New("payment has MPP attempts")
8585

86+
// ErrMPPRecordInBlindedPayment is returned if we try to register an
87+
// attempt with an MPP record for a payment to a blinded path.
88+
ErrMPPRecordInBlindedPayment = errors.New("blinded payment cannot " +
89+
"contain MPP records")
90+
91+
// ErrBlindedPaymentTotalAmountMismatch is returned if we try to
92+
// register an HTLC shard to a blinded route where the total amount
93+
// doesn't match existing shards.
94+
ErrBlindedPaymentTotalAmountMismatch = errors.New("blinded path " +
95+
"total amount mismatch")
96+
8697
// ErrMPPPaymentAddrMismatch is returned if we try to register an MPP
8798
// shard where the payment address doesn't match existing shards.
8899
ErrMPPPaymentAddrMismatch = errors.New("payment address mismatch")
@@ -96,7 +107,7 @@ var (
96107
// attempt to a payment that has at least one of its HTLCs settled.
97108
ErrPaymentPendingSettled = errors.New("payment has settled htlcs")
98109

99-
// ErrPaymentAlreadyFailed is returned when we try to add a new attempt
110+
// ErrPaymentPendingFailed is returned when we try to add a new attempt
100111
// to a payment that already has a failure reason.
101112
ErrPaymentPendingFailed = errors.New("payment has failure reason")
102113

@@ -334,12 +345,48 @@ func (p *PaymentControl) RegisterAttempt(paymentHash lntypes.Hash,
334345
return err
335346
}
336347

348+
// If the final hop has encrypted data, then we know this is a
349+
// blinded payment. In blinded payments, MPP records are not set
350+
// for split payments and the recipient is responsible for using
351+
// a consistent PathID across the various encrypted data
352+
// payloads that we received from them for this payment. All we
353+
// need to check is that the total amount field for each HTLC
354+
// in the split payment is correct.
355+
isBlinded := len(attempt.Route.FinalHop().EncryptedData) != 0
356+
337357
// Make sure any existing shards match the new one with regards
338358
// to MPP options.
339359
mpp := attempt.Route.FinalHop().MPP
360+
361+
// MPP records should not be set for attempts to blinded paths.
362+
if isBlinded && mpp != nil {
363+
return ErrMPPRecordInBlindedPayment
364+
}
365+
340366
for _, h := range payment.InFlightHTLCs() {
341367
hMpp := h.Route.FinalHop().MPP
342368

369+
// If this is a blinded payment, then no existing HTLCs
370+
// should have MPP records.
371+
if isBlinded && hMpp != nil {
372+
return ErrMPPRecordInBlindedPayment
373+
}
374+
375+
// If this is a blinded payment, then we just need to
376+
// check that the TotalAmtMsat field for this shard
377+
// is equal to that of any other shard in the same
378+
// payment.
379+
if isBlinded {
380+
if attempt.Route.FinalHop().TotalAmtMsat !=
381+
h.Route.FinalHop().TotalAmtMsat {
382+
383+
//nolint:lll
384+
return ErrBlindedPaymentTotalAmountMismatch
385+
}
386+
387+
continue
388+
}
389+
343390
switch {
344391
// We tried to register a non-MPP attempt for a MPP
345392
// payment.
@@ -367,9 +414,10 @@ func (p *PaymentControl) RegisterAttempt(paymentHash lntypes.Hash,
367414
}
368415

369416
// If this is a non-MPP attempt, it must match the total amount
370-
// exactly.
417+
// exactly. Note that a blinded payment is considered an MPP
418+
// attempt.
371419
amt := attempt.Route.ReceiverAmt()
372-
if mpp == nil && amt != payment.Info.Value {
420+
if !isBlinded && mpp == nil && amt != payment.Info.Value {
373421
return ErrValueMismatch
374422
}
375423

cmd/lncli/cmd_invoice.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ var addInvoiceCommand = cli.Command{
8282
Usage: "creates an AMP invoice. If true, preimage " +
8383
"should not be set.",
8484
},
85+
cli.BoolFlag{
86+
Name: "blind",
87+
Usage: "creates an invoice that contains blinded " +
88+
"paths. Note that invoices with blinded " +
89+
"paths will be signed using a random " +
90+
"ephemeral key so as not to reveal the real " +
91+
"node ID of this node.",
92+
},
8593
},
8694
Action: actionDecorator(addInvoice),
8795
}
@@ -127,6 +135,11 @@ func addInvoice(ctx *cli.Context) error {
127135
return fmt.Errorf("unable to parse description_hash: %w", err)
128136
}
129137

138+
if ctx.IsSet("private") && ctx.IsSet("blind") {
139+
return fmt.Errorf("cannot include both route hints and " +
140+
"blinded paths in the same invoice")
141+
}
142+
130143
invoice := &lnrpc.Invoice{
131144
Memo: ctx.String("memo"),
132145
RPreimage: preimage,
@@ -138,6 +151,7 @@ func addInvoice(ctx *cli.Context) error {
138151
CltvExpiry: ctx.Uint64("cltv_expiry_delta"),
139152
Private: ctx.Bool("private"),
140153
IsAmp: ctx.Bool("amp"),
154+
Blind: ctx.Bool("blind"),
141155
}
142156

143157
resp, err := client.AddInvoice(ctxc, invoice)

config.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,15 @@ func DefaultConfig() Config {
681681
Invoices: &lncfg.Invoices{
682682
HoldExpiryDelta: lncfg.DefaultHoldInvoiceExpiryDelta,
683683
},
684+
Routing: &lncfg.Routing{
685+
BlindedPaths: lncfg.BlindedPaths{
686+
MinNumRealHops: lncfg.DefaultMinNumRealBlindedPathHops,
687+
NumHops: lncfg.DefaultNumBlindedPathHops,
688+
MaxNumPaths: lncfg.DefaultMaxNumBlindedPaths,
689+
PolicyIncreaseMultiplier: lncfg.DefaultBlindedPathPolicyIncreaseMultiplier,
690+
PolicyDecreaseMultiplier: lncfg.DefaultBlindedPathPolicyDecreaseMultiplier,
691+
},
692+
},
684693
MaxOutgoingCltvExpiry: htlcswitch.DefaultMaxOutgoingCltvExpiry,
685694
MaxChannelFeeAllocation: htlcswitch.DefaultMaxLinkFeeAllocation,
686695
MaxCommitFeeRateAnchors: lnwallet.DefaultAnchorsCommitMaxFeeRateSatPerVByte,
@@ -1656,18 +1665,6 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
16561665
return nil, mkErr("error parsing gossip syncer: %v", err)
16571666
}
16581667

1659-
// Log a warning if our expiry delta is not greater than our incoming
1660-
// broadcast delta. We do not fail here because this value may be set
1661-
// to zero to intentionally keep lnd's behavior unchanged from when we
1662-
// didn't auto-cancel these invoices.
1663-
if cfg.Invoices.HoldExpiryDelta <= lncfg.DefaultIncomingBroadcastDelta {
1664-
ltndLog.Warnf("Invoice hold expiry delta: %v <= incoming "+
1665-
"delta: %v, accepted hold invoices will force close "+
1666-
"channels if they are not canceled manually",
1667-
cfg.Invoices.HoldExpiryDelta,
1668-
lncfg.DefaultIncomingBroadcastDelta)
1669-
}
1670-
16711668
// If the experimental protocol options specify any protocol messages
16721669
// that we want to handle as custom messages, set them now.
16731670
customMsg := cfg.ProtocolOptions.CustomMessageOverrides()
@@ -1690,6 +1687,8 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
16901687
cfg.RemoteSigner,
16911688
cfg.Sweeper,
16921689
cfg.Htlcswitch,
1690+
cfg.Invoices,
1691+
cfg.Routing,
16931692
)
16941693
if err != nil {
16951694
return nil, err

docs/release-notes/release-notes-0.18.3.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@
117117
* [Groundwork](https://github.com/lightningnetwork/lnd/pull/8752) in preparation
118118
for implementing route blinding receives.
119119

120+
* [Generate and send to](https://github.com/lightningnetwork/lnd/pull/8735) an
121+
invoice with blinded paths. With this, the `--blind` flag can be used with
122+
the `lncli addinvoice` command to instruct LND to include blinded paths in the
123+
invoice.
124+
120125
## Testing
121126
## Database
122127

feature/default_sets.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,7 @@ var defaultSetDesc = setDesc{
9292
SetInit: {}, // I
9393
SetNodeAnn: {}, // N
9494
},
95+
lnwire.Bolt11BlindedPathsOptional: {
96+
SetInvoice: {}, // I
97+
},
9598
}

feature/deps.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ var deps = depDesc{
8282
lnwire.RouteBlindingOptional: {
8383
lnwire.TLVOnionPayloadOptional: {},
8484
},
85-
lnwire.RouteBlindingRequired: {
86-
lnwire.TLVOnionPayloadRequired: {},
85+
lnwire.Bolt11BlindedPathsOptional: {
86+
lnwire.RouteBlindingOptional: {},
8787
},
8888
}
8989

feature/manager.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
128128
raw.Unset(lnwire.MPPRequired)
129129
raw.Unset(lnwire.RouteBlindingOptional)
130130
raw.Unset(lnwire.RouteBlindingRequired)
131+
raw.Unset(lnwire.Bolt11BlindedPathsOptional)
132+
raw.Unset(lnwire.Bolt11BlindedPathsRequired)
131133
raw.Unset(lnwire.AMPOptional)
132134
raw.Unset(lnwire.AMPRequired)
133135
raw.Unset(lnwire.KeysendOptional)
@@ -187,6 +189,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
187189
if cfg.NoRouteBlinding {
188190
raw.Unset(lnwire.RouteBlindingOptional)
189191
raw.Unset(lnwire.RouteBlindingRequired)
192+
raw.Unset(lnwire.Bolt11BlindedPathsOptional)
193+
raw.Unset(lnwire.Bolt11BlindedPathsRequired)
190194
}
191195
for _, custom := range cfg.CustomFeatures[set] {
192196
if custom > set.Maximum() {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ require (
3232
github.com/kkdai/bstream v1.0.0
3333
github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd
3434
github.com/lightninglabs/neutrino/cache v1.1.2
35-
github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f
35+
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb
3636
github.com/lightningnetwork/lnd/cert v1.2.2
3737
github.com/lightningnetwork/lnd/clock v1.1.1
3838
github.com/lightningnetwork/lnd/fn v1.2.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,8 @@ github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3
444444
github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo=
445445
github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display h1:pRdza2wleRN1L2fJXd6ZoQ9ZegVFTAb2bOQfruJPKcY=
446446
github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
447-
github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f h1:Pua7+5TcFEJXIIZ1I2YAUapmbcttmLj4TTi786bIi3s=
448-
github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI=
447+
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY=
448+
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI=
449449
github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI=
450450
github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U=
451451
github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0=

htlcswitch/circuit_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88

99
"github.com/btcsuite/btcd/btcec/v2"
1010
"github.com/btcsuite/btcd/btcutil"
11-
bitcoinCfg "github.com/btcsuite/btcd/chaincfg"
1211
sphinx "github.com/lightningnetwork/lightning-onion"
1312
"github.com/lightningnetwork/lnd/channeldb"
1413
"github.com/lightningnetwork/lnd/htlcswitch"
@@ -87,7 +86,7 @@ func initTestExtracter() {
8786
func newOnionProcessor(t *testing.T) *hop.OnionProcessor {
8887
sphinxRouter := sphinx.NewRouter(
8988
&keychain.PrivKeyECDH{PrivKey: sphinxPrivKey},
90-
&bitcoinCfg.SimNetParams, sphinx.NewMemoryReplayLog(),
89+
sphinx.NewMemoryReplayLog(),
9190
)
9291

9392
if err := sphinxRouter.Start(); err != nil {

0 commit comments

Comments
 (0)