Skip to content

Commit eb39f89

Browse files
committed
Enforce disconnect timeout during quiescence
Since new updates are not allowed during quiescence (local updates enter the holding cell), we want to ensure quiescence eventually terminates if the handshake takes too long or our counterparty is uncooperative. Disconnecting implicitly terminates quiescence, so the holding cell can be freed upon re-establishing the channel (assuming quiescence is not requested again).
1 parent 2892f98 commit eb39f89

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

lightning/src/ln/channel.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7113,6 +7113,10 @@ impl<SP: Deref> FundedChannel<SP> where
71137113
} else if
71147114
// Cleared upon receiving `channel_reestablish`.
71157115
self.context.channel_state.is_peer_disconnected()
7116+
// Cleared upon receiving `stfu`.
7117+
|| self.context.channel_state.is_local_stfu_sent()
7118+
// Cleared upon receiving a message that triggers the end of quiescence.
7119+
|| matches!(self.context.channel_state, ChannelState::ChannelQuiescent)
71167120
// Cleared upon receiving `revoke_and_ack`.
71177121
|| self.context.has_pending_channel_update(false)
71187122
|| self.context.has_pending_channel_update(true)
@@ -8896,6 +8900,7 @@ impl<SP: Deref> FundedChannel<SP> where
88968900
self.context.channel_state = ChannelState::ChannelQuiescent;
88978901
let quiescence_initiator = !msg.initiator || self.context.is_outbound();
88988902
self.context.quiescence_initiator = Some(quiescence_initiator);
8903+
self.mark_response_received();
88998904

89008905
log_debug!(
89018906
logger,
@@ -8909,6 +8914,7 @@ impl<SP: Deref> FundedChannel<SP> where
89098914
#[cfg(any(test, fuzzing))]
89108915
pub fn exit_quiescence(&mut self) -> bool {
89118916
if self.context.channel_state == ChannelState::ChannelQuiescent {
8917+
self.mark_response_received();
89128918
self.context.channel_state = ChannelState::ChannelReady(ChannelReadyFlags::new());
89138919
self.context.quiescence_initiator.take().expect("Must always be set while quiescent")
89148920
} else {

lightning/src/ln/quiescence_tests.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ use crate::events::Event;
33
use crate::events::HTLCDestination;
44
use crate::events::MessageSendEvent;
55
use crate::events::MessageSendEventsProvider;
6+
use crate::ln::channel::DISCONNECT_PEER_AWAITING_RESPONSE_TICKS;
67
use crate::ln::channelmanager::PaymentId;
78
use crate::ln::channelmanager::RecipientOnionFields;
89
use crate::ln::functional_test_utils::*;
10+
use crate::ln::msgs;
911
use crate::ln::msgs::{ChannelMessageHandler, ErrorAction};
1012
use crate::util::errors::APIError;
1113

@@ -373,3 +375,88 @@ fn quiescence_updates_go_to_holding_cell(fail_htlc: bool) {
373375
expect_payment_sent(&nodes[1], payment_preimage1, None, true, true);
374376
}
375377
}
378+
379+
#[test]
380+
fn test_quiescence_timeout() {
381+
// Test that we'll disconnect if we remain quiescent for `DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`.
382+
let chanmon_cfgs = create_chanmon_cfgs(2);
383+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
384+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
385+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
386+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
387+
388+
let node_id_0 = nodes[0].node.get_our_node_id();
389+
let node_id_1 = nodes[1].node.get_our_node_id();
390+
391+
nodes[0].node.maybe_propose_quiescence(&nodes[1].node.get_our_node_id(), &chan_id).unwrap();
392+
393+
let stfu_initiator = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, node_id_1);
394+
nodes[1].node.handle_stfu(node_id_0, &stfu_initiator);
395+
396+
let stfu_responder = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
397+
nodes[0].node.handle_stfu(node_id_1, &stfu_responder);
398+
399+
assert!(stfu_initiator.initiator && !stfu_responder.initiator);
400+
401+
for _ in 0..DISCONNECT_PEER_AWAITING_RESPONSE_TICKS {
402+
nodes[0].node.timer_tick_occurred();
403+
nodes[1].node.timer_tick_occurred();
404+
}
405+
406+
let f = |event| {
407+
if let MessageSendEvent::HandleError { action, .. } = event {
408+
if let msgs::ErrorAction::DisconnectPeerWithWarning { .. } = action {
409+
Some(())
410+
} else {
411+
None
412+
}
413+
} else {
414+
None
415+
}
416+
};
417+
assert!(nodes[0].node.get_and_clear_pending_msg_events().into_iter().find_map(f).is_some());
418+
assert!(nodes[1].node.get_and_clear_pending_msg_events().into_iter().find_map(f).is_some());
419+
}
420+
421+
#[test]
422+
fn test_quiescence_timeout_while_waiting_for_counterparty_stfu() {
423+
// Test that we'll disconnect if the counterparty does not send their stfu within a reasonable
424+
// time if we've already sent ours.
425+
let chanmon_cfgs = create_chanmon_cfgs(2);
426+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
427+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
428+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
429+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
430+
431+
let node_id_0 = nodes[0].node.get_our_node_id();
432+
433+
nodes[1].node.maybe_propose_quiescence(&node_id_0, &chan_id).unwrap();
434+
let _ = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
435+
436+
// Route a payment in between to ensure expecting to receive `revoke_and_ack` doesn't override
437+
// the expectation of receiving `stfu` as well.
438+
let _ = route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
439+
440+
for _ in 0..DISCONNECT_PEER_AWAITING_RESPONSE_TICKS {
441+
nodes[0].node.timer_tick_occurred();
442+
nodes[1].node.timer_tick_occurred();
443+
}
444+
445+
// nodes[0] hasn't received stfu from nodes[1], so it's not enforcing any timeouts.
446+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
447+
448+
// nodes[1] didn't receive nodes[0]'s stfu within the timeout so it'll disconnect.
449+
let f = |&ref event| {
450+
if let MessageSendEvent::HandleError { action, .. } = event {
451+
if let msgs::ErrorAction::DisconnectPeerWithWarning { .. } = action {
452+
Some(())
453+
} else {
454+
None
455+
}
456+
} else {
457+
None
458+
}
459+
};
460+
assert!(nodes[1].node.get_and_clear_pending_msg_events().iter().find_map(f).is_some());
461+
}
462+

0 commit comments

Comments
 (0)