From 15b04b5e5ece974ee7693a8ac449a7577f1e7514 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 18 Feb 2026 15:44:32 -0800 Subject: [PATCH] Avoid sending stfu for quiescent splice action while pending splice We can't splice while one is already pending, so there's no point in attempting to become quiescent. --- lightning/src/ln/channel.rs | 11 +++++++++++ lightning/src/ln/splicing_tests.rs | 21 ++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 27ccd1c12c0..5faa784614b 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -13619,6 +13619,17 @@ where return Ok(None); } + if let Some(action) = self.quiescent_action.as_ref() { + // We can't initiate another splice while ours is pending, so don't bother becoming + // quiescent yet. + // TODO(splicing): Allow the splice as an RBF once supported. + let has_splice_action = matches!(action, QuiescentAction::Splice { .. }) + || matches!(action, QuiescentAction::LegacySplice(_)); + if has_splice_action && self.pending_splice.is_some() { + return Ok(None); + } + } + // We need to send our `stfu`, either because we're trying to initiate quiescence, or the // counterparty is and we've yet to send ours. if self.context.channel_state.is_awaiting_quiescence() diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs index 6727437a38a..96b9b13f3a1 100644 --- a/lightning/src/ln/splicing_tests.rs +++ b/lightning/src/ln/splicing_tests.rs @@ -1031,6 +1031,12 @@ fn test_splice_in_and_out() { #[test] fn test_fails_initiating_concurrent_splices() { + fails_initiating_concurrent_splices(true); + fails_initiating_concurrent_splices(false); +} + +#[cfg(test)] +fn fails_initiating_concurrent_splices(reconnect: bool) { let chanmon_cfgs = create_chanmon_cfgs(2); let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let config = test_default_channel_config(); @@ -1043,6 +1049,7 @@ fn test_fails_initiating_concurrent_splices() { let node_0_id = nodes[0].node.get_our_node_id(); let node_1_id = nodes[1].node.get_our_node_id(); + send_payment(&nodes[0], &[&nodes[1]], 1_000); provide_utxo_reserves(&nodes, 2, Amount::ONE_BTC); let outputs = vec![TxOut { @@ -1116,15 +1123,23 @@ fn test_fails_initiating_concurrent_splices() { expect_splice_pending_event(&nodes[0], &node_1_id); expect_splice_pending_event(&nodes[1], &node_0_id); - // Now that the splice is pending, another splice may be initiated. + // Now that the splice is pending, another splice may be initiated, but we must wait until + // the `splice_locked` exchange to send the initiator `stfu`. assert!(nodes[0].node.splice_channel(&channel_id, &node_1_id, feerate).is_ok()); + if reconnect { + nodes[0].node.peer_disconnected(node_1_id); + nodes[1].node.peer_disconnected(node_0_id); + reconnect_nodes(ReconnectArgs::new(&nodes[0], &nodes[1])); + } + + assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); + assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); + mine_transaction(&nodes[0], &splice_tx); mine_transaction(&nodes[1], &splice_tx); let stfu = lock_splice_after_blocks(&nodes[0], &nodes[1], ANTI_REORG_DELAY - 1); - // However, the acceptor had enqueued a quiescent action while the splice was pending, so it - // will now attempt to initiate quiescence. assert!( matches!(stfu, Some(MessageSendEvent::SendStfu { node_id, .. }) if node_id == node_0_id) );