Skip to content

Commit 98501d6

Browse files
authored
Merge pull request #4388 from jkczyz/2026-02-splice-discard-funding
Split `DiscardFunding` from `SpliceFailed` event
2 parents 9df659a + 24062c0 commit 98501d6

8 files changed

Lines changed: 905 additions & 148 deletions

File tree

lightning/src/events/mod.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ pub enum FundingInfo {
7777
/// The outpoint of the funding
7878
outpoint: transaction::OutPoint,
7979
},
80+
/// The contributions used for a dual funding or splice funding transaction.
81+
Contribution {
82+
/// UTXOs spent as inputs contributed to the funding transaction.
83+
inputs: Vec<OutPoint>,
84+
/// Outputs contributed to the funding transaction.
85+
outputs: Vec<TxOut>,
86+
},
8087
}
8188

8289
impl_writeable_tlv_based_enum!(FundingInfo,
@@ -85,6 +92,10 @@ impl_writeable_tlv_based_enum!(FundingInfo,
8592
},
8693
(1, OutPoint) => {
8794
(1, outpoint, required)
95+
},
96+
(2, Contribution) => {
97+
(1, inputs, optional_vec),
98+
(3, outputs, optional_vec),
8899
}
89100
);
90101

@@ -1561,10 +1572,6 @@ pub enum Event {
15611572
abandoned_funding_txo: Option<OutPoint>,
15621573
/// The features that this channel will operate with, if available.
15631574
channel_type: Option<ChannelTypeFeatures>,
1564-
/// UTXOs spent as inputs contributed to the splice transaction.
1565-
contributed_inputs: Vec<OutPoint>,
1566-
/// Outputs contributed to the splice transaction.
1567-
contributed_outputs: Vec<TxOut>,
15681575
},
15691576
/// Used to indicate to the user that they can abandon the funding transaction and recycle the
15701577
/// inputs for another purpose.
@@ -2326,8 +2333,6 @@ impl Writeable for Event {
23262333
ref counterparty_node_id,
23272334
ref abandoned_funding_txo,
23282335
ref channel_type,
2329-
ref contributed_inputs,
2330-
ref contributed_outputs,
23312336
} => {
23322337
52u8.write(writer)?;
23332338
write_tlv_fields!(writer, {
@@ -2336,8 +2341,6 @@ impl Writeable for Event {
23362341
(5, user_channel_id, required),
23372342
(7, counterparty_node_id, required),
23382343
(9, abandoned_funding_txo, option),
2339-
(11, *contributed_inputs, optional_vec),
2340-
(13, *contributed_outputs, optional_vec),
23412344
});
23422345
},
23432346
// Note that, going forward, all new events must only write data inside of
@@ -2965,8 +2968,6 @@ impl MaybeReadable for Event {
29652968
(5, user_channel_id, required),
29662969
(7, counterparty_node_id, required),
29672970
(9, abandoned_funding_txo, option),
2968-
(11, contributed_inputs, optional_vec),
2969-
(13, contributed_outputs, optional_vec),
29702971
});
29712972

29722973
Ok(Some(Event::SpliceFailed {
@@ -2975,8 +2976,6 @@ impl MaybeReadable for Event {
29752976
counterparty_node_id: counterparty_node_id.0.unwrap(),
29762977
abandoned_funding_txo,
29772978
channel_type,
2978-
contributed_inputs: contributed_inputs.unwrap_or_default(),
2979-
contributed_outputs: contributed_outputs.unwrap_or_default(),
29802979
}))
29812980
};
29822981
f()

lightning/src/ln/channel.rs

Lines changed: 94 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3050,6 +3050,35 @@ pub(crate) enum QuiescentAction {
30503050
DoNothing,
30513051
}
30523052

3053+
pub(super) enum QuiescentError {
3054+
DoNothing,
3055+
DiscardFunding { inputs: Vec<bitcoin::OutPoint>, outputs: Vec<bitcoin::TxOut> },
3056+
FailSplice(SpliceFundingFailed),
3057+
}
3058+
3059+
impl From<QuiescentAction> for QuiescentError {
3060+
fn from(action: QuiescentAction) -> Self {
3061+
match action {
3062+
QuiescentAction::LegacySplice(_) => {
3063+
debug_assert!(false);
3064+
QuiescentError::DoNothing
3065+
},
3066+
QuiescentAction::Splice { contribution, .. } => {
3067+
let (contributed_inputs, contributed_outputs) =
3068+
contribution.into_contributed_inputs_and_outputs();
3069+
return QuiescentError::FailSplice(SpliceFundingFailed {
3070+
funding_txo: None,
3071+
channel_type: None,
3072+
contributed_inputs,
3073+
contributed_outputs,
3074+
});
3075+
},
3076+
#[cfg(any(test, fuzzing, feature = "_test_utils"))]
3077+
QuiescentAction::DoNothing => QuiescentError::DoNothing,
3078+
}
3079+
}
3080+
}
3081+
30533082
pub(crate) enum StfuResponse {
30543083
Stfu(msgs::Stfu),
30553084
SpliceInit(msgs::SpliceInit),
@@ -6833,11 +6862,16 @@ impl FundingNegotiationContext {
68336862
(contributed_inputs, contributed_outputs)
68346863
}
68356864

6865+
fn contributed_inputs(&self) -> impl Iterator<Item = bitcoin::OutPoint> + '_ {
6866+
self.our_funding_inputs.iter().map(|input| input.utxo.outpoint)
6867+
}
6868+
6869+
fn contributed_outputs(&self) -> impl Iterator<Item = &TxOut> + '_ {
6870+
self.our_funding_outputs.iter()
6871+
}
6872+
68366873
fn to_contributed_inputs_and_outputs(&self) -> (Vec<bitcoin::OutPoint>, Vec<TxOut>) {
6837-
let contributed_inputs =
6838-
self.our_funding_inputs.iter().map(|input| input.utxo.outpoint).collect();
6839-
let contributed_outputs = self.our_funding_outputs.clone();
6840-
(contributed_inputs, contributed_outputs)
6874+
(self.contributed_inputs().collect(), self.contributed_outputs().cloned().collect())
68416875
}
68426876
}
68436877

@@ -12210,9 +12244,58 @@ where
1221012244

1221112245
pub fn funding_contributed<L: Logger>(
1221212246
&mut self, contribution: FundingContribution, locktime: LockTime, logger: &L,
12213-
) -> Result<Option<msgs::Stfu>, SpliceFundingFailed> {
12247+
) -> Result<Option<msgs::Stfu>, QuiescentError> {
1221412248
debug_assert!(contribution.is_splice());
1221512249

12250+
if let Some(QuiescentAction::Splice { contribution: existing, .. }) = &self.quiescent_action
12251+
{
12252+
return match contribution.into_unique_contributions(
12253+
existing.contributed_inputs(),
12254+
existing.contributed_outputs(),
12255+
) {
12256+
None => Err(QuiescentError::DoNothing),
12257+
Some((inputs, outputs)) => Err(QuiescentError::DiscardFunding { inputs, outputs }),
12258+
};
12259+
}
12260+
12261+
let initiated_funding_negotiation = self
12262+
.pending_splice
12263+
.as_ref()
12264+
.and_then(|pending_splice| pending_splice.funding_negotiation.as_ref())
12265+
.filter(|funding_negotiation| funding_negotiation.is_initiator());
12266+
12267+
if let Some(funding_negotiation) = initiated_funding_negotiation {
12268+
let unique_contributions = match funding_negotiation {
12269+
FundingNegotiation::AwaitingAck { context, .. } => contribution
12270+
.into_unique_contributions(
12271+
context.contributed_inputs(),
12272+
context.contributed_outputs(),
12273+
),
12274+
FundingNegotiation::ConstructingTransaction {
12275+
interactive_tx_constructor, ..
12276+
} => contribution.into_unique_contributions(
12277+
interactive_tx_constructor.contributed_inputs(),
12278+
interactive_tx_constructor.contributed_outputs(),
12279+
),
12280+
FundingNegotiation::AwaitingSignatures { .. } => {
12281+
let session = self
12282+
.context
12283+
.interactive_tx_signing_session
12284+
.as_ref()
12285+
.expect("pending splice awaiting signatures");
12286+
contribution.into_unique_contributions(
12287+
session.contributed_inputs(),
12288+
session.contributed_outputs(),
12289+
)
12290+
},
12291+
};
12292+
12293+
return match unique_contributions {
12294+
None => Err(QuiescentError::DoNothing),
12295+
Some((inputs, outputs)) => Err(QuiescentError::DiscardFunding { inputs, outputs }),
12296+
};
12297+
}
12298+
1221612299
if let Err(e) = contribution.validate().and_then(|()| {
1221712300
// For splice-out, our_funding_contribution is adjusted to cover fees if there
1221812301
// aren't any inputs.
@@ -12224,37 +12307,15 @@ where
1222412307
let (contributed_inputs, contributed_outputs) =
1222512308
contribution.into_contributed_inputs_and_outputs();
1222612309

12227-
return Err(SpliceFundingFailed {
12310+
return Err(QuiescentError::FailSplice(SpliceFundingFailed {
1222812311
funding_txo: None,
1222912312
channel_type: None,
1223012313
contributed_inputs,
1223112314
contributed_outputs,
12232-
});
12315+
}));
1223312316
}
1223412317

12235-
self.propose_quiescence(logger, QuiescentAction::Splice { contribution, locktime }).map_err(
12236-
|action| {
12237-
// FIXME: Any better way to do this?
12238-
if let QuiescentAction::Splice { contribution, .. } = action {
12239-
let (contributed_inputs, contributed_outputs) =
12240-
contribution.into_contributed_inputs_and_outputs();
12241-
SpliceFundingFailed {
12242-
funding_txo: None,
12243-
channel_type: None,
12244-
contributed_inputs,
12245-
contributed_outputs,
12246-
}
12247-
} else {
12248-
debug_assert!(false);
12249-
SpliceFundingFailed {
12250-
funding_txo: None,
12251-
channel_type: None,
12252-
contributed_inputs: vec![],
12253-
contributed_outputs: vec![],
12254-
}
12255-
}
12256-
},
12257-
)
12318+
self.propose_quiescence(logger, QuiescentAction::Splice { contribution, locktime })
1225812319
}
1225912320

1226012321
fn send_splice_init(&mut self, instructions: SpliceInstructions) -> msgs::SpliceInit {
@@ -13377,19 +13438,19 @@ where
1337713438
#[rustfmt::skip]
1337813439
pub fn propose_quiescence<L: Logger>(
1337913440
&mut self, logger: &L, action: QuiescentAction,
13380-
) -> Result<Option<msgs::Stfu>, QuiescentAction> {
13441+
) -> Result<Option<msgs::Stfu>, QuiescentError> {
1338113442
log_debug!(logger, "Attempting to initiate quiescence");
1338213443

1338313444
if !self.context.is_usable() {
1338413445
log_debug!(logger, "Channel is not in a usable state to propose quiescence");
13385-
return Err(action);
13446+
return Err(action.into());
1338613447
}
1338713448
if self.quiescent_action.is_some() {
1338813449
log_debug!(
1338913450
logger,
1339013451
"Channel already has a pending quiescent action and cannot start another",
1339113452
);
13392-
return Err(action);
13453+
return Err(action.into());
1339313454
}
1339413455
// Since we don't have a pending quiescent action, we should never be in a state where we
1339513456
// sent `stfu` without already having become quiescent.

0 commit comments

Comments
 (0)