@@ -1257,6 +1257,8 @@ where
12571257 /// This removes the intercept SCID, any outbound channel state, and associated
12581258 /// channel‐ID mappings for the specified `user_channel_id`, but only while no payment
12591259 /// has been forwarded yet and no channel has been opened on-chain.
1260+ /// Any held HTLCs for the pending flow are failed backwards before the local state
1261+ /// is removed.
12601262 ///
12611263 /// Returns an error if:
12621264 /// - there is no channel matching `user_channel_id`, or
@@ -1292,25 +1294,27 @@ where
12921294
12931295 let jit_channel = peer_state
12941296 . outbound_channels_by_intercept_scid
1295- . get ( & intercept_scid)
1297+ . get_mut ( & intercept_scid)
12961298 . ok_or_else ( || APIError :: APIMisuseError {
1297- err : format ! (
1298- "Failed to map intercept_scid {} for user_channel_id {} to a channel." ,
1299- intercept_scid, user_channel_id,
1300- ) ,
1301- } ) ?;
1299+ err : format ! (
1300+ "Failed to map intercept_scid {} for user_channel_id {} to a channel." ,
1301+ intercept_scid, user_channel_id,
1302+ ) ,
1303+ } ) ?;
13021304
1303- let is_pending = matches ! (
1304- jit_channel. state,
1305- OutboundJITChannelState :: PendingInitialPayment { .. }
1306- | OutboundJITChannelState :: PendingChannelOpen { .. }
1307- ) ;
1305+ let intercepted_htlcs = match & mut jit_channel. state {
1306+ OutboundJITChannelState :: PendingInitialPayment { payment_queue }
1307+ | OutboundJITChannelState :: PendingChannelOpen { payment_queue, .. } => payment_queue. clear ( ) ,
1308+ _ => {
1309+ return Err ( APIError :: APIMisuseError {
1310+ err : "Cannot abandon channel open after channel creation or payment forwarding"
1311+ . to_string ( ) ,
1312+ } ) ;
1313+ } ,
1314+ } ;
13081315
1309- if !is_pending {
1310- return Err ( APIError :: APIMisuseError {
1311- err : "Cannot abandon channel open after channel creation or payment forwarding"
1312- . to_string ( ) ,
1313- } ) ;
1316+ for htlc in intercepted_htlcs {
1317+ let _ = self . channel_manager . get_cm ( ) . fail_intercepted_htlc ( htlc. intercept_id ) ;
13141318 }
13151319
13161320 peer_state. intercept_scid_by_user_channel_id . remove ( & user_channel_id) ;
@@ -1784,14 +1788,22 @@ where
17841788 // TODO: We should eventually persist in parallel, however, when we do, we probably want to
17851789 // introduce some batching to upper-bound the number of requests inflight at any given
17861790 // time.
1787- let mut did_persist = false ;
17881791
17891792 if self . persistence_in_flight . fetch_add ( 1 , Ordering :: AcqRel ) > 0 {
17901793 // If we're not the first event processor to get here, just return early, the increment
17911794 // we just did will be treated as "go around again" at the end.
1792- return Ok ( did_persist ) ;
1795+ return Ok ( false ) ;
17931796 }
17941797
1798+ let res = self . do_persist ( ) . await ;
1799+ debug_assert ! ( res. is_err( ) || self . persistence_in_flight. load( Ordering :: Acquire ) == 0 ) ;
1800+ self . persistence_in_flight . store ( 0 , Ordering :: Release ) ;
1801+ res
1802+ }
1803+
1804+ async fn do_persist ( & self ) -> Result < bool , lightning:: io:: Error > {
1805+ let mut did_persist = false ;
1806+
17951807 loop {
17961808 let mut need_remove = Vec :: new ( ) ;
17971809 let mut need_persist = Vec :: new ( ) ;
@@ -1853,6 +1865,7 @@ where
18531865 did_persist = true ;
18541866 } else {
18551867 self . persist_peer_state ( counterparty_node_id) . await ?;
1868+ did_persist = true ;
18561869 }
18571870 }
18581871
@@ -2353,6 +2366,8 @@ mod tests {
23532366
23542367 use bitcoin:: { absolute:: LockTime , transaction:: Version } ;
23552368 use core:: str:: FromStr ;
2369+ use lightning:: io:: Cursor ;
2370+ use lightning:: util:: ser:: { Readable , Writeable } ;
23562371
23572372 const MAX_VALUE_MSAT : u64 = 21_000_000_0000_0000_000 ;
23582373
@@ -2760,6 +2775,52 @@ mod tests {
27602775 }
27612776 }
27622777
2778+ #[ test]
2779+ fn replayed_intercepted_htlc_after_persist_is_idempotent ( ) {
2780+ let payment_size_msat = Some ( 500_000_000 ) ;
2781+ let opening_fee_params = LSPS2OpeningFeeParams {
2782+ min_fee_msat : 10_000_000 ,
2783+ proportional : 10_000 ,
2784+ valid_until : LSPSDateTime :: from_str ( "2035-05-20T08:30:45Z" ) . unwrap ( ) ,
2785+ min_lifetime : 4032 ,
2786+ max_client_to_self_delay : 2016 ,
2787+ min_payment_size_msat : 10_000_000 ,
2788+ max_payment_size_msat : 1_000_000_000 ,
2789+ promise : "ignore" . to_string ( ) ,
2790+ } ;
2791+ let intercept_scid = 42 ;
2792+ let user_channel_id = 43 ;
2793+ let htlc = InterceptedHTLC {
2794+ intercept_id : InterceptId ( [ 1 ; 32 ] ) ,
2795+ expected_outbound_amount_msat : 500_000_000 ,
2796+ payment_hash : PaymentHash ( [ 2 ; 32 ] ) ,
2797+ } ;
2798+
2799+ let mut jit_channel =
2800+ OutboundJITChannel :: new ( payment_size_msat, opening_fee_params, user_channel_id, false ) ;
2801+ assert ! ( matches!(
2802+ jit_channel. htlc_intercepted( htlc) . unwrap( ) ,
2803+ Some ( HTLCInterceptedAction :: OpenChannel ( _) )
2804+ ) ) ;
2805+
2806+ let mut peer_state = PeerState :: new ( ) ;
2807+ peer_state. intercept_scid_by_user_channel_id . insert ( user_channel_id, intercept_scid) ;
2808+ peer_state. insert_outbound_channel ( intercept_scid, jit_channel) ;
2809+
2810+ let encoded_peer_state = peer_state. encode ( ) ;
2811+ let mut decoded_peer_state = PeerState :: read ( & mut Cursor :: new ( encoded_peer_state) ) . unwrap ( ) ;
2812+ let decoded_jit_channel = decoded_peer_state
2813+ . outbound_channels_by_intercept_scid
2814+ . get_mut ( & intercept_scid)
2815+ . unwrap ( ) ;
2816+
2817+ assert ! ( decoded_jit_channel. htlc_intercepted( htlc) . unwrap( ) . is_none( ) ) ;
2818+
2819+ let ForwardPaymentAction ( _, fee_payment) =
2820+ decoded_jit_channel. channel_ready ( ChannelId ( [ 3 ; 32 ] ) ) . unwrap ( ) ;
2821+ assert_eq ! ( fee_payment. htlcs, vec![ htlc] ) ;
2822+ }
2823+
27632824 #[ test]
27642825 fn broadcast_not_allowed_after_non_paying_fee_payment_claimed ( ) {
27652826 let min_fee_msat: u64 = 12345 ;
0 commit comments