@@ -1051,3 +1051,171 @@ func testMPPToSingleBlindedPath(ht *lntest.HarnessTest) {
10511051 ht .AssertNumWaitingClose (hn , 0 )
10521052 }
10531053}
1054+
1055+ // testBlindedRouteDummyHops tests that the route blinding flow works as
1056+ // expected in the cases where the recipient chooses to pad the blinded path
1057+ // with dummy hops.
1058+ //
1059+ // We will set up the following network were Dave will always be the recipient
1060+ // and Alice is always the payer. Bob will not support route blinding and so
1061+ // will never be chosen as the introduction node.
1062+ //
1063+ // Alice -- Bob -- Carol -- Dave
1064+ //
1065+ // First we will start Carol _without_ route blinding support. Then we will
1066+ // configure Dave such that any blinded route he constructs must be 2 hops long.
1067+ // Since Carol cannot be chosen as an introduction node, Dave chooses himself
1068+ // as an introduction node and appends two dummy hops.
1069+ // Next, we will restart Carol with route blinding support and repeat the test
1070+ // but this time we force Dave to construct a path with a minimum distance of 1
1071+ // between him and the introduction node. So we expect that Carol is chosen as
1072+ // the intro node and that one dummy hops is appended.
1073+ func testBlindedRouteDummyHops (ht * lntest.HarnessTest ) {
1074+ alice , bob := ht .Alice , ht .Bob
1075+
1076+ // Disable route blinding for Bob so that he is never chosen as the
1077+ // introduction node.
1078+ ht .RestartNodeWithExtraArgs (bob , []string {
1079+ "--protocol.no-route-blinding" ,
1080+ })
1081+
1082+ // Start carol without route blinding so that initially Carol is also
1083+ // not chosen.
1084+ carol := ht .NewNode ("carol" , []string {
1085+ "--protocol.no-route-blinding" ,
1086+ })
1087+
1088+ // Configure Dave so that all blinded paths always contain 2 hops and
1089+ // so that there is no minimum number of real hops.
1090+ dave := ht .NewNode ("dave" , []string {
1091+ "--invoices.blinding.min-num-real-hops=0" ,
1092+ "--invoices.blinding.num-hops=2" ,
1093+ })
1094+
1095+ ht .EnsureConnected (alice , bob )
1096+ ht .EnsureConnected (bob , carol )
1097+ ht .EnsureConnected (carol , dave )
1098+
1099+ // Send coins to carol and mine 1 blocks to confirm them.
1100+ ht .FundCoinsUnconfirmed (btcutil .SatoshiPerBitcoin , carol )
1101+ ht .MineBlocksAndAssertNumTxes (1 , 1 )
1102+
1103+ const paymentAmt = btcutil .Amount (300000 )
1104+
1105+ nodes := []* node.HarnessNode {alice , bob , carol , dave }
1106+ reqs := []* lntest.OpenChannelRequest {
1107+ {
1108+ Local : alice ,
1109+ Remote : bob ,
1110+ Param : lntest.OpenChannelParams {
1111+ Amt : paymentAmt * 3 ,
1112+ },
1113+ },
1114+ {
1115+ Local : bob ,
1116+ Remote : carol ,
1117+ Param : lntest.OpenChannelParams {
1118+ Amt : paymentAmt * 3 ,
1119+ },
1120+ },
1121+ {
1122+ Local : carol ,
1123+ Remote : dave ,
1124+ Param : lntest.OpenChannelParams {
1125+ Amt : paymentAmt * 3 ,
1126+ },
1127+ },
1128+ }
1129+
1130+ channelPoints := ht .OpenMultiChannelsAsync (reqs )
1131+
1132+ // Make sure every node has heard about every channel.
1133+ for _ , hn := range nodes {
1134+ for _ , cp := range channelPoints {
1135+ ht .AssertTopologyChannelOpen (hn , cp )
1136+ }
1137+
1138+ // Each node should have exactly 5 edges.
1139+ ht .AssertNumEdges (hn , len (channelPoints ), false )
1140+ }
1141+
1142+ // Make Dave create an invoice with a blinded path for Alice to pay.
1143+ invoice := & lnrpc.Invoice {
1144+ Memo : "test" ,
1145+ Value : int64 (paymentAmt ),
1146+ Blind : true ,
1147+ }
1148+ invoiceResp := dave .RPC .AddInvoice (invoice )
1149+
1150+ // Assert that it contains a single blinded path and that the
1151+ // introduction node is dave.
1152+ payReq := dave .RPC .DecodePayReq (invoiceResp .PaymentRequest )
1153+ require .Len (ht , payReq .BlindedPaths , 1 )
1154+
1155+ // The total number of hop payloads is 3: one for the introduction node
1156+ // and then one for each dummy hop.
1157+ path := payReq .BlindedPaths [0 ].BlindedPath
1158+ require .Len (ht , path .BlindedHops , 3 )
1159+ require .EqualValues (ht , path .IntroductionNode , dave .PubKey [:])
1160+
1161+ // Now let Alice pay the invoice.
1162+ ht .CompletePaymentRequests (
1163+ ht .Alice , []string {invoiceResp .PaymentRequest },
1164+ )
1165+
1166+ // Make sure Dave show the invoice as settled.
1167+ inv := dave .RPC .LookupInvoice (invoiceResp .RHash )
1168+ require .Equal (ht , lnrpc .Invoice_SETTLED , inv .State )
1169+
1170+ // Let's also test the case where Dave is not the introduction node.
1171+ // We restart Carol so that she supports route blinding. We also restart
1172+ // Dave and force a minimum of 1 real blinded hop. We keep the number
1173+ // of hops to 2 meaning that one dummy hop should be added.
1174+ ht .RestartNodeWithExtraArgs (carol , nil )
1175+ ht .RestartNodeWithExtraArgs (dave , []string {
1176+ "--invoices.blinding.min-num-real-hops=1" ,
1177+ "--invoices.blinding.num-hops=2" ,
1178+ })
1179+ ht .EnsureConnected (bob , carol )
1180+ ht .EnsureConnected (carol , dave )
1181+
1182+ // Make Dave create an invoice with a blinded path for Alice to pay.
1183+ invoiceResp = dave .RPC .AddInvoice (invoice )
1184+
1185+ // Assert that it contains a single blinded path and that the
1186+ // introduction node is Carol.
1187+ payReq = dave .RPC .DecodePayReq (invoiceResp .PaymentRequest )
1188+ for _ , path := range payReq .BlindedPaths {
1189+ ht .Logf ("intro node: %x" , path .BlindedPath .IntroductionNode )
1190+ }
1191+
1192+ require .Len (ht , payReq .BlindedPaths , 1 )
1193+
1194+ // The total number of hop payloads is 3: one for the introduction node
1195+ // and then one for each dummy hop.
1196+ path = payReq .BlindedPaths [0 ].BlindedPath
1197+ require .Len (ht , path .BlindedHops , 3 )
1198+ require .EqualValues (ht , path .IntroductionNode , carol .PubKey [:])
1199+
1200+ // Now let Alice pay the invoice.
1201+ ht .CompletePaymentRequests (
1202+ ht .Alice , []string {invoiceResp .PaymentRequest },
1203+ )
1204+
1205+ // Make sure Dave show the invoice as settled.
1206+ inv = dave .RPC .LookupInvoice (invoiceResp .RHash )
1207+ require .Equal (ht , lnrpc .Invoice_SETTLED , inv .State )
1208+
1209+ // Close all channels without mining the closing transactions.
1210+ ht .CloseChannelAssertPending (alice , channelPoints [0 ], false )
1211+ ht .CloseChannelAssertPending (bob , channelPoints [1 ], false )
1212+ ht .CloseChannelAssertPending (carol , channelPoints [2 ], false )
1213+
1214+ // Now mine a block to include all the closing transactions.
1215+ ht .MineBlocksAndAssertNumTxes (1 , 3 )
1216+
1217+ // Assert that the channels are closed.
1218+ for _ , hn := range nodes {
1219+ ht .AssertNumWaitingClose (hn , 0 )
1220+ }
1221+ }
0 commit comments