Skip to content

Commit 459ee9b

Browse files
authored
Merge pull request #8976 from ellemouton/rb-follow-ups
route blinding: follow ups
2 parents a449a5d + f1a58dc commit 459ee9b

File tree

19 files changed

+2532
-1900
lines changed

19 files changed

+2532
-1900
lines changed

cmd/lncli/cmd_invoice.go

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,32 @@ var addInvoiceCommand = cli.Command{
9090
"ephemeral key so as not to reveal the real " +
9191
"node ID of this node.",
9292
},
93+
cli.UintFlag{
94+
Name: "min_real_blinded_hops",
95+
Usage: "The minimum number of real hops to use in a " +
96+
"blinded path. This option will only be used " +
97+
"if `--blind` has also been set.",
98+
},
99+
cli.UintFlag{
100+
Name: "num_blinded_hops",
101+
Usage: "The number of hops to use for each " +
102+
"blinded path included in the invoice. This " +
103+
"option will only be used if `--blind` has " +
104+
"also been set. Dummy hops will be used to " +
105+
"pad paths shorter than this.",
106+
},
107+
cli.UintFlag{
108+
Name: "max_blinded_paths",
109+
Usage: "The maximum number of blinded paths to add " +
110+
"to an invoice. This option will only be " +
111+
"used if `--blind` has also been set.",
112+
},
113+
cli.StringSliceFlag{
114+
Name: "blinded_path_omit_node",
115+
Usage: "The pub key (in hex) of a node not to " +
116+
"use on a blinded path. The flag may be " +
117+
"specified multiple times.",
118+
},
93119
},
94120
Action: actionDecorator(addInvoice),
95121
}
@@ -140,18 +166,24 @@ func addInvoice(ctx *cli.Context) error {
140166
"blinded paths in the same invoice")
141167
}
142168

169+
blindedPathCfg, err := parseBlindedPathCfg(ctx)
170+
if err != nil {
171+
return fmt.Errorf("could not parse blinded path config: %w",
172+
err)
173+
}
174+
143175
invoice := &lnrpc.Invoice{
144-
Memo: ctx.String("memo"),
145-
RPreimage: preimage,
146-
Value: amt,
147-
ValueMsat: amtMsat,
148-
DescriptionHash: descHash,
149-
FallbackAddr: ctx.String("fallback_addr"),
150-
Expiry: ctx.Int64("expiry"),
151-
CltvExpiry: ctx.Uint64("cltv_expiry_delta"),
152-
Private: ctx.Bool("private"),
153-
IsAmp: ctx.Bool("amp"),
154-
Blind: ctx.Bool("blind"),
176+
Memo: ctx.String("memo"),
177+
RPreimage: preimage,
178+
Value: amt,
179+
ValueMsat: amtMsat,
180+
DescriptionHash: descHash,
181+
FallbackAddr: ctx.String("fallback_addr"),
182+
Expiry: ctx.Int64("expiry"),
183+
CltvExpiry: ctx.Uint64("cltv_expiry_delta"),
184+
Private: ctx.Bool("private"),
185+
IsAmp: ctx.Bool("amp"),
186+
BlindedPathConfig: blindedPathCfg,
155187
}
156188

157189
resp, err := client.AddInvoice(ctxc, invoice)
@@ -164,6 +196,51 @@ func addInvoice(ctx *cli.Context) error {
164196
return nil
165197
}
166198

199+
func parseBlindedPathCfg(ctx *cli.Context) (*lnrpc.BlindedPathConfig, error) {
200+
if !ctx.Bool("blind") {
201+
if ctx.IsSet("min_real_blinded_hops") ||
202+
ctx.IsSet("num_blinded_hops") ||
203+
ctx.IsSet("max_blinded_paths") ||
204+
ctx.IsSet("blinded_path_omit_node") {
205+
206+
return nil, fmt.Errorf("blinded path options are " +
207+
"only used if the `--blind` options is set")
208+
}
209+
210+
return nil, nil
211+
}
212+
213+
var blindCfg lnrpc.BlindedPathConfig
214+
215+
if ctx.IsSet("min_real_blinded_hops") {
216+
minNumRealHops := uint32(ctx.Uint("min_real_blinded_hops"))
217+
blindCfg.MinNumRealHops = &minNumRealHops
218+
}
219+
220+
if ctx.IsSet("num_blinded_hops") {
221+
numHops := uint32(ctx.Uint("num_blinded_hops"))
222+
blindCfg.NumHops = &numHops
223+
}
224+
225+
if ctx.IsSet("max_blinded_paths") {
226+
maxPaths := uint32(ctx.Uint("max_blinded_paths"))
227+
blindCfg.MaxNumPaths = &maxPaths
228+
}
229+
230+
for _, pubKey := range ctx.StringSlice("blinded_path_omit_node") {
231+
pubKeyBytes, err := hex.DecodeString(pubKey)
232+
if err != nil {
233+
return nil, err
234+
}
235+
236+
blindCfg.NodeOmissionList = append(
237+
blindCfg.NodeOmissionList, pubKeyBytes,
238+
)
239+
}
240+
241+
return &blindCfg, nil
242+
}
243+
167244
var lookupInvoiceCommand = cli.Command{
168245
Name: "lookupinvoice",
169246
Category: "Invoices",

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ commitment when the channel was force closed.
206206
* Add the ability to [send to use multiple blinded payment
207207
paths](https://github.com/lightningnetwork/lnd/pull/8764) in an MP payment.
208208

209+
* [Improve route blinding invoice generation
210+
UX](https://github.com/lightningnetwork/lnd/pull/8976) by making various
211+
params configurable on a per-RPC basis.
212+
209213
## Testing
210214
## Database
211215

itest/lnd_route_blinding_test.go

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -363,13 +363,7 @@ func (b *blindedForwardTest) setupNetwork(ctx context.Context,
363363
require.NoError(b.ht, err, "interceptor")
364364
}
365365

366-
// Restrict Dave so that he only ever creates a single blinded path from
367-
// Bob to himself.
368-
b.dave = b.ht.NewNode("Dave", []string{
369-
"--bitcoin.timelockdelta=18",
370-
"--routing.blinding.min-num-real-hops=2",
371-
"--routing.blinding.num-hops=2",
372-
})
366+
b.dave = b.ht.NewNode("Dave", []string{"--bitcoin.timelockdelta=18"})
373367

374368
b.channels = setupFourHopNetwork(b.ht, b.carol, b.dave)
375369
}
@@ -378,11 +372,20 @@ func (b *blindedForwardTest) setupNetwork(ctx context.Context,
378372
// acting as the introduction point.
379373
func (b *blindedForwardTest) buildBlindedPath() *lnrpc.BlindedPaymentPath {
380374
// Let Dave add a blinded invoice.
375+
// Add restrictions so that he only ever creates a single blinded path
376+
// from Bob to himself.
377+
var (
378+
minNumRealHops uint32 = 2
379+
numHops uint32 = 2
380+
)
381381
invoice := b.dave.RPC.AddInvoice(&lnrpc.Invoice{
382382
RPreimage: b.preimage[:],
383383
Memo: "test",
384384
ValueMsat: 10_000_000,
385-
Blind: true,
385+
BlindedPathConfig: &lnrpc.BlindedPathConfig{
386+
MinNumRealHops: &minNumRealHops,
387+
NumHops: &numHops,
388+
},
386389
})
387390

388391
// Assert that only one blinded path is selected and that it contains
@@ -613,29 +616,37 @@ func testBlindedRouteInvoices(ht *lntest.HarnessTest) {
613616
testCase.setupNetwork(ctx, false)
614617

615618
// Let Dave add a blinded invoice.
619+
// Add restrictions so that he only ever creates a single blinded path
620+
// from Bob to himself.
621+
var (
622+
minNumRealHops uint32 = 2
623+
numHops uint32 = 2
624+
)
616625
invoice := testCase.dave.RPC.AddInvoice(&lnrpc.Invoice{
617626
Memo: "test",
618627
ValueMsat: 10_000_000,
619-
Blind: true,
628+
BlindedPathConfig: &lnrpc.BlindedPathConfig{
629+
MinNumRealHops: &minNumRealHops,
630+
NumHops: &numHops,
631+
},
620632
})
621633

622634
// Now let Alice pay the invoice.
623635
ht.CompletePaymentRequests(ht.Alice, []string{invoice.PaymentRequest})
624636

625-
// Restart Dave with blinded path restrictions that will result in him
626-
// creating a blinded path that uses himself as the introduction node.
627-
ht.RestartNodeWithExtraArgs(testCase.dave, []string{
628-
"--routing.blinding.min-num-real-hops=0",
629-
"--routing.blinding.num-hops=0",
630-
})
631-
ht.EnsureConnected(testCase.dave, testCase.carol)
632-
633637
// Let Dave add a blinded invoice.
634638
// Once again let Dave create a blinded invoice.
639+
// This time, add path restrictions that will result in him
640+
// creating a blinded path that uses himself as the introduction node.
641+
minNumRealHops = 0
642+
numHops = 0
635643
invoice = testCase.dave.RPC.AddInvoice(&lnrpc.Invoice{
636644
Memo: "test",
637645
ValueMsat: 10_000_000,
638-
Blind: true,
646+
BlindedPathConfig: &lnrpc.BlindedPathConfig{
647+
MinNumRealHops: &minNumRealHops,
648+
NumHops: &numHops,
649+
},
639650
})
640651

641652
// Assert that it contains a single blinded path with only an
@@ -898,12 +909,7 @@ func testMPPToSingleBlindedPath(ht *lntest.HarnessTest) {
898909
// nodes.
899910
alice, bob := ht.Alice, ht.Bob
900911

901-
// Restrict Dave so that he only ever chooses the Carol->Dave path for
902-
// a blinded route.
903-
dave := ht.NewNode("dave", []string{
904-
"--routing.blinding.min-num-real-hops=1",
905-
"--routing.blinding.num-hops=1",
906-
})
912+
dave := ht.NewNode("dave", nil)
907913
carol := ht.NewNode("carol", nil)
908914
eve := ht.NewNode("eve", nil)
909915

@@ -984,10 +990,19 @@ func testMPPToSingleBlindedPath(ht *lntest.HarnessTest) {
984990
}
985991

986992
// Make Dave create an invoice with a blinded path for Alice to pay.
993+
// Restrict the blinded path config such that Dave only ever chooses
994+
// the Carol->Dave path for a blinded route.
995+
var (
996+
numHops uint32 = 1
997+
minNumRealHops uint32 = 1
998+
)
987999
invoice := &lnrpc.Invoice{
9881000
Memo: "test",
9891001
Value: int64(paymentAmt),
990-
Blind: true,
1002+
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1003+
NumHops: &numHops,
1004+
MinNumRealHops: &minNumRealHops,
1005+
},
9911006
}
9921007
invoiceResp := dave.RPC.AddInvoice(invoice)
9931008

@@ -1095,12 +1110,7 @@ func testBlindedRouteDummyHops(ht *lntest.HarnessTest) {
10951110
"--protocol.no-route-blinding",
10961111
})
10971112

1098-
// Configure Dave so that all blinded paths always contain 2 hops and
1099-
// so that there is no minimum number of real hops.
1100-
dave := ht.NewNode("dave", []string{
1101-
"--routing.blinding.min-num-real-hops=0",
1102-
"--routing.blinding.num-hops=2",
1103-
})
1113+
dave := ht.NewNode("dave", nil)
11041114

11051115
ht.EnsureConnected(alice, bob)
11061116
ht.EnsureConnected(bob, carol)
@@ -1150,10 +1160,19 @@ func testBlindedRouteDummyHops(ht *lntest.HarnessTest) {
11501160
}
11511161

11521162
// Make Dave create an invoice with a blinded path for Alice to pay.
1163+
// Configure the invoice so that all blinded paths always contain 2 hops
1164+
// and so that there is no minimum number of real hops.
1165+
var (
1166+
minNumRealHops uint32 = 0
1167+
numHops uint32 = 2
1168+
)
11531169
invoice := &lnrpc.Invoice{
11541170
Memo: "test",
11551171
Value: int64(paymentAmt),
1156-
Blind: true,
1172+
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1173+
MinNumRealHops: &minNumRealHops,
1174+
NumHops: &numHops,
1175+
},
11571176
}
11581177
invoiceResp := dave.RPC.AddInvoice(invoice)
11591178

@@ -1178,27 +1197,29 @@ func testBlindedRouteDummyHops(ht *lntest.HarnessTest) {
11781197
require.Equal(ht, lnrpc.Invoice_SETTLED, inv.State)
11791198

11801199
// Let's also test the case where Dave is not the introduction node.
1181-
// We restart Carol so that she supports route blinding. We also restart
1182-
// Dave and force a minimum of 1 real blinded hop. We keep the number
1183-
// of hops to 2 meaning that one dummy hop should be added.
1200+
// We restart Carol so that she supports route blinding.
11841201
ht.RestartNodeWithExtraArgs(carol, nil)
1185-
ht.RestartNodeWithExtraArgs(dave, []string{
1186-
"--routing.blinding.min-num-real-hops=1",
1187-
"--routing.blinding.num-hops=2",
1188-
})
11891202
ht.EnsureConnected(bob, carol)
11901203
ht.EnsureConnected(carol, dave)
11911204

11921205
// Make Dave create an invoice with a blinded path for Alice to pay.
1206+
// This time, configure the invoice so that there is always a minimum
1207+
// of 1 real blinded hop. We keep the number of total hops to 2 meaning
1208+
// that one dummy hop should be added.
1209+
minNumRealHops = 1
1210+
invoice = &lnrpc.Invoice{
1211+
Memo: "test",
1212+
Value: int64(paymentAmt),
1213+
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1214+
MinNumRealHops: &minNumRealHops,
1215+
NumHops: &numHops,
1216+
},
1217+
}
11931218
invoiceResp = dave.RPC.AddInvoice(invoice)
11941219

11951220
// Assert that it contains a single blinded path and that the
11961221
// introduction node is Carol.
11971222
payReq = dave.RPC.DecodePayReq(invoiceResp.PaymentRequest)
1198-
for _, path := range payReq.BlindedPaths {
1199-
ht.Logf("intro node: %x", path.BlindedPath.IntroductionNode)
1200-
}
1201-
12021223
require.Len(ht, payReq.BlindedPaths, 1)
12031224

12041225
// The total number of hop payloads is 3: one for the introduction node
@@ -1248,10 +1269,7 @@ func testMPPToMultipleBlindedPaths(ht *lntest.HarnessTest) {
12481269

12491270
// Create a four-node context consisting of Alice, Bob and three new
12501271
// nodes.
1251-
dave := ht.NewNode("dave", []string{
1252-
"--routing.blinding.min-num-real-hops=1",
1253-
"--routing.blinding.num-hops=1",
1254-
})
1272+
dave := ht.NewNode("dave", nil)
12551273
carol := ht.NewNode("carol", nil)
12561274

12571275
// Connect nodes to ensure propagation of channels.
@@ -1311,10 +1329,17 @@ func testMPPToMultipleBlindedPaths(ht *lntest.HarnessTest) {
13111329
// Ok now make a payment that must be split to succeed.
13121330

13131331
// Make Dave create an invoice for Alice to pay
1332+
var (
1333+
minNumRealHops uint32 = 1
1334+
numHops uint32 = 1
1335+
)
13141336
invoice := &lnrpc.Invoice{
13151337
Memo: "test",
13161338
Value: int64(paymentAmt),
1317-
Blind: true,
1339+
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1340+
MinNumRealHops: &minNumRealHops,
1341+
NumHops: &numHops,
1342+
},
13181343
}
13191344
invoiceResp := dave.RPC.AddInvoice(invoice)
13201345

0 commit comments

Comments
 (0)