Skip to content

Commit 2809840

Browse files
committed
Fix liquidity event cancellation in select loop
handle_next_event() both consumed events from the queue via next_event_async() and processed them inline, including spawn_blocking(...).await calls for wallet checks. Because this ran as a polled future inside tokio::select!, any tick timer firing while the handler was suspended at an .await point would cancel the future. The event had already been dequeued, so it was silently lost. Split event receipt from processing: next_event_async() is polled as the select! future (cancellation-safe since it only dequeues on Poll::Ready), and handle_event() runs in the handler block which select! guarantees runs to completion before the next iteration. This was the root cause of JIT channel opens and splices timing out in production. The HTLC would be intercepted, the OpenChannel event consumed from the queue, but a timer tick would cancel processing before create_channel was called. The peer would disconnect after 40s and the HTLC would expire.
1 parent e174f61 commit 2809840

2 files changed

Lines changed: 10 additions & 3 deletions

File tree

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ impl Node {
651651
// First tick fires immediately; consume it so we don't run at t=0.
652652
pending_htlc_interval.tick().await;
653653
expiry_check_interval.tick().await;
654+
let lm = liquidity_handler.liquidity_manager();
654655
loop {
655656
tokio::select! {
656657
_ = stop_liquidity_handler.changed() => {
@@ -666,7 +667,9 @@ impl Node {
666667
_ = expiry_check_interval.tick() => {
667668
liquidity_handler.handle_expired_htlcs().await;
668669
}
669-
_ = liquidity_handler.handle_next_event() => {}
670+
event = lm.next_event_async() => {
671+
liquidity_handler.handle_event(event).await;
672+
}
670673
}
671674
}
672675
});

src/liquidity.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,12 @@ where
523523
}
524524
}
525525

526-
pub(crate) async fn handle_next_event(&self) {
527-
match self.liquidity_manager.next_event_async().await {
526+
/// Handles a single liquidity event. Must be called from a context that
527+
/// guarantees the future runs to completion (e.g. a `select!` handler block),
528+
/// since event processing includes `.await` points that are not
529+
/// cancellation-safe.
530+
pub(crate) async fn handle_event(&self, event: LiquidityEvent) {
531+
match event {
528532
LiquidityEvent::LSPS1Client(LSPS1ClientEvent::SupportedOptionsReady {
529533
request_id,
530534
counterparty_node_id,

0 commit comments

Comments
 (0)