Skip to content

Commit 4a47f19

Browse files
committed
Allow quiescence-init while disconnected from peers
There are a number of things in LDK where we've been lazy and not allowed the user to initiate an action while a peer is disconnected. While it may be accurate in the sense that the action cannot be started while the peer is disconnected, it is terrible dev UX - these actions can fail without the developer being at fault and the only way for them to address it is just try again. Here we fix this dev UX shortcoming for splicing, keeping any queued post-quiescent actions around when a peer disconnects and retrying the action (and quiescence generally) when the peer reconnects.
1 parent a46cea5 commit 4a47f19

File tree

2 files changed

+134
-7
lines changed

2 files changed

+134
-7
lines changed

lightning/src/ln/channel.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8223,7 +8223,10 @@ where
82238223

82248224
// Reset any quiescence-related state as it is implicitly terminated once disconnected.
82258225
if matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8226-
self.context.channel_state.clear_awaiting_quiescence();
8226+
if self.quiescent_action.is_some() {
8227+
// If we were trying to get quiescent, try again after reconnection.
8228+
self.context.channel_state.set_awaiting_quiescence();
8229+
}
82278230
self.context.channel_state.clear_local_stfu_sent();
82288231
self.context.channel_state.clear_remote_stfu_sent();
82298232
self.context.channel_state.clear_quiescent();
@@ -11571,9 +11574,9 @@ where
1157111574
{
1157211575
log_debug!(logger, "Attempting to initiate quiescence");
1157311576

11574-
if !self.context.is_live() {
11577+
if !self.context.is_usable() {
1157511578
return Err(ChannelError::Ignore(
11576-
"Channel is not in a live state to propose quiescence".to_owned()
11579+
"Channel is not in a usable state to propose quiescence".to_owned()
1157711580
));
1157811581
}
1157911582
if self.quiescent_action.is_some() {
@@ -11589,7 +11592,11 @@ where
1158911592
}
1159011593

1159111594
self.context.channel_state.set_awaiting_quiescence();
11592-
Ok(Some(self.send_stfu(logger)?))
11595+
if self.context.is_live() {
11596+
Ok(Some(self.send_stfu(logger)?))
11597+
} else {
11598+
Ok(None)
11599+
}
1159311600
}
1159411601

1159511602
// Assumes we are either awaiting quiescence or our counterparty has requested quiescence.
@@ -11599,7 +11606,6 @@ where
1159911606
L::Target: Logger,
1160011607
{
1160111608
debug_assert!(!self.context.channel_state.is_local_stfu_sent());
11602-
// Either state being set implies the channel is live.
1160311609
debug_assert!(
1160411610
self.context.channel_state.is_awaiting_quiescence()
1160511611
|| self.context.channel_state.is_remote_stfu_sent()
@@ -11731,6 +11737,10 @@ where
1173111737
&& self.context.channel_state.is_remote_stfu_sent())
1173211738
);
1173311739

11740+
if !self.context.is_live() {
11741+
return Ok(None);
11742+
}
11743+
1173411744
// We need to send our `stfu`, either because we're trying to initiate quiescence, or the
1173511745
// counterparty is and we've yet to send ours.
1173611746
if self.context.channel_state.is_awaiting_quiescence()
@@ -12870,7 +12880,11 @@ where
1287012880
match channel_state {
1287112881
ChannelState::AwaitingChannelReady(_) => {},
1287212882
ChannelState::ChannelReady(_) => {
12873-
channel_state.clear_awaiting_quiescence();
12883+
if self.quiescent_action.is_some() {
12884+
// If we're trying to get quiescent to do something, try again when we
12885+
// reconnect to the peer.
12886+
channel_state.set_awaiting_quiescence();
12887+
}
1287412888
channel_state.clear_local_stfu_sent();
1287512889
channel_state.clear_remote_stfu_sent();
1287612890
channel_state.clear_quiescent();
@@ -13277,6 +13291,7 @@ where
1327713291
(59, self.funding.minimum_depth_override, option), // Added in 0.2
1327813292
(60, self.context.historical_scids, optional_vec), // Added in 0.2
1327913293
(61, fulfill_attribution_data, optional_vec), // Added in 0.2
13294+
(63, self.quiescent_action, option), // Added in 0.2
1328013295
});
1328113296

1328213297
Ok(())
@@ -13637,6 +13652,8 @@ where
1363713652

1363813653
let mut minimum_depth_override: Option<u32> = None;
1363913654

13655+
let mut quiescent_action = None;
13656+
1364013657
read_tlv_fields!(reader, {
1364113658
(0, announcement_sigs, option),
1364213659
(1, minimum_depth, option),
@@ -13679,6 +13696,7 @@ where
1367913696
(59, minimum_depth_override, option), // Added in 0.2
1368013697
(60, historical_scids, optional_vec), // Added in 0.2
1368113698
(61, fulfill_attribution_data, optional_vec), // Added in 0.2
13699+
(63, quiescent_action, upgradable_option), // Added in 0.2
1368213700
});
1368313701

1368413702
let holder_signer = signer_provider.derive_channel_signer(channel_keys_id);
@@ -14026,7 +14044,7 @@ where
1402614044
holder_commitment_point,
1402714045
#[cfg(splicing)]
1402814046
pending_splice: None,
14029-
quiescent_action: None,
14047+
quiescent_action,
1403014048
})
1403114049
}
1403214050
}

lightning/src/ln/quiescence_tests.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,3 +548,112 @@ fn test_quiescence_timeout_while_waiting_for_counterparty_stfu() {
548548
};
549549
assert!(nodes[1].node.get_and_clear_pending_msg_events().iter().find_map(f).is_some());
550550
}
551+
552+
fn do_test_quiescence_during_disconnection(with_pending_claim: bool, propose_disconnected: bool) {
553+
// Test that we'll start trying for quiescence immediately after reconnection if we're waiting
554+
// to do some quiescence-required action.
555+
let chanmon_cfgs = create_chanmon_cfgs(2);
556+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
557+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
558+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
559+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
560+
561+
let node_a_id = nodes[0].node.get_our_node_id();
562+
let node_b_id = nodes[1].node.get_our_node_id();
563+
564+
// First get both nodes off the starting state so we don't have to deal with channel_ready
565+
// retransmissions on reconect.
566+
send_payment(&nodes[0], &[&nodes[1]], 100_000);
567+
568+
let (preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], 100_000);
569+
if with_pending_claim {
570+
// Optionally reconnect with pending quiescence while there's some pending messages to
571+
// deliver.
572+
nodes[1].node.claim_funds(preimage);
573+
check_added_monitors(&nodes[1], 1);
574+
expect_payment_claimed!(nodes[1], payment_hash, 100_000);
575+
let _ = get_htlc_update_msgs(&nodes[1], &node_a_id);
576+
}
577+
578+
if !propose_disconnected {
579+
nodes[1].node.maybe_propose_quiescence(&node_a_id, &chan_id).unwrap();
580+
}
581+
582+
nodes[0].node.peer_disconnected(node_b_id);
583+
nodes[1].node.peer_disconnected(node_a_id);
584+
585+
if propose_disconnected {
586+
nodes[1].node.maybe_propose_quiescence(&node_a_id, &chan_id).unwrap();
587+
}
588+
589+
let init_msg = msgs::Init {
590+
features: nodes[1].node.init_features(),
591+
networks: None,
592+
remote_network_address: None,
593+
};
594+
nodes[0].node.peer_connected(node_b_id, &init_msg, true).unwrap();
595+
nodes[1].node.peer_connected(node_a_id, &init_msg, true).unwrap();
596+
597+
let reestab_a = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, node_b_id);
598+
let reestab_b = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, node_a_id);
599+
600+
nodes[0].node.handle_channel_reestablish(node_b_id, &reestab_b);
601+
get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, node_b_id);
602+
603+
nodes[1].node.handle_channel_reestablish(node_a_id, &reestab_a);
604+
let mut bs_msgs = nodes[1].node.get_and_clear_pending_msg_events();
605+
bs_msgs.retain(|msg| !matches!(msg, MessageSendEvent::SendChannelUpdate { .. }));
606+
assert_eq!(bs_msgs.len(), 1, "{bs_msgs:?}");
607+
let stfu = if with_pending_claim {
608+
// Node B should first re-send its channel update, then try to enter quiescence once that
609+
// completes...
610+
let msg = bs_msgs.pop().unwrap();
611+
if let MessageSendEvent::UpdateHTLCs { mut updates, .. } = msg {
612+
let fulfill = updates.update_fulfill_htlcs.pop().unwrap();
613+
nodes[0].node.handle_update_fulfill_htlc(node_b_id, fulfill);
614+
let cs = updates.commitment_signed;
615+
nodes[0].node.handle_commitment_signed_batch_test(node_b_id, &cs);
616+
check_added_monitors(&nodes[0], 1);
617+
618+
let (raa, cs) = get_revoke_commit_msgs(&nodes[0], &node_b_id);
619+
nodes[1].node.handle_revoke_and_ack(node_a_id, &raa);
620+
check_added_monitors(&nodes[1], 1);
621+
nodes[1].node.handle_commitment_signed_batch_test(node_a_id, &cs);
622+
check_added_monitors(&nodes[1], 1);
623+
624+
let mut bs_raa_stfu = nodes[1].node.get_and_clear_pending_msg_events();
625+
assert_eq!(bs_raa_stfu.len(), 2);
626+
if let MessageSendEvent::SendRevokeAndACK { msg, .. } = &bs_raa_stfu[0] {
627+
nodes[0].node.handle_revoke_and_ack(node_b_id, &msg);
628+
expect_payment_sent!(&nodes[0], preimage);
629+
} else {
630+
panic!("Unexpected first message {bs_raa_stfu:?}");
631+
}
632+
633+
bs_raa_stfu.pop().unwrap()
634+
} else {
635+
panic!("Unexpected message {msg:?}");
636+
}
637+
} else {
638+
bs_msgs.pop().unwrap()
639+
};
640+
if let MessageSendEvent::SendStfu { msg, .. } = stfu {
641+
nodes[0].node.handle_stfu(node_b_id, &msg);
642+
} else {
643+
panic!("Unexpected message {stfu:?}");
644+
}
645+
646+
let stfu_resp = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, node_b_id);
647+
nodes[1].node.handle_stfu(node_a_id, &stfu_resp);
648+
649+
assert!(nodes[0].node.exit_quiescence(&node_b_id, &chan_id).unwrap());
650+
assert!(nodes[1].node.exit_quiescence(&node_a_id, &chan_id).unwrap());
651+
}
652+
653+
#[test]
654+
fn test_quiescence_during_disconnection() {
655+
do_test_quiescence_during_disconnection(false, false);
656+
do_test_quiescence_during_disconnection(true, false);
657+
do_test_quiescence_during_disconnection(false, true);
658+
do_test_quiescence_during_disconnection(true, true);
659+
}

0 commit comments

Comments
 (0)