@@ -1473,3 +1473,58 @@ def test_simple_dummy_channel(node_factory):
14731473 final_cltv = 5 ,
14741474 layers = ["mylayer" ],
14751475 )
1476+
1477+
1478+ def test_reservations_leak (node_factory , executor ):
1479+ l1 , l2 , l3 , l4 , l5 = node_factory .get_nodes (
1480+ 5 ,
1481+ opts = [
1482+ {},
1483+ {},
1484+ {"plugin" : os .path .join (os .getcwd (), "tests/plugins/hold_htlcs.py" )},
1485+ {},
1486+ {},
1487+ ],
1488+ )
1489+
1490+ # There must be a common non-local channel in both payment paths.
1491+ # With a local channel we cannot trigger the reservation leak because we
1492+ # reserve slightly different amounts locally due to HTLC onchain costs.
1493+ node_factory .join_nodes ([l1 , l2 , l4 , l3 ], wait_for_announce = True )
1494+ node_factory .join_nodes ([l1 , l2 , l4 , l5 ], wait_for_announce = True )
1495+
1496+ # Use offers instead of bolt11 because we are going to pay through a blinded
1497+ # path and trigger a fake channel collision between both payments.
1498+ offer1 = l3 .rpc .offer ("any" )["bolt12" ]
1499+ offer2 = l5 .rpc .offer ("any" )["bolt12" ]
1500+
1501+ inv1 = l1 .rpc .fetchinvoice (offer1 , "100sat" )["invoice" ]
1502+ inv2 = l1 .rpc .fetchinvoice (offer2 , "100sat" )["invoice" ]
1503+
1504+ # Initiate the first payment that has a delay.
1505+ fut = executor .submit (l1 .rpc .xpay , (inv1 ))
1506+
1507+ # Wait for the first payment to reserve the path.
1508+ l1 .daemon .wait_for_log (r"json_askrene_reserve called" )
1509+
1510+ # A second payment starts.
1511+ l1 .rpc .xpay (inv2 )
1512+ l1 .daemon .wait_for_log (r"json_askrene_unreserve called" )
1513+
1514+ l3 .daemon .wait_for_log (r"Holding onto an incoming htlc for 10 seconds" )
1515+
1516+ # There is a payment pending therefore we expect reservations.
1517+ reservations = l1 .rpc .askrene_listreservations ()
1518+ assert reservations != {"reservations" : []}
1519+
1520+ l3 .daemon .wait_for_log (r"htlc_accepted hook called" )
1521+ fut .result ()
1522+ l1 .daemon .wait_for_log (r"json_askrene_unreserve called" )
1523+
1524+ # The first payment has finished we expect no reservations.
1525+ reservations = l1 .rpc .askrene_listreservations ()
1526+ assert reservations == {"reservations" : []}
1527+
1528+ # We shouldn't fail askrene-unreserve. If it does it means something went
1529+ # wrong.
1530+ assert l1 .daemon .is_in_log ("askrene-unreserve failed" ) is None
0 commit comments