Skip to content

Commit 311a986

Browse files
authored
Merge pull request #4608 from jkczyz/2026-05-splice-rbf-fail-event-follow-ups
Drop `QuiescentError::with_negotiation_failure_reason`
2 parents 71fdc27 + f4139db commit 311a986

4 files changed

Lines changed: 63 additions & 52 deletions

File tree

lightning/src/events/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,6 +1706,12 @@ pub enum Event {
17061706
/// Alternatively, call [`ChannelManager::splice_channel`] to obtain a fresh
17071707
/// [`FundingTemplate`] and build a new contribution.
17081708
///
1709+
/// The contribution preserves the full set of inputs and outputs from the failed round,
1710+
/// including any that were also committed to a prior negotiated (but not yet locked)
1711+
/// splice transaction. Those overlapping inputs and outputs are intentionally omitted
1712+
/// from the preceding [`Event::DiscardFunding`], since they remain committed to that
1713+
/// prior splice.
1714+
///
17091715
/// [`ChannelManager::funding_contributed`]: crate::ln::channelmanager::ChannelManager::funding_contributed
17101716
/// [`ChannelManager::splice_channel`]: crate::ln::channelmanager::ChannelManager::splice_channel
17111717
/// [`FundingTemplate`]: crate::ln::funding::FundingTemplate

lightning/src/ln/channel.rs

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3250,16 +3250,6 @@ pub(super) enum QuiescentError {
32503250
FailSplice(SpliceFundingFailed, NegotiationFailureReason),
32513251
}
32523252

3253-
impl QuiescentError {
3254-
fn with_negotiation_failure_reason(mut self, reason: NegotiationFailureReason) -> Self {
3255-
match self {
3256-
QuiescentError::FailSplice(_, ref mut r) => *r = reason,
3257-
_ => debug_assert!(false, "Expected FailSplice variant"),
3258-
}
3259-
self
3260-
}
3261-
}
3262-
32633253
pub(crate) enum StfuResponse {
32643254
Stfu(msgs::Stfu),
32653255
SpliceInit(msgs::SpliceInit),
@@ -7217,27 +7207,13 @@ where
72177207
.expect("is_initiator is true so this always returns Some")
72187208
}
72197209

7220-
fn quiescent_action_into_error(&self, action: QuiescentAction) -> QuiescentError {
7221-
match action {
7222-
QuiescentAction::Splice { contribution, .. } => QuiescentError::FailSplice(
7223-
self.splice_funding_failed_for(contribution),
7224-
NegotiationFailureReason::Unknown,
7225-
),
7226-
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
7227-
QuiescentAction::DoNothing => QuiescentError::DoNothing,
7228-
}
7229-
}
7230-
72317210
fn abandon_quiescent_action(&mut self) -> Option<SpliceFundingFailed> {
7232-
let action = self.quiescent_action.take()?;
7233-
match self.quiescent_action_into_error(action) {
7234-
QuiescentError::FailSplice(failed, _) => Some(failed),
7235-
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
7236-
QuiescentError::DoNothing => None,
7237-
_ => {
7238-
debug_assert!(false);
7239-
None
7211+
match self.quiescent_action.take()? {
7212+
QuiescentAction::Splice { contribution, .. } => {
7213+
Some(self.splice_funding_failed_for(contribution))
72407214
},
7215+
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
7216+
QuiescentAction::DoNothing => None,
72417217
}
72427218
}
72437219

@@ -12734,22 +12710,28 @@ where
1273412710
) -> Result<Option<msgs::Stfu>, QuiescentError> {
1273512711
debug_assert!(contribution.is_splice());
1273612712

12737-
if let Some(QuiescentAction::Splice { contribution: existing, .. }) = &self.quiescent_action
12738-
{
12739-
let pending_splice = self.pending_splice.as_ref();
12740-
let prior_inputs = pending_splice
12741-
.into_iter()
12742-
.flat_map(|pending_splice| pending_splice.contributed_inputs());
12743-
let prior_outputs = pending_splice
12744-
.into_iter()
12745-
.flat_map(|pending_splice| pending_splice.contributed_outputs());
12746-
return match contribution.into_unique_contributions(
12747-
existing.contributed_inputs().chain(prior_inputs),
12748-
existing.contributed_outputs().chain(prior_outputs),
12749-
) {
12750-
None => Err(QuiescentError::DoNothing),
12751-
Some((inputs, outputs)) => Err(QuiescentError::DiscardFunding { inputs, outputs }),
12752-
};
12713+
match self.quiescent_action.as_ref() {
12714+
Some(QuiescentAction::Splice { contribution: existing, .. }) => {
12715+
let pending_splice = self.pending_splice.as_ref();
12716+
let prior_inputs = pending_splice
12717+
.into_iter()
12718+
.flat_map(|pending_splice| pending_splice.contributed_inputs());
12719+
let prior_outputs = pending_splice
12720+
.into_iter()
12721+
.flat_map(|pending_splice| pending_splice.contributed_outputs());
12722+
return match contribution.into_unique_contributions(
12723+
existing.contributed_inputs().chain(prior_inputs),
12724+
existing.contributed_outputs().chain(prior_outputs),
12725+
) {
12726+
None => Err(QuiescentError::DoNothing),
12727+
Some((inputs, outputs)) => {
12728+
Err(QuiescentError::DiscardFunding { inputs, outputs })
12729+
},
12730+
};
12731+
},
12732+
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
12733+
Some(QuiescentAction::DoNothing) => unreachable!(),
12734+
None => {},
1275312735
}
1275412736

1275512737
let initiated_funding_negotiation = self
@@ -14396,25 +14378,41 @@ where
1439614378
) -> Result<Option<msgs::Stfu>, QuiescentError> {
1439714379
log_debug!(logger, "Attempting to initiate quiescence");
1439814380

14399-
// TODO: NegotiationFailureReason is splice-specific, but propose_quiescence is
14400-
// generic. The reason should be selected by the caller, but it currently can't
14401-
// distinguish why quiescence failed. Revisit when a second quiescent protocol is added.
1440214381
if !self.context.is_usable() {
1440314382
debug_assert!(
1440414383
self.context.channel_state.is_local_shutdown_sent()
1440514384
|| self.context.channel_state.is_remote_shutdown_sent(),
1440614385
"splice_channel should have prevented reaching propose_quiescence on a non-ready channel"
1440714386
);
1440814387
log_debug!(logger, "Channel is not in a usable state to propose quiescence");
14409-
return Err(self.quiescent_action_into_error(action)
14410-
.with_negotiation_failure_reason(NegotiationFailureReason::ChannelClosing));
14388+
return Err(match action {
14389+
QuiescentAction::Splice { contribution, .. } => QuiescentError::FailSplice(
14390+
self.splice_funding_failed_for(contribution),
14391+
NegotiationFailureReason::ChannelClosing,
14392+
),
14393+
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
14394+
QuiescentAction::DoNothing => QuiescentError::DoNothing,
14395+
});
1441114396
}
14397+
1441214398
if self.quiescent_action.is_some() {
14399+
debug_assert!(
14400+
false,
14401+
"callers must not invoke propose_quiescence with {:?} while quiescent_action is set",
14402+
action,
14403+
);
1441314404
log_debug!(
1441414405
logger,
1441514406
"Channel already has a pending quiescent action and cannot start another",
1441614407
);
14417-
return Err(self.quiescent_action_into_error(action));
14408+
return Err(match action {
14409+
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
14410+
QuiescentAction::DoNothing => QuiescentError::DoNothing,
14411+
QuiescentAction::Splice { contribution, .. } => QuiescentError::FailSplice(
14412+
self.splice_funding_failed_for(contribution),
14413+
NegotiationFailureReason::Unknown,
14414+
),
14415+
});
1441814416
}
1441914417
// Since we don't have a pending quiescent action, we should never be in a state where we
1442014418
// sent `stfu` without already having become quiescent.

lightning/src/ln/funding.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,13 @@ impl FundingContribution {
761761
(contributed_inputs, contributed_outputs.map(|output| output.script_pubkey).collect())
762762
}
763763

764+
/// Returns this contribution's inputs and outputs after removing any that overlap
765+
/// with the provided `existing_inputs`/`existing_outputs`.
766+
///
767+
/// Multiple contribution outputs sharing a `script_pubkey` are all dropped when any
768+
/// existing output uses the same script.
769+
///
770+
/// Returns `None` if every input and output was filtered as overlapping.
764771
pub(crate) fn into_unique_contributions<'a>(
765772
self, existing_inputs: impl Iterator<Item = OutPoint>,
766773
existing_outputs: impl Iterator<Item = &'a bitcoin::Script>,

lightning/src/ln/splicing_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3847,7 +3847,7 @@ fn do_abandon_splice_quiescent_action_on_shutdown(local_shutdown: bool, pending_
38473847
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, initial_channel_capacity, 0);
38483848

38493849
// When testing with a prior pending splice, complete splice A first so that
3850-
// `quiescent_action_into_error` filters against `pending_splice.contributed_inputs/outputs`.
3850+
// `splice_funding_failed_for` filters against `pending_splice.contributed_inputs/outputs`.
38513851
if pending_splice {
38523852
let funding_contribution = do_initiate_splice_in(
38533853
&nodes[0],

0 commit comments

Comments
 (0)