Skip to content

Commit d1862e9

Browse files
committed
tests: lnpeer: test_dont_settle_partial_mpp_trigger_with_invalid_cltv_htlc
Adds unittest to verify that lnpeer doesn't settle any htlcs of incomplete mpp.
1 parent ce74830 commit d1862e9

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

tests/test_lnpeer.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,84 @@ async def on_htlc_failed(*args):
13421342
util.unregister_callback(on_htlc_fulfilled)
13431343
util.unregister_callback(on_htlc_failed)
13441344

1345+
async def test_dont_settle_partial_mpp_trigger_with_invalid_cltv_htlc(self):
1346+
"""Alice gets two htlcs as part of a mpp, one has a cltv too close to expiry and will get failed.
1347+
Test that the other htlc won't get settled if the mpp isn't complete anymore after failing the other htlc.
1348+
"""
1349+
alice_channel, bob_channel = create_test_channels()
1350+
p1, p2, w1, w2, _q1, _q2 = self.prepare_peers(alice_channel, bob_channel)
1351+
async def pay():
1352+
await util.wait_for2(p1.initialized, 1)
1353+
await util.wait_for2(p2.initialized, 1)
1354+
w2.features |= LnFeatures.BASIC_MPP_OPT
1355+
lnaddr1, _pay_req = self.prepare_invoice(w2, amount_msat=10_000, min_final_cltv_delta=144)
1356+
self.assertTrue(lnaddr1.get_features().supports(LnFeatures.BASIC_MPP_OPT))
1357+
route = (await w1.create_routes_from_invoice(amount_msat=10_000, decoded_invoice=lnaddr1))[0][0].route
1358+
1359+
# now p1 sends two htlcs, one is valid (1 msat), one is invalid (9_999 msat)
1360+
p1.pay(
1361+
route=route,
1362+
chan=alice_channel,
1363+
amount_msat=1,
1364+
total_msat=lnaddr1.get_amount_msat(),
1365+
payment_hash=lnaddr1.paymenthash,
1366+
# this htlc is valid and will get accepted, but it shouldn't get settled
1367+
min_final_cltv_delta=400,
1368+
payment_secret=lnaddr1.payment_secret,
1369+
)
1370+
await asyncio.sleep(0.1)
1371+
assert w1.get_preimage(lnaddr1.paymenthash) is None
1372+
p1.pay(
1373+
route=route,
1374+
chan=alice_channel,
1375+
amount_msat=9_999,
1376+
total_msat=lnaddr1.get_amount_msat(),
1377+
payment_hash=lnaddr1.paymenthash,
1378+
# this htlc will get failed directly as the cltv is too close to expiry (< 144)
1379+
min_final_cltv_delta=1,
1380+
payment_secret=lnaddr1.payment_secret,
1381+
)
1382+
1383+
while nhtlc_success + nhtlc_failed < 2:
1384+
await htlc_resolved.wait()
1385+
# both htlcs of the mpp set should get failed and w2 shouldn't release the preimage
1386+
self.assertEqual(0, nhtlc_success, f"{nhtlc_success=} | {nhtlc_failed=}")
1387+
self.assertEqual(2, nhtlc_failed, f"{nhtlc_success=} | {nhtlc_failed=}")
1388+
assert w1.get_preimage(lnaddr1.paymenthash) is None, "w1 shouldn't get the preimage"
1389+
raise SuccessfulTest()
1390+
1391+
async def f():
1392+
async with OldTaskGroup() as group:
1393+
await group.spawn(p1._message_loop())
1394+
await group.spawn(p1.htlc_switch())
1395+
await group.spawn(p2._message_loop())
1396+
await group.spawn(p2.htlc_switch())
1397+
await asyncio.sleep(0.01)
1398+
await group.spawn(pay())
1399+
1400+
htlc_resolved = asyncio.Event()
1401+
nhtlc_success = 0
1402+
nhtlc_failed = 0
1403+
async def on_htlc_fulfilled(*args):
1404+
htlc_resolved.set()
1405+
htlc_resolved.clear()
1406+
nonlocal nhtlc_success
1407+
nhtlc_success += 1
1408+
async def on_htlc_failed(*args):
1409+
htlc_resolved.set()
1410+
htlc_resolved.clear()
1411+
nonlocal nhtlc_failed
1412+
nhtlc_failed += 1
1413+
util.register_callback(on_htlc_fulfilled, ["htlc_fulfilled"])
1414+
util.register_callback(on_htlc_failed, ["htlc_failed"])
1415+
1416+
try:
1417+
with self.assertRaises(SuccessfulTest):
1418+
await f()
1419+
finally:
1420+
util.unregister_callback(on_htlc_fulfilled)
1421+
util.unregister_callback(on_htlc_failed)
1422+
13451423
async def test_legacy_shutdown_low(self):
13461424
await self._test_shutdown(alice_fee=100, bob_fee=150)
13471425

0 commit comments

Comments
 (0)