Skip to content

Commit d09cccc

Browse files
jkczyzclaude
andcommitted
Add FundingContribution to SpliceFailed event
Replace the abandoned_funding_txo and channel_type fields on Event::SpliceFailed with an Option<FundingContribution> from the failed round. Users can feed this back to funding_contributed to retry or use it to inform a fresh attempt via splice_channel. Also makes FundingContribution::feerate() public so users can inspect the feerate when deciding whether to retry or bump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3ca4f3d commit d09cccc

6 files changed

Lines changed: 62 additions & 50 deletions

File tree

lightning/src/events/mod.rs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::blinded_path::payment::{
2525
use crate::chain::transaction;
2626
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
2727
use crate::ln::channelmanager::{InterceptId, PaymentId};
28+
use crate::ln::funding::FundingContribution;
2829
use crate::ln::msgs;
2930
use crate::ln::onion_utils::LocalHTLCFailureReason;
3031
use crate::ln::outbound_payment::RecipientOnionFields;
@@ -1621,19 +1622,20 @@ pub enum Event {
16211622
/// The witness script that is used to lock the channel's funding output to commitment transactions.
16221623
new_funding_redeem_script: ScriptBuf,
16231624
},
1624-
/// Used to indicate that a splice for the given `channel_id` has failed.
1625+
/// Used to indicate that a splice negotiation round for the given `channel_id` has failed.
16251626
///
1626-
/// This event may be emitted if a splice fails after it has been initiated but prior to signing
1627-
/// any negotiated funding transaction.
1627+
/// Each splice attempt (initial or RBF) resolves to either [`Event::SplicePending`] on
1628+
/// success or this event on failure. Prior successfully negotiated splice transactions are
1629+
/// unaffected.
16281630
///
1629-
/// Any UTXOs contributed to be spent by the funding transaction may be reused and will be
1630-
/// given in `contributed_inputs`.
1631+
/// Any UTXOs contributed to the failed round that are not committed to a prior negotiated
1632+
/// splice transaction will be returned via a preceding [`Event::DiscardFunding`].
16311633
///
16321634
/// # Failure Behavior and Persistence
16331635
/// This event will eventually be replayed after failures-to-handle (i.e., the event handler
16341636
/// returning `Err(ReplayEvent ())`) and will be persisted across restarts.
16351637
SpliceFailed {
1636-
/// The `channel_id` of the channel for which the splice failed.
1638+
/// The `channel_id` of the channel for which the splice negotiation round failed.
16371639
channel_id: ChannelId,
16381640
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
16391641
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels.
@@ -1643,12 +1645,17 @@ pub enum Event {
16431645
user_channel_id: u128,
16441646
/// The `node_id` of the channel counterparty.
16451647
counterparty_node_id: PublicKey,
1646-
/// The outpoint of the channel's splice funding transaction, if one was created.
1647-
abandoned_funding_txo: Option<OutPoint>,
1648-
/// The features that this channel will operate with, if available.
1649-
channel_type: Option<ChannelTypeFeatures>,
16501648
/// The reason the splice negotiation failed.
16511649
reason: NegotiationFailureReason,
1650+
/// The funding contribution from the failed negotiation round, if available. This can be
1651+
/// fed back to [`ChannelManager::funding_contributed`] to retry with the same parameters.
1652+
/// Alternatively, call [`ChannelManager::splice_channel`] to obtain a fresh
1653+
/// [`FundingTemplate`] and build a new contribution.
1654+
///
1655+
/// [`ChannelManager::funding_contributed`]: crate::ln::channelmanager::ChannelManager::funding_contributed
1656+
/// [`ChannelManager::splice_channel`]: crate::ln::channelmanager::ChannelManager::splice_channel
1657+
/// [`FundingTemplate`]: crate::ln::channelmanager::FundingTemplate
1658+
contribution: Option<FundingContribution>,
16521659
},
16531660
/// Used to indicate to the user that they can abandon the funding transaction and recycle the
16541661
/// inputs for another purpose.
@@ -2440,18 +2447,16 @@ impl Writeable for Event {
24402447
ref channel_id,
24412448
ref user_channel_id,
24422449
ref counterparty_node_id,
2443-
ref abandoned_funding_txo,
2444-
ref channel_type,
24452450
ref reason,
2451+
ref contribution,
24462452
} => {
24472453
52u8.write(writer)?;
24482454
write_tlv_fields!(writer, {
24492455
(1, channel_id, required),
2450-
(3, channel_type, option),
24512456
(5, user_channel_id, required),
24522457
(7, counterparty_node_id, required),
2453-
(9, abandoned_funding_txo, option),
24542458
(11, reason, required),
2459+
(13, contribution, option),
24552460
});
24562461
},
24572462
// Note that, going forward, all new events must only write data inside of
@@ -3092,20 +3097,18 @@ impl MaybeReadable for Event {
30923097
let mut f = || {
30933098
_init_and_read_len_prefixed_tlv_fields!(reader, {
30943099
(1, channel_id, required),
3095-
(3, channel_type, option),
30963100
(5, user_channel_id, required),
30973101
(7, counterparty_node_id, required),
3098-
(9, abandoned_funding_txo, option),
30993102
(11, reason, (default_value, NegotiationFailureReason::Unknown)),
3103+
(13, contribution, option),
31003104
});
31013105

31023106
Ok(Some(Event::SpliceFailed {
31033107
channel_id: channel_id.0.unwrap(),
31043108
user_channel_id: user_channel_id.0.unwrap(),
31053109
counterparty_node_id: counterparty_node_id.0.unwrap(),
3106-
abandoned_funding_txo,
3107-
channel_type,
31083110
reason: reason.0.unwrap(),
3111+
contribution,
31093112
}))
31103113
};
31113114
f()

lightning/src/ln/channel.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6741,6 +6741,9 @@ pub struct SpliceFundingFailed {
67416741

67426742
/// Outputs contributed to the splice transaction.
67436743
pub contributed_outputs: Vec<bitcoin::TxOut>,
6744+
6745+
/// The funding contribution from the failed round, if available.
6746+
pub contribution: Option<FundingContribution>,
67446747
}
67456748

67466749
macro_rules! maybe_create_splice_funding_failed {
@@ -6789,11 +6792,15 @@ macro_rules! maybe_create_splice_funding_failed {
67896792
return None;
67906793
}
67916794

6795+
let contribution =
6796+
$pending_splice_ref.and_then(|ps| ps.contributions.last().cloned());
6797+
67926798
Some(SpliceFundingFailed {
67936799
funding_txo,
67946800
channel_type,
67956801
contributed_inputs,
67966802
contributed_outputs,
6803+
contribution,
67976804
})
67986805
})
67996806
}};
@@ -6825,6 +6832,7 @@ where
68256832
/// Builds a [`SpliceFundingFailed`] from a contribution, filtering out inputs/outputs
68266833
/// that are still committed to a prior splice round.
68276834
fn splice_funding_failed_for(&self, contribution: FundingContribution) -> SpliceFundingFailed {
6835+
let cloned_contribution = contribution.clone();
68286836
let (mut inputs, mut outputs) = contribution.into_contributed_inputs_and_outputs();
68296837
if let Some(ref pending_splice) = self.pending_splice {
68306838
for input in pending_splice.contributed_inputs() {
@@ -6839,6 +6847,7 @@ where
68396847
channel_type: None,
68406848
contributed_inputs: inputs,
68416849
contributed_outputs: outputs,
6850+
contribution: Some(cloned_contribution),
68426851
}
68436852
}
68446853

lightning/src/ln/channelmanager.rs

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4009,8 +4009,7 @@ impl<
40094009
channel_id: *chan_id,
40104010
counterparty_node_id: *counterparty_node_id,
40114011
user_channel_id: chan.context().get_user_id(),
4012-
abandoned_funding_txo: splice_funding_failed.funding_txo,
4013-
channel_type: splice_funding_failed.channel_type,
4012+
contribution: splice_funding_failed.contribution,
40144013
reason: events::NegotiationFailureReason::ChannelClosing,
40154014
},
40164015
None,
@@ -4316,8 +4315,7 @@ impl<
43164315
channel_id: shutdown_res.channel_id,
43174316
counterparty_node_id: shutdown_res.counterparty_node_id,
43184317
user_channel_id: shutdown_res.user_channel_id,
4319-
abandoned_funding_txo: splice_funding_failed.funding_txo,
4320-
channel_type: splice_funding_failed.channel_type,
4318+
contribution: splice_funding_failed.contribution,
43214319
reason: events::NegotiationFailureReason::ChannelClosing,
43224320
},
43234321
None,
@@ -4823,8 +4821,7 @@ impl<
48234821
channel_id: *channel_id,
48244822
counterparty_node_id: *counterparty_node_id,
48254823
user_channel_id: chan.context.get_user_id(),
4826-
abandoned_funding_txo: splice_funding_failed.funding_txo,
4827-
channel_type: splice_funding_failed.channel_type,
4824+
contribution: splice_funding_failed.contribution,
48284825
reason: events::NegotiationFailureReason::LocallyAbandoned,
48294826
},
48304827
None,
@@ -6510,10 +6507,7 @@ impl<
65106507
},
65116508
QuiescentError::FailSplice(
65126509
SpliceFundingFailed {
6513-
funding_txo,
6514-
channel_type,
6515-
contributed_inputs,
6516-
contributed_outputs,
6510+
contributed_inputs, contributed_outputs, contribution, ..
65176511
},
65186512
reason,
65196513
) => {
@@ -6523,9 +6517,8 @@ impl<
65236517
channel_id,
65246518
counterparty_node_id,
65256519
user_channel_id,
6526-
abandoned_funding_txo: funding_txo,
6527-
channel_type,
65286520
reason,
6521+
contribution,
65296522
},
65306523
None,
65316524
));
@@ -11713,8 +11706,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1171311706
channel_id,
1171411707
counterparty_node_id: *counterparty_node_id,
1171511708
user_channel_id: channel.context().get_user_id(),
11716-
abandoned_funding_txo: splice_funding_failed.funding_txo,
11717-
channel_type: splice_funding_failed.channel_type.clone(),
11709+
contribution: splice_funding_failed.contribution.clone(),
1171811710
reason: events::NegotiationFailureReason::NegotiationError,
1171911711
},
1172011712
None,
@@ -11873,8 +11865,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1187311865
channel_id: msg.channel_id,
1187411866
counterparty_node_id,
1187511867
user_channel_id: chan.context().get_user_id(),
11876-
abandoned_funding_txo: splice_funding_failed.funding_txo,
11877-
channel_type: splice_funding_failed.channel_type.clone(),
11868+
contribution: splice_funding_failed.contribution.clone(),
1187811869
reason: events::NegotiationFailureReason::NegotiationError,
1187911870
},
1188011871
None,
@@ -12044,8 +12035,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1204412035
channel_id: msg.channel_id,
1204512036
counterparty_node_id: *counterparty_node_id,
1204612037
user_channel_id: chan_entry.get().context().get_user_id(),
12047-
abandoned_funding_txo: splice_funding_failed.funding_txo,
12048-
channel_type: splice_funding_failed.channel_type,
12038+
contribution: splice_funding_failed.contribution,
1204912039
reason: events::NegotiationFailureReason::CounterpartyAborted,
1205012040
},
1205112041
None,
@@ -12193,8 +12183,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1219312183
channel_id: msg.channel_id,
1219412184
counterparty_node_id: *counterparty_node_id,
1219512185
user_channel_id: chan.context().get_user_id(),
12196-
abandoned_funding_txo: splice_funding_failed.funding_txo,
12197-
channel_type: splice_funding_failed.channel_type,
12186+
contribution: splice_funding_failed.contribution,
1219812187
reason: events::NegotiationFailureReason::ChannelClosing,
1219912188
},
1220012189
None,
@@ -15302,8 +15291,7 @@ impl<
1530215291
channel_id: chan.context().channel_id(),
1530315292
counterparty_node_id,
1530415293
user_channel_id: chan.context().get_user_id(),
15305-
abandoned_funding_txo: splice_funding_failed.funding_txo,
15306-
channel_type: splice_funding_failed.channel_type,
15294+
contribution: splice_funding_failed.contribution,
1530715295
reason: events::NegotiationFailureReason::PeerDisconnected,
1530815296
});
1530915297
splice_failed_events.push(events::Event::DiscardFunding {
@@ -17953,9 +17941,8 @@ impl<
1795317941
channel_id: chan.context.channel_id(),
1795417942
counterparty_node_id: chan.context.get_counterparty_node_id(),
1795517943
user_channel_id: chan.context.get_user_id(),
17956-
abandoned_funding_txo: splice_funding_failed.funding_txo,
17957-
channel_type: splice_funding_failed.channel_type,
1795817944
reason: events::NegotiationFailureReason::PeerDisconnected,
17945+
contribution: splice_funding_failed.contribution,
1795917946
},
1796017947
None,
1796117948
));

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3243,9 +3243,10 @@ pub fn expect_splice_failed_events<'a, 'b, 'c, 'd>(
32433243
let events = node.node.get_and_clear_pending_events();
32443244
assert_eq!(events.len(), 2);
32453245
match &events[0] {
3246-
Event::SpliceFailed { channel_id, reason, .. } => {
3246+
Event::SpliceFailed { channel_id, reason, contribution, .. } => {
32473247
assert_eq!(*expected_channel_id, *channel_id);
32483248
assert_eq!(*reason, expected_reason);
3249+
assert_eq!(contribution.as_ref(), Some(&funding_contribution));
32493250
},
32503251
_ => panic!("Unexpected event"),
32513252
}

lightning/src/ln/funding.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,8 @@ impl_writeable_tlv_based!(FundingContribution, {
708708
});
709709

710710
impl FundingContribution {
711-
pub(super) fn feerate(&self) -> FeeRate {
711+
/// Returns the feerate of this contribution.
712+
pub fn feerate(&self) -> FeeRate {
712713
self.feerate
713714
}
714715

@@ -729,6 +730,11 @@ impl FundingContribution {
729730
self.value_added
730731
}
731732

733+
/// Returns the inputs included in this contribution.
734+
pub fn inputs(&self) -> &[FundingTxInput] {
735+
&self.inputs
736+
}
737+
732738
/// Returns the outputs (e.g., withdrawal destinations) included in this contribution.
733739
///
734740
/// This does not include the change output; see [`FundingContribution::change_output`].

lightning/src/ln/splicing_tests.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3056,9 +3056,10 @@ fn do_abandon_splice_quiescent_action_on_shutdown(local_shutdown: bool, pending_
30563056
let events = nodes[0].node.get_and_clear_pending_events();
30573057
assert_eq!(events.len(), 2, "{events:?}");
30583058
match &events[0] {
3059-
Event::SpliceFailed { channel_id: cid, reason, .. } => {
3059+
Event::SpliceFailed { channel_id: cid, reason, contribution, .. } => {
30603060
assert_eq!(*cid, channel_id);
30613061
assert_eq!(*reason, NegotiationFailureReason::ChannelClosing);
3062+
assert!(contribution.is_some());
30623063
},
30633064
other => panic!("Expected SpliceFailed, got {:?}", other),
30643065
}
@@ -4338,9 +4339,10 @@ fn test_splice_acceptor_disconnect_emits_events() {
43384339
let events = nodes[1].node.get_and_clear_pending_events();
43394340
assert_eq!(events.len(), 2, "{events:?}");
43404341
match &events[0] {
4341-
Event::SpliceFailed { channel_id: cid, reason, .. } => {
4342+
Event::SpliceFailed { channel_id: cid, reason, contribution, .. } => {
43424343
assert_eq!(*cid, channel_id);
43434344
assert_eq!(*reason, NegotiationFailureReason::PeerDisconnected);
4345+
assert!(contribution.is_some());
43444346
},
43454347
other => panic!("Expected SpliceFailed, got {:?}", other),
43464348
}
@@ -5715,9 +5717,10 @@ fn test_splice_rbf_acceptor_contributes_then_disconnects() {
57155717
let events = nodes[0].node.get_and_clear_pending_events();
57165718
assert_eq!(events.len(), 2, "{events:?}");
57175719
match &events[0] {
5718-
Event::SpliceFailed { channel_id: cid, reason, .. } => {
5720+
Event::SpliceFailed { channel_id: cid, reason, contribution, .. } => {
57195721
assert_eq!(*cid, channel_id);
57205722
assert_eq!(*reason, NegotiationFailureReason::PeerDisconnected);
5723+
assert!(contribution.is_some());
57215724
},
57225725
other => panic!("Expected SpliceFailed, got {:?}", other),
57235726
}
@@ -5797,9 +5800,10 @@ fn test_splice_rbf_disconnect_filters_prior_contributions() {
57975800
let events = nodes[0].node.get_and_clear_pending_events();
57985801
assert_eq!(events.len(), 2, "{events:?}");
57995802
match &events[0] {
5800-
Event::SpliceFailed { channel_id: cid, reason, .. } => {
5803+
Event::SpliceFailed { channel_id: cid, reason, contribution, .. } => {
58015804
assert_eq!(*cid, channel_id);
58025805
assert_eq!(*reason, NegotiationFailureReason::PeerDisconnected);
5806+
assert!(contribution.is_some());
58035807
},
58045808
other => panic!("Expected SpliceFailed, got {:?}", other),
58055809
}
@@ -5839,9 +5843,10 @@ fn test_splice_rbf_disconnect_filters_prior_contributions() {
58395843
let events = nodes[0].node.get_and_clear_pending_events();
58405844
assert_eq!(events.len(), 2, "{events:?}");
58415845
match &events[0] {
5842-
Event::SpliceFailed { channel_id: cid, reason, .. } => {
5846+
Event::SpliceFailed { channel_id: cid, reason, contribution, .. } => {
58435847
assert_eq!(*cid, channel_id);
58445848
assert_eq!(*reason, NegotiationFailureReason::PeerDisconnected);
5849+
assert!(contribution.is_some());
58455850
},
58465851
other => panic!("Expected SpliceFailed, got {:?}", other),
58475852
}
@@ -6530,9 +6535,10 @@ fn test_splice_rbf_rejects_own_low_feerate_after_several_attempts() {
65306535
let events = nodes[0].node.get_and_clear_pending_events();
65316536
assert_eq!(events.len(), 1, "{events:?}");
65326537
match &events[0] {
6533-
Event::SpliceFailed { channel_id: cid, reason, .. } => {
6538+
Event::SpliceFailed { channel_id: cid, reason, contribution, .. } => {
65346539
assert_eq!(*cid, channel_id);
65356540
assert_eq!(*reason, NegotiationFailureReason::FeeRateTooLow);
6541+
assert!(contribution.is_some());
65366542
},
65376543
other => panic!("Expected SpliceFailed, got {:?}", other),
65386544
}

0 commit comments

Comments
 (0)