Skip to content

Commit 62ebc03

Browse files
jkczyzclaude
andcommitted
Derive SpliceFundingFailed inputs from FundingContribution
Replace the maybe_create_splice_funding_failed! macro and splice_funding_failed_for method with a unified splice_funding_failed_for! macro that derives contributed inputs and outputs from the FundingContribution rather than extracting them from the negotiation state. Callers pass ident parameters for which PendingSplice filtering methods to use: contributed_inputs/contributed_outputs when the current round's contribution has been popped or was never pushed, and prior_contributed_inputs/prior_contributed_outputs for the read-only persistence path where the contribution is cloned instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 436c206 commit 62ebc03

1 file changed

Lines changed: 74 additions & 84 deletions

File tree

lightning/src/ln/channel.rs

Lines changed: 74 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6849,24 +6849,13 @@ impl FundingNegotiationContext {
68496849
}
68506850
}
68516851

6852-
fn into_contributed_inputs_and_outputs(self) -> (Vec<bitcoin::OutPoint>, Vec<TxOut>) {
6853-
let contributed_inputs =
6854-
self.our_funding_inputs.into_iter().map(|input| input.utxo.outpoint).collect();
6855-
let contributed_outputs = self.our_funding_outputs;
6856-
(contributed_inputs, contributed_outputs)
6857-
}
6858-
68596852
fn contributed_inputs(&self) -> impl Iterator<Item = bitcoin::OutPoint> + '_ {
68606853
self.our_funding_inputs.iter().map(|input| input.utxo.outpoint)
68616854
}
68626855

68636856
fn contributed_outputs(&self) -> impl Iterator<Item = &TxOut> + '_ {
68646857
self.our_funding_outputs.iter()
68656858
}
6866-
6867-
fn to_contributed_inputs_and_outputs(&self) -> (Vec<bitcoin::OutPoint>, Vec<TxOut>) {
6868-
(self.contributed_inputs().collect(), self.contributed_outputs().cloned().collect())
6869-
}
68706859
}
68716860

68726861
// Holder designates channel data owned for the benefit of the user client.
@@ -7044,48 +7033,29 @@ impl SpliceFundingFailed {
70447033
}
70457034
}
70467035

7047-
macro_rules! maybe_create_splice_funding_failed {
7048-
($funded_channel: expr, $pending_splice: expr, $pending_splice_ref: expr, $get: ident, $contributed_inputs_and_outputs: ident) => {{
7049-
$pending_splice
7050-
.and_then(|pending_splice| pending_splice.funding_negotiation.$get())
7051-
.and_then(|funding_negotiation| {
7052-
let is_initiator = funding_negotiation.is_initiator();
7053-
7054-
let (mut contributed_inputs, mut contributed_outputs) = match funding_negotiation {
7055-
FundingNegotiation::AwaitingAck { context, .. } => {
7056-
context.$contributed_inputs_and_outputs()
7057-
},
7058-
FundingNegotiation::ConstructingTransaction {
7059-
interactive_tx_constructor,
7060-
..
7061-
} => interactive_tx_constructor.$contributed_inputs_and_outputs(),
7062-
FundingNegotiation::AwaitingSignatures { .. } => $funded_channel
7063-
.context
7064-
.interactive_tx_signing_session
7065-
.$get()
7066-
.expect("We have a pending splice awaiting signatures")
7067-
.$contributed_inputs_and_outputs(),
7068-
};
7069-
7070-
if let Some(pending_splice) = $pending_splice_ref {
7071-
for input in pending_splice.prior_contributed_inputs() {
7072-
contributed_inputs.retain(|i| *i != input);
7073-
}
7074-
for output in pending_splice.prior_contributed_outputs() {
7075-
contributed_outputs.retain(|o| o.script_pubkey != output.script_pubkey);
7076-
}
7077-
}
7078-
7079-
if !is_initiator && contributed_inputs.is_empty() && contributed_outputs.is_empty()
7080-
{
7081-
return None;
7082-
}
7083-
7084-
let contribution =
7085-
$pending_splice_ref.and_then(|ps| ps.contributions.last().cloned());
7086-
7087-
Some(SpliceFundingFailed { contributed_inputs, contributed_outputs, contribution })
7088-
})
7036+
macro_rules! splice_funding_failed_for {
7037+
($self: expr, $is_initiator: expr, $contribution: expr,
7038+
$contributed_inputs: ident, $contributed_outputs: ident) => {{
7039+
let contribution = $contribution;
7040+
let existing_inputs =
7041+
$self.pending_splice.as_ref().into_iter().flat_map(|ps| ps.$contributed_inputs());
7042+
let existing_outputs =
7043+
$self.pending_splice.as_ref().into_iter().flat_map(|ps| ps.$contributed_outputs());
7044+
let filtered =
7045+
contribution.clone().into_unique_contributions(existing_inputs, existing_outputs);
7046+
match filtered {
7047+
None if !$is_initiator => None,
7048+
None => Some(SpliceFundingFailed {
7049+
contributed_inputs: vec![],
7050+
contributed_outputs: vec![],
7051+
contribution: Some(contribution),
7052+
}),
7053+
Some((contributed_inputs, contributed_outputs)) => Some(SpliceFundingFailed {
7054+
contributed_inputs,
7055+
contributed_outputs,
7056+
contribution: Some(contribution),
7057+
}),
7058+
}
70897059
}};
70907060
}
70917061

@@ -7115,21 +7085,16 @@ where
71157085
/// Builds a [`SpliceFundingFailed`] from a contribution, filtering out inputs/outputs
71167086
/// that are still committed to a prior splice round.
71177087
fn splice_funding_failed_for(&self, contribution: FundingContribution) -> SpliceFundingFailed {
7118-
let cloned_contribution = contribution.clone();
7119-
let (mut inputs, mut outputs) = contribution.into_contributed_inputs_and_outputs();
7120-
if let Some(ref pending_splice) = self.pending_splice {
7121-
for input in pending_splice.contributed_inputs() {
7122-
inputs.retain(|i| *i != input);
7123-
}
7124-
for output in pending_splice.contributed_outputs() {
7125-
outputs.retain(|o| o.script_pubkey != output.script_pubkey);
7126-
}
7127-
}
7128-
SpliceFundingFailed {
7129-
contributed_inputs: inputs,
7130-
contributed_outputs: outputs,
7131-
contribution: Some(cloned_contribution),
7132-
}
7088+
// The contribution was never pushed to `contributions`, so `contributed_inputs()` and
7089+
// `contributed_outputs()` return only prior rounds' entries for filtering.
7090+
splice_funding_failed_for!(
7091+
self,
7092+
true,
7093+
contribution,
7094+
contributed_inputs,
7095+
contributed_outputs
7096+
)
7097+
.expect("is_initiator is true so this always returns Some")
71337098
}
71347099

71357100
fn quiescent_action_into_error(&self, action: QuiescentAction) -> QuiescentError {
@@ -7273,21 +7238,23 @@ where
72737238
);
72747239
}
72757240

7276-
let splice_funding_failed = maybe_create_splice_funding_failed!(
7277-
self,
7278-
self.pending_splice.as_mut(),
7279-
self.pending_splice.as_ref(),
7280-
take,
7281-
into_contributed_inputs_and_outputs
7282-
);
7283-
7284-
// Pop the current round's contribution, if any (acceptors may not have one). This
7285-
// must happen after `maybe_create_splice_funding_failed` for correct filtering.
7241+
// Take the funding negotiation and pop the current round's contribution, if any
7242+
// (acceptors may not have one).
72867243
let pending_splice = self
72877244
.pending_splice
72887245
.as_mut()
72897246
.expect("reset_pending_splice_state requires pending_splice");
7290-
if let Some(contribution) = pending_splice.contributions.pop() {
7247+
debug_assert!(
7248+
pending_splice.funding_negotiation.is_some(),
7249+
"reset_pending_splice_state requires an active funding negotiation"
7250+
);
7251+
let is_initiator = pending_splice
7252+
.funding_negotiation
7253+
.take()
7254+
.map(|negotiation| negotiation.is_initiator())
7255+
.unwrap_or(false);
7256+
let contribution = pending_splice.contributions.pop();
7257+
if let Some(ref contribution) = contribution {
72917258
debug_assert!(
72927259
pending_splice
72937260
.last_funding_feerate_sat_per_1000_weight
@@ -7297,6 +7264,18 @@ where
72977264
);
72987265
}
72997266

7267+
// After pop, `contributed_inputs()` / `contributed_outputs()` return only prior
7268+
// rounds for filtering.
7269+
let splice_funding_failed = contribution.and_then(|contribution| {
7270+
splice_funding_failed_for!(
7271+
self,
7272+
is_initiator,
7273+
contribution,
7274+
contributed_inputs,
7275+
contributed_outputs
7276+
)
7277+
});
7278+
73007279
if self.pending_funding().is_empty() {
73017280
self.pending_splice.take();
73027281
}
@@ -7314,12 +7293,23 @@ where
73147293
return None;
73157294
}
73167295

7317-
maybe_create_splice_funding_failed!(
7296+
let pending_splice = self.pending_splice.as_ref()?;
7297+
debug_assert!(
7298+
pending_splice.funding_negotiation.is_some(),
7299+
"maybe_splice_funding_failed requires an active funding negotiation"
7300+
);
7301+
let is_initiator = pending_splice
7302+
.funding_negotiation
7303+
.as_ref()
7304+
.map(|negotiation| negotiation.is_initiator())
7305+
.unwrap_or(false);
7306+
let contribution = pending_splice.contributions.last().cloned()?;
7307+
splice_funding_failed_for!(
73187308
self,
7319-
self.pending_splice.as_ref(),
7320-
self.pending_splice.as_ref(),
7321-
as_ref,
7322-
to_contributed_inputs_and_outputs
7309+
is_initiator,
7310+
contribution,
7311+
prior_contributed_inputs,
7312+
prior_contributed_outputs
73237313
)
73247314
}
73257315

0 commit comments

Comments
 (0)