@@ -1612,6 +1612,197 @@ fn late_payment_forwarded_and_safe_after_force_close_does_not_broadcast() {
1612
1612
}
1613
1613
}
1614
1614
1615
+ #[ test]
1616
+ fn htlc_timeout_before_client_claim_results_in_handling_failed ( ) {
1617
+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
1618
+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
1619
+ let mut service_node_config = test_default_channel_config ( ) ;
1620
+ service_node_config. accept_intercept_htlcs = true ;
1621
+
1622
+ let mut client_node_config = test_default_channel_config ( ) ;
1623
+ client_node_config. manually_accept_inbound_channels = true ;
1624
+ client_node_config. channel_config . accept_underpaying_htlcs = true ;
1625
+
1626
+ let node_chanmgrs = create_node_chanmgrs (
1627
+ 3 ,
1628
+ & node_cfgs,
1629
+ & [ Some ( service_node_config) , Some ( client_node_config) , None ] ,
1630
+ ) ;
1631
+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
1632
+ let ( lsps_nodes, promise_secret) = setup_test_lsps2_nodes_with_payer ( nodes) ;
1633
+ let LSPSNodesWithPayer { ref service_node, ref client_node, ref payer_node } = lsps_nodes;
1634
+
1635
+ let payer_node_id = payer_node. node . get_our_node_id ( ) ;
1636
+ let service_node_id = service_node. inner . node . get_our_node_id ( ) ;
1637
+ let client_node_id = client_node. inner . node . get_our_node_id ( ) ;
1638
+
1639
+ let service_handler = service_node. liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
1640
+
1641
+ create_chan_between_nodes_with_value ( & payer_node, & service_node. inner , 2_000_000 , 100_000 ) ;
1642
+
1643
+ let intercept_scid = service_node. node . get_intercept_scid ( ) ;
1644
+ let user_channel_id = 44u128 ;
1645
+ let cltv_expiry_delta: u32 = 144 ;
1646
+ let payment_size_msat = Some ( 1_000_000 ) ;
1647
+ let fee_base_msat: u64 = 10_000 ;
1648
+
1649
+ execute_lsps2_dance (
1650
+ & lsps_nodes,
1651
+ intercept_scid,
1652
+ user_channel_id,
1653
+ cltv_expiry_delta,
1654
+ promise_secret,
1655
+ payment_size_msat,
1656
+ fee_base_msat,
1657
+ ) ;
1658
+
1659
+ let invoice = create_jit_invoice (
1660
+ & client_node,
1661
+ service_node_id,
1662
+ intercept_scid,
1663
+ cltv_expiry_delta,
1664
+ payment_size_msat,
1665
+ "timeout-before-claim" ,
1666
+ 3600 ,
1667
+ )
1668
+ . unwrap ( ) ;
1669
+
1670
+ payer_node
1671
+ . node
1672
+ . pay_for_bolt11_invoice (
1673
+ & invoice,
1674
+ PaymentId ( invoice. payment_hash ( ) . to_byte_array ( ) ) ,
1675
+ None ,
1676
+ Default :: default ( ) ,
1677
+ Retry :: Attempts ( 3 ) ,
1678
+ )
1679
+ . unwrap ( ) ;
1680
+
1681
+ check_added_monitors ! ( payer_node, 1 ) ;
1682
+ let events = payer_node. node . get_and_clear_pending_msg_events ( ) ;
1683
+ let ev = SendEvent :: from_event ( events[ 0 ] . clone ( ) ) ;
1684
+ service_node. inner . node . handle_update_add_htlc ( payer_node_id, & ev. msgs [ 0 ] ) ;
1685
+ do_commitment_signed_dance ( & service_node. inner , & payer_node, & ev. commitment_msg , false , true ) ;
1686
+ service_node. inner . node . process_pending_htlc_forwards ( ) ;
1687
+
1688
+ let events = service_node. inner . node . get_and_clear_pending_events ( ) ;
1689
+ assert_eq ! ( events. len( ) , 1 ) ;
1690
+ match & events[ 0 ] {
1691
+ Event :: HTLCIntercepted {
1692
+ intercept_id,
1693
+ requested_next_hop_scid,
1694
+ payment_hash : _,
1695
+ expected_outbound_amount_msat,
1696
+ ..
1697
+ } => {
1698
+ assert_eq ! ( * requested_next_hop_scid, intercept_scid) ;
1699
+ service_handler
1700
+ . htlc_intercepted (
1701
+ * requested_next_hop_scid,
1702
+ * intercept_id,
1703
+ * expected_outbound_amount_msat,
1704
+ PaymentHash ( invoice. payment_hash ( ) . to_byte_array ( ) ) ,
1705
+ )
1706
+ . unwrap ( ) ;
1707
+ } ,
1708
+ other => panic ! ( "Expected HTLCIntercepted, got {:?}" , other) ,
1709
+ }
1710
+
1711
+ // Create and mark broadcast safe so the channel is fully ready
1712
+ let expected_outbound_amount_msat = payment_size_msat. unwrap ( ) - fee_base_msat;
1713
+ let ( channel_id, _funding_tx) = create_channel_with_manual_broadcast (
1714
+ & service_node_id,
1715
+ & client_node_id,
1716
+ & service_node,
1717
+ & client_node,
1718
+ user_channel_id,
1719
+ & expected_outbound_amount_msat,
1720
+ true ,
1721
+ ) ;
1722
+
1723
+ service_handler. channel_ready ( user_channel_id, & channel_id, & client_node_id) . unwrap ( ) ;
1724
+ service_node. inner . node . process_pending_htlc_forwards ( ) ;
1725
+
1726
+ // Forward to client, but do not claim yet
1727
+ let pay_event = {
1728
+ {
1729
+ let mut added_monitors =
1730
+ service_node. inner . chain_monitor . added_monitors . lock ( ) . unwrap ( ) ;
1731
+ assert_eq ! ( added_monitors. len( ) , 1 ) ;
1732
+ added_monitors. clear ( ) ;
1733
+ }
1734
+ let mut msg_events = service_node. inner . node . get_and_clear_pending_msg_events ( ) ;
1735
+ assert_eq ! ( msg_events. len( ) , 1 ) ;
1736
+ SendEvent :: from_event ( msg_events. remove ( 0 ) )
1737
+ } ;
1738
+
1739
+ client_node. inner . node . handle_update_add_htlc ( service_node_id, & pay_event. msgs [ 0 ] ) ;
1740
+ do_commitment_signed_dance (
1741
+ & client_node. inner ,
1742
+ & service_node. inner ,
1743
+ & pay_event. commitment_msg ,
1744
+ false ,
1745
+ true ,
1746
+ ) ;
1747
+ client_node. inner . node . process_pending_htlc_forwards ( ) ;
1748
+
1749
+ let client_events = client_node. inner . node . get_and_clear_pending_events ( ) ;
1750
+ assert_eq ! ( client_events. len( ) , 1 ) ;
1751
+ let preimage = match & client_events[ 0 ] {
1752
+ Event :: PaymentClaimable { purpose, .. } => purpose. preimage ( ) . unwrap ( ) ,
1753
+ other => panic ! ( "Expected PaymentClaimable, got {:?}" , other) ,
1754
+ } ;
1755
+
1756
+ // Advance blocks past CLTV expiry before the client attempts to claim
1757
+ const SOME_EXTRA_BLOCKS : u32 = 3 ;
1758
+ let client_htlc_cltv_expiry = pay_event. msgs [ 0 ] . cltv_expiry ;
1759
+ let target_height = client_htlc_cltv_expiry. saturating_add ( SOME_EXTRA_BLOCKS ) ;
1760
+ let cur_height = service_node. inner . best_block_info ( ) . 1 ;
1761
+ let d = target_height - cur_height;
1762
+ connect_blocks ( & service_node. inner , d) ;
1763
+ connect_blocks ( & client_node. inner , d) ;
1764
+ connect_blocks ( & payer_node, d) ;
1765
+
1766
+ service_node. inner . node . process_pending_htlc_forwards ( ) ;
1767
+ client_node. inner . node . process_pending_htlc_forwards ( ) ;
1768
+
1769
+ // Service->client channel should close due to HTLC timeout
1770
+ let svc_events = service_node. inner . node . get_and_clear_pending_events ( ) ;
1771
+ let closed_on_service = svc_events. iter ( ) . any ( |ev| {
1772
+ matches ! ( ev, Event :: ChannelClosed { reason: ClosureReason :: HTLCsTimedOut { .. } , .. } )
1773
+ } ) ;
1774
+ assert ! ( closed_on_service, "Expected service->client channel to close due to HTLC timeout" ) ;
1775
+
1776
+ // Client tries to claim but should fail since HTLC timed out
1777
+ client_node. inner . node . claim_funds ( preimage) ;
1778
+ let client_events = client_node. inner . node . get_and_clear_pending_events ( ) ;
1779
+ assert_eq ! ( client_events. len( ) , 1 ) ;
1780
+ match & client_events[ 0 ] {
1781
+ Event :: HTLCHandlingFailed { failure_type, .. } => match failure_type {
1782
+ lightning:: events:: HTLCHandlingFailureType :: Receive { payment_hash } => {
1783
+ assert_eq ! ( * payment_hash, PaymentHash ( invoice. payment_hash( ) . to_byte_array( ) ) ) ;
1784
+ } ,
1785
+ _ => panic ! ( "Unexpected failure_type: {:?}" , failure_type) ,
1786
+ } ,
1787
+ other => panic ! ( "Expected HTLCHandlingFailed after timeout, got {:?}" , other) ,
1788
+ }
1789
+
1790
+ // Payer->service channel should remain open
1791
+ {
1792
+ let chans = service_node. inner . node . list_channels ( ) ;
1793
+ assert ! ( chans
1794
+ . iter( )
1795
+ . any( |cd| cd. counterparty. node_id == payer_node_id && cd. is_channel_ready) ) ;
1796
+ }
1797
+
1798
+ service_node. inner . node . get_and_clear_pending_msg_events ( ) ;
1799
+ client_node. inner . node . get_and_clear_pending_msg_events ( ) ;
1800
+ payer_node. node . get_and_clear_pending_msg_events ( ) ;
1801
+ service_node. inner . chain_monitor . added_monitors . lock ( ) . unwrap ( ) . clear ( ) ;
1802
+ client_node. inner . chain_monitor . added_monitors . lock ( ) . unwrap ( ) . clear ( ) ;
1803
+ payer_node. chain_monitor . added_monitors . lock ( ) . unwrap ( ) . clear ( ) ;
1804
+ }
1805
+
1615
1806
fn claim_and_assert_forwarded_only < ' a , ' b , ' c > (
1616
1807
payer_node : & lightning:: ln:: functional_test_utils:: Node < ' a , ' b , ' c > ,
1617
1808
service_node : & lightning:: ln:: functional_test_utils:: Node < ' a , ' b , ' c > ,
0 commit comments