@@ -1420,3 +1420,179 @@ func testBlindedPaymentHTLCReForward(ht *lntest.HarnessTest) {
1420
1420
require .Fail (ht , "timeout waiting for sending payment" )
1421
1421
}
1422
1422
}
1423
+
1424
+ // testPartiallySpecifiedBlindedPath tests lnd's ability to:
1425
+ // - Assert the error when attempting to create a blinded payment with an
1426
+ // invalid partially specified path.
1427
+ // - Create a blinded payment path when the blinded path is partially
1428
+ // pre-specified.
1429
+ func testPartiallySpecifiedBlindedPath (ht * lntest.HarnessTest ) {
1430
+ // 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 }
1434
+ chanPoints , nodes := ht .CreateSimpleNetwork (
1435
+ cfgs , lntest.OpenChannelParams {Amt : chanAmt },
1436
+ )
1437
+
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 ]
1443
+
1444
+ // 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 )
1448
+
1449
+ // Let carol set no incoming channel restrictions.
1450
+ var (
1451
+ minNumRealHops uint32 = 1
1452
+ numHops uint32 = 1
1453
+ )
1454
+ invoice := carol .RPC .AddInvoice (& lnrpc.Invoice {
1455
+ Memo : "test" ,
1456
+ ValueMsat : 10_000_000 ,
1457
+ IsBlinded : true ,
1458
+ BlindedPathConfig : & lnrpc.BlindedPathConfig {
1459
+ MinNumRealHops : & minNumRealHops ,
1460
+ NumHops : & numHops ,
1461
+ },
1462
+ })
1463
+
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
+
1488
+ carol .RPC .AddInvoiceAssertErr (
1489
+ & lnrpc.Invoice {
1490
+ Memo : "test" ,
1491
+ ValueMsat : 10_000_000 ,
1492
+ IsBlinded : true ,
1493
+ BlindedPathConfig : & lnrpc.BlindedPathConfig {
1494
+ MinNumRealHops : & minNumRealHops ,
1495
+ NumHops : & numHops ,
1496
+ IncomingChannelList : incomingChannelList ,
1497
+ },
1498
+ },
1499
+ err ,
1500
+ )
1501
+
1502
+ // Let Carol set the incoming channel list greater than minimum number
1503
+ // 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
+
1511
+ carol .RPC .AddInvoiceAssertErr (
1512
+ & lnrpc.Invoice {
1513
+ Memo : "test" ,
1514
+ ValueMsat : 10_000_000 ,
1515
+ IsBlinded : true ,
1516
+ BlindedPathConfig : & lnrpc.BlindedPathConfig {
1517
+ MinNumRealHops : & minNumRealHops ,
1518
+ NumHops : & numHops ,
1519
+ IncomingChannelList : incomingChannelList ,
1520
+ },
1521
+ },
1522
+ err ,
1523
+ )
1524
+
1525
+ // Let Carol choose an incoming channel that points to a node in the
1526
+ // 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
+
1535
+ carol .RPC .AddInvoiceAssertErr (
1536
+ & lnrpc.Invoice {
1537
+ Memo : "test" ,
1538
+ ValueMsat : 10_000_000 ,
1539
+ IsBlinded : true ,
1540
+ BlindedPathConfig : & lnrpc.BlindedPathConfig {
1541
+ MinNumRealHops : & minNumRealHops ,
1542
+ NumHops : & numHops ,
1543
+ IncomingChannelList : incomingChannelList ,
1544
+ NodeOmissionList : nodeOmissionList ,
1545
+ },
1546
+ },
1547
+ err ,
1548
+ )
1549
+
1550
+ // Let carol restrict bob as incoming channel.
1551
+ incomingChannelList = []uint64 {bobCarolChan .ChanId }
1552
+
1553
+ invoice = carol .RPC .AddInvoice (& lnrpc.Invoice {
1554
+ Memo : "test" ,
1555
+ ValueMsat : 10_000_000 ,
1556
+ IsBlinded : true ,
1557
+ BlindedPathConfig : & lnrpc.BlindedPathConfig {
1558
+ MinNumRealHops : & minNumRealHops ,
1559
+ NumHops : & numHops ,
1560
+ IncomingChannelList : incomingChannelList ,
1561
+ },
1562
+ })
1563
+
1564
+ // Assert that it contains a single blinded path with only
1565
+ // 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 [:])
1571
+
1572
+ // Now let alice pay the invoice.
1573
+ ht .CompletePaymentRequests (alice , []string {invoice .PaymentRequest })
1574
+
1575
+ // Let carol restrict dave as incoming channel and max Hops as 2
1576
+ numHops = 2
1577
+ incomingChannelList = []uint64 {carolDaveChan .ChanId }
1578
+
1579
+ invoice = carol .RPC .AddInvoice (& lnrpc.Invoice {
1580
+ Memo : "test" ,
1581
+ ValueMsat : 10_000_000 ,
1582
+ IsBlinded : true ,
1583
+ BlindedPathConfig : & lnrpc.BlindedPathConfig {
1584
+ MinNumRealHops : & minNumRealHops ,
1585
+ NumHops : & numHops ,
1586
+ IncomingChannelList : incomingChannelList ,
1587
+ },
1588
+ })
1589
+
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 [:])
1598
+ }
0 commit comments