Skip to content

Commit e416501

Browse files
committed
itest: test sending MP payment over multiple blinded paths
1 parent e871103 commit e416501

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

itest/list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,10 @@ var allTestCases = []*lntest.TestCase{
594594
Name: "mpp to single blinded path",
595595
TestFunc: testMPPToSingleBlindedPath,
596596
},
597+
{
598+
Name: "mpp to multiple blinded paths",
599+
TestFunc: testMPPToMultipleBlindedPaths,
600+
},
597601
{
598602
Name: "route blinding dummy hops",
599603
TestFunc: testBlindedRouteDummyHops,

itest/lnd_route_blinding_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,3 +1229,166 @@ func testBlindedRouteDummyHops(ht *lntest.HarnessTest) {
12291229
ht.AssertNumWaitingClose(hn, 0)
12301230
}
12311231
}
1232+
1233+
// testMPPToMultipleBlindedPaths tests that a two-shard MPP payment can be sent
1234+
// over a multiple blinded paths. The following network is created where Dave
1235+
// is the recipient and Alice the sender. Dave will create an invoice containing
1236+
// two blinded paths: one with Bob at the intro node and one with Carol as the
1237+
// intro node. Channel liquidity will be set up in such a way that Alice will be
1238+
// forced to send one shared via the Bob->Dave route and one over the
1239+
// Carol->Dave route.
1240+
//
1241+
// --- Bob ---
1242+
// / \
1243+
// Alice Dave
1244+
// \ /
1245+
// --- Carol ---
1246+
func testMPPToMultipleBlindedPaths(ht *lntest.HarnessTest) {
1247+
alice, bob := ht.Alice, ht.Bob
1248+
1249+
// Create a four-node context consisting of Alice, Bob and three new
1250+
// nodes.
1251+
dave := ht.NewNode("dave", []string{
1252+
"--routing.blinding.min-num-real-hops=1",
1253+
"--routing.blinding.num-hops=1",
1254+
})
1255+
carol := ht.NewNode("carol", nil)
1256+
1257+
// Connect nodes to ensure propagation of channels.
1258+
ht.EnsureConnected(alice, carol)
1259+
ht.EnsureConnected(alice, bob)
1260+
ht.EnsureConnected(carol, dave)
1261+
ht.EnsureConnected(bob, dave)
1262+
1263+
// Fund the new nodes.
1264+
ht.FundCoinsUnconfirmed(btcutil.SatoshiPerBitcoin, carol)
1265+
ht.FundCoinsUnconfirmed(btcutil.SatoshiPerBitcoin, dave)
1266+
ht.MineBlocksAndAssertNumTxes(1, 2)
1267+
1268+
const paymentAmt = btcutil.Amount(300000)
1269+
1270+
nodes := []*node.HarnessNode{alice, bob, carol, dave}
1271+
1272+
reqs := []*lntest.OpenChannelRequest{
1273+
{
1274+
Local: alice,
1275+
Remote: bob,
1276+
Param: lntest.OpenChannelParams{
1277+
Amt: paymentAmt * 2 / 3,
1278+
},
1279+
},
1280+
{
1281+
Local: alice,
1282+
Remote: carol,
1283+
Param: lntest.OpenChannelParams{
1284+
Amt: paymentAmt * 2 / 3,
1285+
},
1286+
},
1287+
{
1288+
Local: bob,
1289+
Remote: dave,
1290+
Param: lntest.OpenChannelParams{Amt: paymentAmt * 2},
1291+
},
1292+
{
1293+
Local: carol,
1294+
Remote: dave,
1295+
Param: lntest.OpenChannelParams{Amt: paymentAmt * 2},
1296+
},
1297+
}
1298+
1299+
channelPoints := ht.OpenMultiChannelsAsync(reqs)
1300+
1301+
// Make sure every node has heard every channel.
1302+
for _, hn := range nodes {
1303+
for _, cp := range channelPoints {
1304+
ht.AssertTopologyChannelOpen(hn, cp)
1305+
}
1306+
1307+
// Each node should have exactly 5 edges.
1308+
ht.AssertNumEdges(hn, len(channelPoints), false)
1309+
}
1310+
1311+
// Ok now make a payment that must be split to succeed.
1312+
1313+
// Make Dave create an invoice for Alice to pay
1314+
invoice := &lnrpc.Invoice{
1315+
Memo: "test",
1316+
Value: int64(paymentAmt),
1317+
Blind: true,
1318+
}
1319+
invoiceResp := dave.RPC.AddInvoice(invoice)
1320+
1321+
// Assert that two blinded paths are included in the invoice.
1322+
payReq := dave.RPC.DecodePayReq(invoiceResp.PaymentRequest)
1323+
require.Len(ht, payReq.BlindedPaths, 2)
1324+
1325+
sendReq := &routerrpc.SendPaymentRequest{
1326+
PaymentRequest: invoiceResp.PaymentRequest,
1327+
MaxParts: 10,
1328+
TimeoutSeconds: 60,
1329+
FeeLimitMsat: noFeeLimitMsat,
1330+
}
1331+
payment := ht.SendPaymentAssertSettled(alice, sendReq)
1332+
1333+
preimageBytes, err := hex.DecodeString(payment.PaymentPreimage)
1334+
require.NoError(ht, err)
1335+
1336+
preimage, err := lntypes.MakePreimage(preimageBytes)
1337+
require.NoError(ht, err)
1338+
1339+
hash, err := lntypes.MakeHash(invoiceResp.RHash)
1340+
require.NoError(ht, err)
1341+
1342+
// Make sure we got the preimage.
1343+
require.True(ht, preimage.Matches(hash), "preimage doesn't match")
1344+
1345+
// Check that Alice split the payment in at least two shards. Because
1346+
// the hand-off of the htlc to the link is asynchronous (via a mailbox),
1347+
// there is some non-determinism in the process. Depending on whether
1348+
// the new pathfinding round is started before or after the htlc is
1349+
// locked into the channel, different sharding may occur. Therefore we
1350+
// can only check if the number of shards isn't below the theoretical
1351+
// minimum.
1352+
succeeded := 0
1353+
for _, htlc := range payment.Htlcs {
1354+
if htlc.Status == lnrpc.HTLCAttempt_SUCCEEDED {
1355+
succeeded++
1356+
}
1357+
}
1358+
1359+
const minExpectedShards = 2
1360+
require.GreaterOrEqual(ht, succeeded, minExpectedShards,
1361+
"expected shards not reached")
1362+
1363+
// Make sure Dave show the invoice as settled for the full amount.
1364+
inv := dave.RPC.LookupInvoice(invoiceResp.RHash)
1365+
1366+
require.EqualValues(ht, paymentAmt, inv.AmtPaidSat,
1367+
"incorrect payment amt")
1368+
1369+
require.Equal(ht, lnrpc.Invoice_SETTLED, inv.State,
1370+
"Invoice not settled")
1371+
1372+
settled := 0
1373+
for _, htlc := range inv.Htlcs {
1374+
if htlc.State == lnrpc.InvoiceHTLCState_SETTLED {
1375+
settled++
1376+
}
1377+
}
1378+
require.Equal(ht, succeeded, settled, "num of HTLCs wrong")
1379+
1380+
// Close all channels without mining the closing transactions.
1381+
ht.CloseChannelAssertPending(alice, channelPoints[0], false)
1382+
ht.CloseChannelAssertPending(alice, channelPoints[1], false)
1383+
ht.CloseChannelAssertPending(bob, channelPoints[2], false)
1384+
ht.CloseChannelAssertPending(carol, channelPoints[3], false)
1385+
1386+
// Now mine a block to include all the closing transactions. (first
1387+
// iteration: no blinded paths)
1388+
ht.MineBlocksAndAssertNumTxes(1, 4)
1389+
1390+
// Assert that the channels are closed.
1391+
for _, hn := range nodes {
1392+
ht.AssertNumWaitingClose(hn, 0)
1393+
}
1394+
}

0 commit comments

Comments
 (0)