Skip to content

Commit 1fcf112

Browse files
authored
Merge pull request #9925 from ellemouton/rbIncomingFollowup
routing: clean-up & fix blinded path incoming chained channel logic
2 parents 53b249e + a4459cc commit 1fcf112

File tree

5 files changed

+282
-186
lines changed

5 files changed

+282
-186
lines changed

docs/release-notes/release-notes-0.20.0.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ circuit. The indices are only available for forwarding events saved after v0.20.
3737

3838

3939
* The `lncli addinvoice --blind` command now has the option to include a
40-
[chained channels](https://github.com/lightningnetwork/lnd/pull/9127)
40+
chained channels [1](https://github.com/lightningnetwork/lnd/pull/9127)
41+
[2](https://github.com/lightningnetwork/lnd/pull/9925)
4142
incoming list `--blinded_path_incoming_channel_list` which gives users the
4243
control of specifying the channels they prefer to receive the payment on. With
4344
the option to specify multiple channels this control can be extended to

itest/lnd_route_blinding_test.go

Lines changed: 89 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,25 +1428,55 @@ func testBlindedPaymentHTLCReForward(ht *lntest.HarnessTest) {
14281428
// pre-specified.
14291429
func testPartiallySpecifiedBlindedPath(ht *lntest.HarnessTest) {
14301430
// Create a six hop network:
1431-
// Alice -> Bob -> Carol -> Dave -> Eve -> Frank.
1432-
chanAmt := btcutil.Amount(100000)
1433-
cfgs := [][]string{nil, nil, nil, nil, nil, nil}
1431+
// Alice -> Bob -> Carol -> Dave -> Eve -> Frank.
1432+
// Carol will be the node generating invoices containing blinded paths.
14341433
chanPoints, nodes := ht.CreateSimpleNetwork(
1435-
cfgs, lntest.OpenChannelParams{Amt: chanAmt},
1434+
[][]string{nil, nil, nil, nil, nil, nil},
1435+
lntest.OpenChannelParams{
1436+
Amt: chanAmt,
1437+
PushAmt: chanAmt / 2,
1438+
},
14361439
)
14371440

1438-
alice, bob, carol, dave, eve := nodes[0], nodes[1], nodes[2], nodes[3],
1439-
nodes[4]
1440-
1441-
chanPointAliceBob, chanPointBobCarol, chanPointCarolDave :=
1442-
chanPoints[0], chanPoints[1], chanPoints[2]
1441+
var (
1442+
alice = nodes[0]
1443+
bob = nodes[1]
1444+
carol = nodes[2]
1445+
dave = nodes[3]
1446+
eve = nodes[4]
1447+
frank = nodes[5]
1448+
)
14431449

14441450
// Lookup full channel info so that we have channel ids for our route.
1445-
aliceBobChan := ht.GetChannelByChanPoint(alice, chanPointAliceBob)
1446-
bobCarolChan := ht.GetChannelByChanPoint(bob, chanPointBobCarol)
1447-
carolDaveChan := ht.GetChannelByChanPoint(carol, chanPointCarolDave)
1451+
aliceBobChan := ht.GetChannelByChanPoint(alice, chanPoints[0])
1452+
bobCarolChan := ht.GetChannelByChanPoint(bob, chanPoints[1])
1453+
carolDaveChan := ht.GetChannelByChanPoint(carol, chanPoints[2])
1454+
1455+
// assertPathDetails is a helper function that asserts the details of
1456+
// the blinded paths returned in the invoice.
1457+
assertPathDetails := func(invoice *lnrpc.AddInvoiceResponse,
1458+
expPathLengths int, expIntroNodes ...[]byte) {
1459+
1460+
payReq := carol.RPC.DecodePayReq(invoice.PaymentRequest)
1461+
paths := payReq.BlindedPaths
1462+
1463+
introNodes := make([][]byte, 0, len(paths))
1464+
for _, p := range paths {
1465+
require.Len(
1466+
ht, p.BlindedPath.BlindedHops, expPathLengths,
1467+
)
1468+
1469+
introNodes = append(
1470+
introNodes, p.BlindedPath.IntroductionNode,
1471+
)
1472+
}
1473+
require.ElementsMatch(ht, introNodes, expIntroNodes)
1474+
}
14481475

1449-
// Let carol set no incoming channel restrictions.
1476+
// Let carol set no incoming channel restrictions. With a min of 1 hop,
1477+
// we expect the following blinded paths to be included:
1478+
// 1) Bob->Carol
1479+
// 2) Dave->Carol
14501480
var (
14511481
minNumRealHops uint32 = 1
14521482
numHops uint32 = 1
@@ -1460,139 +1490,110 @@ func testPartiallySpecifiedBlindedPath(ht *lntest.HarnessTest) {
14601490
NumHops: &numHops,
14611491
},
14621492
})
1493+
assertPathDetails(invoice, 2, bob.PubKey[:], dave.PubKey[:])
14631494

1464-
introNodesFound := make([][]byte, 0)
1465-
introNodesExpected := [][]byte{bob.PubKey[:], dave.PubKey[:]}
1466-
1467-
// Assert that it contains two blinded path with only 2 hops each one.
1468-
payReq := carol.RPC.DecodePayReq(invoice.PaymentRequest)
1469-
require.Len(ht, payReq.BlindedPaths, 2)
1470-
path := payReq.BlindedPaths[0].BlindedPath
1471-
require.Len(ht, path.BlindedHops, 2)
1472-
introNodesFound = append(introNodesFound, path.IntroductionNode)
1473-
path = payReq.BlindedPaths[1].BlindedPath
1474-
require.Len(ht, path.BlindedHops, 2)
1475-
introNodesFound = append(introNodesFound, path.IntroductionNode)
1476-
1477-
// Assert the introduction nodes without caring about the routes order.
1478-
require.ElementsMatch(ht, introNodesExpected, introNodesFound)
1479-
1480-
// Let carol choose the wrong incoming channel.
1481-
var (
1482-
incomingChannelList = []uint64{aliceBobChan.ChanId}
1483-
)
1484-
1485-
err := fmt.Sprintf("incoming channel %v is not seen as owned by node",
1486-
aliceBobChan.ChanId)
1487-
1495+
// Let carol choose an invalid incoming channel list for the blinded
1496+
// path. It is invalid since it does not connect to her node.
1497+
// Assert that the expected error is returned.
14881498
carol.RPC.AddInvoiceAssertErr(
14891499
&lnrpc.Invoice{
14901500
Memo: "test",
14911501
ValueMsat: 10_000_000,
14921502
IsBlinded: true,
14931503
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1494-
MinNumRealHops: &minNumRealHops,
1495-
NumHops: &numHops,
1496-
IncomingChannelList: incomingChannelList,
1504+
MinNumRealHops: &minNumRealHops,
1505+
NumHops: &numHops,
1506+
IncomingChannelList: []uint64{
1507+
aliceBobChan.ChanId,
1508+
},
14971509
},
14981510
},
1499-
err,
1511+
fmt.Sprintf("incoming channel %v is not seen as owned by node",
1512+
aliceBobChan.ChanId),
15001513
)
15011514

15021515
// Let Carol set the incoming channel list greater than minimum number
15031516
// of real hops.
1504-
incomingChannelList = []uint64{aliceBobChan.ChanId, bobCarolChan.ChanId}
1505-
err = fmt.Sprintf("minimum number of blinded path hops (%d) must be "+
1506-
"greater than or equal to the number of hops specified on "+
1507-
"the chained channels (%d)", minNumRealHops,
1508-
len(incomingChannelList),
1509-
)
1510-
1517+
// Assert that the expected error is returned.
15111518
carol.RPC.AddInvoiceAssertErr(
15121519
&lnrpc.Invoice{
15131520
Memo: "test",
15141521
ValueMsat: 10_000_000,
15151522
IsBlinded: true,
15161523
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1517-
MinNumRealHops: &minNumRealHops,
1518-
NumHops: &numHops,
1519-
IncomingChannelList: incomingChannelList,
1524+
MinNumRealHops: &minNumRealHops,
1525+
NumHops: &numHops,
1526+
IncomingChannelList: []uint64{
1527+
aliceBobChan.ChanId,
1528+
bobCarolChan.ChanId,
1529+
},
15201530
},
15211531
},
1522-
err,
1532+
"must be greater than or equal to the number of hops "+
1533+
"specified on the chained channels",
15231534
)
15241535

15251536
// Let Carol choose an incoming channel that points to a node in the
15261537
// omission set.
1527-
incomingChannelList = []uint64{bobCarolChan.ChanId}
1528-
var nodeOmissionList [][]byte
1529-
nodeOmissionList = append(nodeOmissionList, bob.PubKey[:])
1530-
1531-
err = fmt.Sprintf("cannot simultaneously be included in the omission " +
1532-
"set and in the partially specified path",
1533-
)
1534-
15351538
carol.RPC.AddInvoiceAssertErr(
15361539
&lnrpc.Invoice{
15371540
Memo: "test",
15381541
ValueMsat: 10_000_000,
15391542
IsBlinded: true,
15401543
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1541-
MinNumRealHops: &minNumRealHops,
1542-
NumHops: &numHops,
1543-
IncomingChannelList: incomingChannelList,
1544-
NodeOmissionList: nodeOmissionList,
1544+
MinNumRealHops: &minNumRealHops,
1545+
NumHops: &numHops,
1546+
IncomingChannelList: []uint64{
1547+
bobCarolChan.ChanId,
1548+
},
1549+
NodeOmissionList: [][]byte{
1550+
bob.PubKey[:],
1551+
},
15451552
},
15461553
},
1547-
err,
1554+
"cannot simultaneously be included in the omission set and "+
1555+
"in the partially specified path",
15481556
)
15491557

15501558
// Let carol restrict bob as incoming channel.
1551-
incomingChannelList = []uint64{bobCarolChan.ChanId}
1552-
15531559
invoice = carol.RPC.AddInvoice(&lnrpc.Invoice{
15541560
Memo: "test",
15551561
ValueMsat: 10_000_000,
15561562
IsBlinded: true,
15571563
BlindedPathConfig: &lnrpc.BlindedPathConfig{
1558-
MinNumRealHops: &minNumRealHops,
1559-
NumHops: &numHops,
1560-
IncomingChannelList: incomingChannelList,
1564+
MinNumRealHops: &minNumRealHops,
1565+
NumHops: &numHops,
1566+
IncomingChannelList: []uint64{
1567+
bobCarolChan.ChanId,
1568+
},
15611569
},
15621570
})
15631571

15641572
// Assert that it contains a single blinded path with only
15651573
// 2 hops, with bob as the introduction node.
1566-
payReq = carol.RPC.DecodePayReq(invoice.PaymentRequest)
1567-
require.Len(ht, payReq.BlindedPaths, 1)
1568-
path = payReq.BlindedPaths[0].BlindedPath
1569-
require.Len(ht, path.BlindedHops, 2)
1570-
require.EqualValues(ht, path.IntroductionNode, bob.PubKey[:])
1574+
assertPathDetails(invoice, 2, bob.PubKey[:])
15711575

1572-
// Now let alice pay the invoice.
1576+
// Check that Alice can pay the invoice.
15731577
ht.CompletePaymentRequests(alice, []string{invoice.PaymentRequest})
15741578

1575-
// Let carol restrict dave as incoming channel and max Hops as 2
1579+
// Let carol restrict dave as incoming channel and max hops as 2.
15761580
numHops = 2
1577-
incomingChannelList = []uint64{carolDaveChan.ChanId}
1578-
15791581
invoice = carol.RPC.AddInvoice(&lnrpc.Invoice{
15801582
Memo: "test",
15811583
ValueMsat: 10_000_000,
15821584
IsBlinded: true,
15831585
BlindedPathConfig: &lnrpc.BlindedPathConfig{
15841586
MinNumRealHops: &minNumRealHops,
15851587
NumHops: &numHops,
1586-
IncomingChannelList: incomingChannelList,
1588+
IncomingChannelList: []uint64{carolDaveChan.ChanId},
15871589
},
15881590
})
15891591

1590-
// Assert that it contains one path with 3 hops, with dave as the
1591-
// introduction node. The path alice -> bob -> carol is discarded
1592-
// because alice is a dead-end.
1593-
payReq = carol.RPC.DecodePayReq(invoice.PaymentRequest)
1594-
require.Len(ht, payReq.BlindedPaths, 1)
1595-
path = payReq.BlindedPaths[0].BlindedPath
1596-
require.Len(ht, path.BlindedHops, 3)
1597-
require.EqualValues(ht, path.IntroductionNode, eve.PubKey[:])
1592+
// Assert that it contains two paths: one 3 hop one starting at Eve and
1593+
// a 3 hop one starting at Dave (this one will be padded with a dummy
1594+
// hop) in order to keep all the paths equidistant.
1595+
assertPathDetails(invoice, 3, eve.PubKey[:], dave.PubKey[:])
1596+
1597+
// Check that Frank can pay the invoice.
1598+
ht.CompletePaymentRequests(frank, []string{invoice.PaymentRequest})
15981599
}

0 commit comments

Comments
 (0)