@@ -644,6 +644,33 @@ impl PeerState {
644644 } ) ;
645645 }
646646
647+ fn prune_terminal_state_without_channels (
648+ & mut self , live_channel_ids : & [ ChannelId ] ,
649+ ) -> Vec < ( u64 , ChannelId ) > {
650+ let mut pruned_channels = Vec :: new ( ) ;
651+ self . outbound_channels_by_intercept_scid . retain ( |intercept_scid, entry| {
652+ if let Some ( channel_id) = entry. get_channel_id ( ) {
653+ if !live_channel_ids. contains ( & channel_id) {
654+ pruned_channels. push ( ( * intercept_scid, channel_id) ) ;
655+ return false ;
656+ }
657+ }
658+ true
659+ } ) ;
660+
661+ if pruned_channels. is_empty ( ) {
662+ return pruned_channels;
663+ }
664+
665+ for ( intercept_scid, channel_id) in pruned_channels. iter ( ) {
666+ self . intercept_scid_by_channel_id . remove ( channel_id) ;
667+ self . intercept_scid_by_user_channel_id . retain ( |_, iscid| intercept_scid != iscid) ;
668+ }
669+ self . needs_persist = true ;
670+
671+ pruned_channels
672+ }
673+
647674 fn pending_requests_and_channels ( & self ) -> usize {
648675 let pending_requests = self . pending_requests . len ( ) ;
649676 let pending_outbound_channels = self
@@ -1805,6 +1832,13 @@ where
18051832 loop {
18061833 let mut need_remove = Vec :: new ( ) ;
18071834 let mut need_persist = Vec :: new ( ) ;
1835+ let live_channel_ids: Vec < ChannelId > = self
1836+ . channel_manager
1837+ . get_cm ( )
1838+ . list_channels ( )
1839+ . into_iter ( )
1840+ . map ( |details| details. channel_id )
1841+ . collect ( ) ;
18081842
18091843 {
18101844 // First build a list of peers to persist and prune with the read lock. This allows
@@ -1813,22 +1847,43 @@ where
18131847 for ( counterparty_node_id, inner_state_lock) in outer_state_lock. iter ( ) {
18141848 let mut peer_state_lock = inner_state_lock. lock ( ) . unwrap ( ) ;
18151849 peer_state_lock. prune_expired_request_state ( ) ;
1850+ let removed_terminal_state =
1851+ peer_state_lock. prune_terminal_state_without_channels ( & live_channel_ids) ;
18161852 let is_prunable = peer_state_lock. is_prunable ( ) ;
18171853 if is_prunable {
1818- need_remove. push ( * counterparty_node_id) ;
1854+ need_remove. push ( ( * counterparty_node_id, removed_terminal_state ) ) ;
18191855 } else if peer_state_lock. needs_persist {
1820- need_persist. push ( * counterparty_node_id) ;
1856+ need_persist. push ( ( * counterparty_node_id, removed_terminal_state ) ) ;
18211857 }
18221858 }
18231859 }
18241860
1825- for counterparty_node_id in need_persist. into_iter ( ) {
1826- debug_assert ! ( !need_remove. contains( & counterparty_node_id) ) ;
1861+ let has_removed_terminal_state = need_persist
1862+ . iter ( )
1863+ . chain ( need_remove. iter ( ) )
1864+ . any ( |( _, removed_terminal_state) | !removed_terminal_state. is_empty ( ) ) ;
1865+ if has_removed_terminal_state {
1866+ // The peer state changes above are persisted through `need_persist` and
1867+ // `need_remove`; keep these in-memory lookup indexes in sync with them.
1868+ let mut peer_by_intercept_scid = self . peer_by_intercept_scid . write ( ) . unwrap ( ) ;
1869+ let mut peer_by_channel_id = self . peer_by_channel_id . write ( ) . unwrap ( ) ;
1870+ for ( _, removed_terminal_state) in need_persist. iter ( ) . chain ( need_remove. iter ( ) ) {
1871+ for ( intercept_scid, channel_id) in removed_terminal_state. iter ( ) {
1872+ peer_by_intercept_scid. remove ( intercept_scid) ;
1873+ peer_by_channel_id. remove ( channel_id) ;
1874+ }
1875+ }
1876+ }
1877+
1878+ for ( counterparty_node_id, _) in need_persist. into_iter ( ) {
1879+ debug_assert ! ( !need_remove
1880+ . iter( )
1881+ . any( |( node_id, _) | node_id == & counterparty_node_id) ) ;
18271882 self . persist_peer_state ( counterparty_node_id) . await ?;
18281883 did_persist = true ;
18291884 }
18301885
1831- for counterparty_node_id in need_remove {
1886+ for ( counterparty_node_id, _ ) in need_remove {
18321887 let mut future_opt = None ;
18331888 {
18341889 // We need to take the `per_peer_state` write lock to remove an entry, but also
@@ -2812,6 +2867,68 @@ mod tests {
28122867 assert_eq ! ( fee_payment. htlcs, vec![ htlc] ) ;
28132868 }
28142869
2870+ #[ test]
2871+ fn prunes_terminal_state_for_closed_channels ( ) {
2872+ let opening_fee_params = LSPS2OpeningFeeParams {
2873+ min_fee_msat : 10_000_000 ,
2874+ proportional : 10_000 ,
2875+ valid_until : LSPSDateTime :: from_str ( "2035-05-20T08:30:45Z" ) . unwrap ( ) ,
2876+ min_lifetime : 4032 ,
2877+ max_client_to_self_delay : 2016 ,
2878+ min_payment_size_msat : 10_000_000 ,
2879+ max_payment_size_msat : 1_000_000_000 ,
2880+ promise : "ignore" . to_string ( ) ,
2881+ } ;
2882+ let stale_intercept_scid = 42 ;
2883+ let stale_user_channel_id = 43 ;
2884+ let stale_channel_id = ChannelId ( [ 44 ; 32 ] ) ;
2885+ let live_intercept_scid = 45 ;
2886+ let live_user_channel_id = 46 ;
2887+ let live_channel_id = ChannelId ( [ 47 ; 32 ] ) ;
2888+
2889+ let mut stale_jit_channel =
2890+ OutboundJITChannel :: new ( None , opening_fee_params. clone ( ) , stale_user_channel_id, false ) ;
2891+ stale_jit_channel. state =
2892+ OutboundJITChannelState :: PaymentForwarded { channel_id : stale_channel_id } ;
2893+ let mut live_jit_channel =
2894+ OutboundJITChannel :: new ( None , opening_fee_params, live_user_channel_id, false ) ;
2895+ live_jit_channel. state =
2896+ OutboundJITChannelState :: PaymentForwarded { channel_id : live_channel_id } ;
2897+
2898+ let mut peer_state = PeerState :: new ( ) ;
2899+ peer_state. insert_outbound_channel ( stale_intercept_scid, stale_jit_channel) ;
2900+ peer_state. insert_outbound_channel ( live_intercept_scid, live_jit_channel) ;
2901+ peer_state
2902+ . intercept_scid_by_user_channel_id
2903+ . insert ( stale_user_channel_id, stale_intercept_scid) ;
2904+ peer_state
2905+ . intercept_scid_by_user_channel_id
2906+ . insert ( live_user_channel_id, live_intercept_scid) ;
2907+ peer_state. intercept_scid_by_channel_id . insert ( stale_channel_id, stale_intercept_scid) ;
2908+ peer_state. intercept_scid_by_channel_id . insert ( live_channel_id, live_intercept_scid) ;
2909+ peer_state. needs_persist = false ;
2910+
2911+ assert_eq ! (
2912+ peer_state. prune_terminal_state_without_channels( & [ live_channel_id] ) ,
2913+ vec![ ( stale_intercept_scid, stale_channel_id) ]
2914+ ) ;
2915+ assert ! ( !peer_state
2916+ . outbound_channels_by_intercept_scid
2917+ . contains_key( & stale_intercept_scid) ) ;
2918+ assert ! ( peer_state. outbound_channels_by_intercept_scid. contains_key( & live_intercept_scid) ) ;
2919+ assert ! ( !peer_state. intercept_scid_by_user_channel_id. contains_key( & stale_user_channel_id) ) ;
2920+ assert_eq ! (
2921+ peer_state. intercept_scid_by_user_channel_id. get( & live_user_channel_id) ,
2922+ Some ( & live_intercept_scid)
2923+ ) ;
2924+ assert ! ( !peer_state. intercept_scid_by_channel_id. contains_key( & stale_channel_id) ) ;
2925+ assert_eq ! (
2926+ peer_state. intercept_scid_by_channel_id. get( & live_channel_id) ,
2927+ Some ( & live_intercept_scid)
2928+ ) ;
2929+ assert ! ( peer_state. needs_persist) ;
2930+ }
2931+
28152932 #[ test]
28162933 fn broadcast_not_allowed_after_non_paying_fee_payment_claimed ( ) {
28172934 let min_fee_msat: u64 = 12345 ;
0 commit comments