Skip to content

Commit dbb1c96

Browse files
committed
Avoid splice checks when responding to stfu
Only gate local quiescence initiation on splice RBF eligibility. If the counterparty initiated quiescence first, respond with non-initiator stfu once pending channel updates are clear.
1 parent ee456a8 commit dbb1c96

2 files changed

Lines changed: 71 additions & 20 deletions

File tree

lightning/src/ln/channel.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14636,7 +14636,24 @@ where
1463614636
return None;
1463714637
}
1463814638

14639-
if let Some(action) = self.quiescent_action.as_ref() {
14639+
if self.context.is_waiting_on_peer_pending_channel_update()
14640+
|| self.context.is_monitor_or_signer_pending_channel_update()
14641+
{
14642+
log_given_level!(
14643+
logger,
14644+
logger_level,
14645+
"Waiting for state machine pending changes to complete before sending stfu"
14646+
);
14647+
return None;
14648+
}
14649+
14650+
let initiator = if self.context.channel_state.is_remote_stfu_sent() {
14651+
// Since we may have also attempted to initiate quiescence but the counterparty
14652+
// initiated first, we'll retry after we're no longer quiescent.
14653+
self.context.channel_state.clear_remote_stfu_sent();
14654+
self.context.channel_state.set_quiescent();
14655+
false
14656+
} else if let Some(action) = self.quiescent_action.as_ref() {
1464014657
#[allow(irrefutable_let_patterns)]
1464114658
if let QuiescentAction::Splice { contribution, .. } = action {
1464214659
if self.pending_splice.is_some() {
@@ -14663,29 +14680,16 @@ where
1466314680
}
1466414681
}
1466514682
}
14666-
}
1466714683

14668-
if self.context.is_waiting_on_peer_pending_channel_update()
14669-
|| self.context.is_monitor_or_signer_pending_channel_update()
14670-
{
14671-
log_given_level!(
14672-
logger,
14673-
logger_level,
14674-
"Waiting for state machine pending changes to complete before sending stfu"
14675-
);
14676-
return None;
14677-
}
14678-
14679-
let initiator = if self.context.channel_state.is_remote_stfu_sent() {
14680-
// Since we may have also attempted to initiate quiescence but the counterparty
14681-
// initiated first, we'll retry after we're no longer quiescent.
14682-
self.context.channel_state.clear_remote_stfu_sent();
14683-
self.context.channel_state.set_quiescent();
14684-
false
14685-
} else {
1468614684
log_debug!(logger, "Sending stfu as quiescence initiator");
1468714685
self.context.channel_state.set_local_stfu_sent();
1468814686
true
14687+
} else {
14688+
debug_assert!(
14689+
false,
14690+
"Either we have a pending quiescent action or need to respond to the counterparty"
14691+
);
14692+
false
1468914693
};
1469014694

1469114695
Some(msgs::Stfu { channel_id: self.context.channel_id, initiator })

lightning/src/ln/splicing_tests.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7841,6 +7841,53 @@ fn test_funding_contributed_rbf_adjustment_exceeds_max_feerate() {
78417841
assert_eq!(splice_init.funding_feerate_per_kw, FEERATE_FLOOR_SATS_PER_KW);
78427842
}
78437843

7844+
#[test]
7845+
fn test_peer_initiated_stfu_skips_local_rbf_feerate_check() {
7846+
// Test that a local low-fee splice RBF attempt does not prevent us from responding to a
7847+
// counterparty-initiated quiescence attempt.
7848+
let chanmon_cfgs = create_chanmon_cfgs(2);
7849+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
7850+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
7851+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
7852+
7853+
let node_id_0 = nodes[0].node.get_our_node_id();
7854+
let node_id_1 = nodes[1].node.get_our_node_id();
7855+
7856+
let initial_channel_value_sat = 100_000;
7857+
let (_, _, channel_id, _) =
7858+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_value_sat, 0);
7859+
7860+
let added_value = Amount::from_sat(50_000);
7861+
provide_utxo_reserves(&nodes, 4, added_value * 2);
7862+
7863+
let floor_feerate = FeeRate::from_sat_per_kwu(FEERATE_FLOOR_SATS_PER_KW as u64);
7864+
let node_0_template = nodes[0].node.splice_channel(&channel_id, &node_id_1).unwrap();
7865+
let wallet = WalletSync::new(Arc::clone(&nodes[0].wallet_source), nodes[0].logger);
7866+
let node_0_contribution =
7867+
node_0_template.splice_in_sync(added_value, floor_feerate, floor_feerate, &wallet).unwrap();
7868+
7869+
// Node 1 creates a pending splice before node 0 submits its contribution. Node 0's
7870+
// contribution cannot be adjusted up to the pending splice's minimum RBF feerate, so it must
7871+
// not send its own stfu yet.
7872+
let node_1_contribution = do_initiate_splice_in(&nodes[1], &nodes[0], channel_id, added_value);
7873+
let (_first_splice_tx, _) =
7874+
splice_channel(&nodes[1], &nodes[0], channel_id, node_1_contribution);
7875+
nodes[0].node.funding_contributed(&channel_id, &node_id_1, node_0_contribution, None).unwrap();
7876+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
7877+
7878+
// Node 1 can still initiate quiescence for its own RBF attempt. Node 0 should reply as the
7879+
// non-initiator instead of applying its local splice RBF feerate check to the response.
7880+
let min_rbf_feerate = FeeRate::from_sat_per_kwu(FEERATE_FLOOR_SATS_PER_KW as u64 + 25);
7881+
let _node_1_rbf_contribution =
7882+
do_initiate_rbf_splice_in(&nodes[1], &nodes[0], channel_id, min_rbf_feerate);
7883+
let stfu_init = get_event_msg!(nodes[1], MessageSendEvent::SendStfu, node_id_0);
7884+
assert!(stfu_init.initiator);
7885+
7886+
nodes[0].node.handle_stfu(node_id_1, &stfu_init);
7887+
let stfu_response = get_event_msg!(nodes[0], MessageSendEvent::SendStfu, node_id_1);
7888+
assert!(!stfu_response.initiator);
7889+
}
7890+
78447891
#[test]
78457892
fn test_funding_contributed_rbf_adjustment_insufficient_budget() {
78467893
// Test that when the change output can't absorb the fee increase needed for the minimum RBF feerate

0 commit comments

Comments
 (0)