Skip to content

Commit 23e8a55

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 239c4f8 commit 23e8a55

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
@@ -8226,7 +8226,10 @@ where
82268226

82278227
// Reset any quiescence-related state as it is implicitly terminated once disconnected.
82288228
if matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8229-
self.context.channel_state.clear_awaiting_quiescence();
8229+
if self.context.post_quiescence_action.is_some() {
8230+
// If we were trying to get quiescent, try again after reconnection.
8231+
self.context.channel_state.set_awaiting_quiescence();
8232+
}
82308233
self.context.channel_state.clear_local_stfu_sent();
82318234
self.context.channel_state.clear_remote_stfu_sent();
82328235
self.context.channel_state.clear_quiescent();
@@ -11574,9 +11577,9 @@ where
1157411577
{
1157511578
log_debug!(logger, "Attempting to initiate quiescence");
1157611579

11577-
if !self.context.is_live() {
11580+
if !self.context.is_usable() {
1157811581
return Err(ChannelError::Ignore(
11579-
"Channel is not in a live state to propose quiescence".to_owned()
11582+
"Channel is not in a usable state to propose quiescence".to_owned()
1158011583
));
1158111584
}
1158211585
if self.context.post_quiescence_action.is_some() {
@@ -11592,7 +11595,11 @@ where
1159211595
}
1159311596

1159411597
self.context.channel_state.set_awaiting_quiescence();
11595-
Ok(Some(self.send_stfu(logger)?))
11598+
if self.context.is_live() {
11599+
Ok(Some(self.send_stfu(logger)?))
11600+
} else {
11601+
Ok(None)
11602+
}
1159611603
}
1159711604

1159811605
// Assumes we are either awaiting quiescence or our counterparty has requested quiescence.
@@ -11602,7 +11609,6 @@ where
1160211609
L::Target: Logger,
1160311610
{
1160411611
debug_assert!(!self.context.channel_state.is_local_stfu_sent());
11605-
// Either state being set implies the channel is live.
1160611612
debug_assert!(
1160711613
self.context.channel_state.is_awaiting_quiescence()
1160811614
|| self.context.channel_state.is_remote_stfu_sent()
@@ -11734,6 +11740,10 @@ where
1173411740
&& self.context.channel_state.is_remote_stfu_sent())
1173511741
);
1173611742

11743+
if !self.context.is_live() {
11744+
return Ok(None);
11745+
}
11746+
1173711747
// We need to send our `stfu`, either because we're trying to initiate quiescence, or the
1173811748
// counterparty is and we've yet to send ours.
1173911749
if self.context.channel_state.is_awaiting_quiescence()
@@ -12871,7 +12881,11 @@ where
1287112881
match channel_state {
1287212882
ChannelState::AwaitingChannelReady(_) => {},
1287312883
ChannelState::ChannelReady(_) => {
12874-
channel_state.clear_awaiting_quiescence();
12884+
if self.context.post_quiescence_action.is_some() {
12885+
// If we're trying to get quiescent to do something, try again when we
12886+
// reconnect to the peer.
12887+
channel_state.set_awaiting_quiescence();
12888+
}
1287512889
channel_state.clear_local_stfu_sent();
1287612890
channel_state.clear_remote_stfu_sent();
1287712891
channel_state.clear_quiescent();
@@ -13278,6 +13292,7 @@ where
1327813292
(59, self.funding.minimum_depth_override, option), // Added in 0.2
1327913293
(60, self.context.historical_scids, optional_vec), // Added in 0.2
1328013294
(61, fulfill_attribution_data, optional_vec), // Added in 0.2
13295+
(63, self.context.post_quiescence_action, option), // Added in 0.2
1328113296
});
1328213297

1328313298
Ok(())
@@ -13638,6 +13653,8 @@ where
1363813653

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

13656+
let mut post_quiescence_action = None;
13657+
1364113658
read_tlv_fields!(reader, {
1364213659
(0, announcement_sigs, option),
1364313660
(1, minimum_depth, option),
@@ -13680,6 +13697,7 @@ where
1368013697
(59, minimum_depth_override, option), // Added in 0.2
1368113698
(60, historical_scids, optional_vec), // Added in 0.2
1368213699
(61, fulfill_attribution_data, optional_vec), // Added in 0.2
13700+
(63, post_quiescence_action, upgradable_option), // Added in 0.2
1368313701
});
1368413702

1368513703
let holder_signer = signer_provider.derive_channel_signer(channel_keys_id);
@@ -14023,7 +14041,7 @@ where
1402314041
blocked_monitor_updates: blocked_monitor_updates.unwrap(),
1402414042
is_manual_broadcast: is_manual_broadcast.unwrap_or(false),
1402514043

14026-
post_quiescence_action: None,
14044+
post_quiescence_action,
1402714045
},
1402814046
interactive_tx_signing_session,
1402914047
holder_commitment_point,

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)