Skip to content

Commit 49798a0

Browse files
authored
Fix liquidity event cancellation in select loop (#21)
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 ac65798 commit 49798a0

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
@@ -519,8 +519,12 @@ where
519519
}
520520
}
521521

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

0 commit comments

Comments
 (0)