Skip to content

Commit 9dd4e19

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 47ea758 commit 9dd4e19

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

lightning/src/ln/channel.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7062,6 +7062,10 @@ impl<SP: Deref> FundedChannel<SP> where
70627062
} else if
70637063
// Cleared upon receiving `channel_reestablish`.
70647064
self.context.channel_state.is_peer_disconnected()
7065+
// Cleared upon receiving `stfu`.
7066+
|| self.context.channel_state.is_local_stfu_sent()
7067+
// Cleared upon receiving a message that triggers the end of quiescence.
7068+
|| self.context.channel_state.is_quiescent()
70657069
// Cleared upon receiving `revoke_and_ack`.
70667070
|| self.context.has_pending_channel_update()
70677071
{
@@ -8817,6 +8821,9 @@ impl<SP: Deref> FundedChannel<SP> where
88178821
let is_holder_quiescence_initiator = !msg.initiator || self.context.is_outbound();
88188822
self.context.is_holder_quiescence_initiator = Some(is_holder_quiescence_initiator);
88198823

8824+
// We were expecting to receive `stfu` because we already sent ours.
8825+
self.mark_response_received();
8826+
88208827
if self.context.has_pending_channel_update() {
88218828
return Err(ChannelError::Ignore(
88228829
"Received counterparty stfu, cannot become quiescent until state machine is no longer pending".to_owned()
@@ -8895,6 +8902,7 @@ impl<SP: Deref> FundedChannel<SP> where
88958902
debug_assert!(!self.context.channel_state.is_remote_stfu_sent());
88968903

88978904
if self.context.channel_state.is_quiescent() {
8905+
self.mark_response_received();
88988906
self.context.channel_state.clear_quiescent();
88998907
self.context.is_holder_quiescence_initiator.take().expect("Must always be set while quiescent")
89008908
} else {

lightning/src/ln/quiescence_tests.rs

Lines changed: 86 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
use crate::util::test_channel_signer::SignerOp;
@@ -410,3 +412,87 @@ fn quiescence_updates_go_to_holding_cell(fail_htlc: bool) {
410412
expect_payment_sent(&nodes[1], payment_preimage1, None, true, true);
411413
}
412414
}
415+
416+
#[test]
417+
fn test_quiescence_timeout() {
418+
// Test that we'll disconnect if we remain quiescent for `DISCONNECT_PEER_AWAITING_RESPONSE_TICKS`.
419+
let chanmon_cfgs = create_chanmon_cfgs(2);
420+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
421+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
422+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
423+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
424+
425+
let node_id_0 = nodes[0].node.get_our_node_id();
426+
let node_id_1 = nodes[1].node.get_our_node_id();
427+
428+
nodes[0].node.maybe_propose_quiescence(&nodes[1].node.get_our_node_id(), &chan_id).unwrap();
429+
430+
let stfu_initiator = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, node_id_1);
431+
nodes[1].node.handle_stfu(node_id_0, &stfu_initiator);
432+
433+
let stfu_responder = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
434+
nodes[0].node.handle_stfu(node_id_1, &stfu_responder);
435+
436+
assert!(stfu_initiator.initiator && !stfu_responder.initiator);
437+
438+
for _ in 0..DISCONNECT_PEER_AWAITING_RESPONSE_TICKS {
439+
nodes[0].node.timer_tick_occurred();
440+
nodes[1].node.timer_tick_occurred();
441+
}
442+
443+
let f = |event| {
444+
if let MessageSendEvent::HandleError { action, .. } = event {
445+
if let msgs::ErrorAction::DisconnectPeerWithWarning { .. } = action {
446+
Some(())
447+
} else {
448+
None
449+
}
450+
} else {
451+
None
452+
}
453+
};
454+
assert!(nodes[0].node.get_and_clear_pending_msg_events().into_iter().find_map(f).is_some());
455+
assert!(nodes[1].node.get_and_clear_pending_msg_events().into_iter().find_map(f).is_some());
456+
}
457+
458+
#[test]
459+
fn test_quiescence_timeout_while_waiting_for_counterparty_stfu() {
460+
// Test that we'll disconnect if the counterparty does not send their stfu within a reasonable
461+
// time if we've already sent ours.
462+
let chanmon_cfgs = create_chanmon_cfgs(2);
463+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
464+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
465+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
466+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
467+
468+
let node_id_0 = nodes[0].node.get_our_node_id();
469+
470+
nodes[1].node.maybe_propose_quiescence(&node_id_0, &chan_id).unwrap();
471+
let _ = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
472+
473+
// Route a payment in between to ensure expecting to receive `revoke_and_ack` doesn't override
474+
// the expectation of receiving `stfu` as well.
475+
let _ = route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
476+
477+
for _ in 0..DISCONNECT_PEER_AWAITING_RESPONSE_TICKS {
478+
nodes[0].node.timer_tick_occurred();
479+
nodes[1].node.timer_tick_occurred();
480+
}
481+
482+
// nodes[0] hasn't received stfu from nodes[1], so it's not enforcing any timeouts.
483+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
484+
485+
// nodes[1] didn't receive nodes[0]'s stfu within the timeout so it'll disconnect.
486+
let f = |&ref event| {
487+
if let MessageSendEvent::HandleError { action, .. } = event {
488+
if let msgs::ErrorAction::DisconnectPeerWithWarning { .. } = action {
489+
Some(())
490+
} else {
491+
None
492+
}
493+
} else {
494+
None
495+
}
496+
};
497+
assert!(nodes[1].node.get_and_clear_pending_msg_events().iter().find_map(f).is_some());
498+
}

0 commit comments

Comments
 (0)