Skip to content

Commit 1facb94

Browse files
fix(lsps4): track channel opens in flight to prevent stuck HTLC loop
When an HTLC arrives for an offline peer, the webhook wakes them up. On reconnect, peer_connected defers because channels need reestablish. Once usable, process_pending_htlcs found insufficient capacity but assumed a channel open was already in flight - with no actual tracking. Nobody ever opened the channel, causing an infinite retry loop until the HTLC expired after 45s. Add channel_opens_in_flight HashSet so process_pending_htlcs can: - Emit OpenChannel on the first tick when a new channel is needed - Skip on subsequent ticks while the open is in progress - Clear the flag on channel_ready or peer_disconnected
1 parent eecf53e commit 1facb94

1 file changed

Lines changed: 18 additions & 5 deletions

File tree

lightning-liquidity/src/lsps4/service.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ where
101101
scid_store: ScidStore<L, K>,
102102
htlc_store: HTLCStore<L, K>,
103103
connected_peers: RwLock<HashSet<PublicKey>>,
104+
channel_opens_in_flight: RwLock<HashSet<PublicKey>>,
104105
config: LSPS4ServiceConfig,
105106
}
106107

@@ -128,6 +129,7 @@ where
128129
config,
129130
logger,
130131
connected_peers: RwLock::new(HashSet::new()),
132+
channel_opens_in_flight: RwLock::new(HashSet::new()),
131133
})
132134
}
133135

@@ -275,6 +277,8 @@ where
275277
pub fn channel_ready(
276278
&self, counterparty_node_id: &PublicKey,
277279
) -> Result<(), APIError> {
280+
self.channel_opens_in_flight.write().unwrap().remove(counterparty_node_id);
281+
278282
let is_connected = self.is_peer_connected(counterparty_node_id);
279283

280284
log_info!(
@@ -470,6 +474,8 @@ where
470474

471475
/// Will update the set of connected peers
472476
pub fn peer_disconnected(&self, counterparty_node_id: &PublicKey) {
477+
self.channel_opens_in_flight.write().unwrap().remove(counterparty_node_id);
478+
473479
let (was_present, remaining_count) = {
474480
let mut peers = self.connected_peers.write().unwrap();
475481
let was = peers.remove(counterparty_node_id);
@@ -525,16 +531,23 @@ where
525531
);
526532
let actions = self.calculate_htlc_actions_for_peer(node_id, htlcs);
527533
if actions.new_channel_needed_msat.is_some() {
528-
// A channel open is already in flight from htlc_intercepted or
529-
// peer_connected. Skip — channel_ready will handle forwarding
530-
// once the new channel is established.
534+
let already_in_flight = self.channel_opens_in_flight.read().unwrap().contains(&node_id);
535+
if already_in_flight {
536+
log_info!(
537+
self.logger,
538+
"[LSPS4] process_pending_htlcs: peer {} needs a new channel, \
539+
skipping (channel open already in flight)",
540+
node_id
541+
);
542+
continue;
543+
}
531544
log_info!(
532545
self.logger,
533546
"[LSPS4] process_pending_htlcs: peer {} needs a new channel, \
534-
skipping (channel open already in flight)",
547+
triggering OpenChannel",
535548
node_id
536549
);
537-
continue;
550+
self.channel_opens_in_flight.write().unwrap().insert(node_id);
538551
}
539552
self.execute_htlc_actions(actions, node_id);
540553
}

0 commit comments

Comments
 (0)