@@ -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