Skip to content

Commit 10aeb60

Browse files
jkczyzclaude
andcommitted
Emit SpliceFailed events when tx_abort is received for funded channels
When a tx_abort message is successfully processed for a funded channel with an active splice negotiation, emit Event::SpliceFailed to notify users that the splice operation was aborted by the counterparty. This extends the SpliceFailed event coverage to handle abort scenarios, providing comprehensive splice failure notifications across all stages: - AwaitingAck: funding_txo and channel_type are None since funding parameters were not yet established - ConstructingTransaction/AwaitingSignatures: Include actual funding information since negotiation had progressed to funding establishment The implementation captures splice context before taking the funding negotiation state, ensuring accurate failure information is available for event emission while maintaining proper tx_abort acknowledgment behavior per the Lightning specification. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 3f0cace commit 10aeb60

File tree

2 files changed

+61
-14
lines changed

2 files changed

+61
-14
lines changed

lightning/src/ln/channel.rs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,7 +1887,7 @@ where
18871887

18881888
pub fn tx_abort<L: Deref>(
18891889
&mut self, msg: &msgs::TxAbort, logger: &L,
1890-
) -> Result<Option<msgs::TxAbort>, ChannelError>
1890+
) -> Result<Option<(msgs::TxAbort, SpliceFundingFailed)>, ChannelError>
18911891
where
18921892
L::Target: Logger,
18931893
{
@@ -1896,20 +1896,49 @@ where
18961896
// phase for this interactively constructed transaction and hence we have not exchanged
18971897
// `tx_signatures`. Either way, we never close the channel upon receiving a `tx_abort`:
18981898
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L574-L576
1899-
let should_ack = match &mut self.phase {
1899+
let (should_ack, splice_funding_failed) = match &mut self.phase {
19001900
ChannelPhase::Undefined => unreachable!(),
19011901
ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => {
19021902
let err = "Got an unexpected tx_abort message: This is an unfunded channel created with V1 channel establishment";
19031903
return Err(ChannelError::Warn(err.into()));
19041904
},
19051905
ChannelPhase::UnfundedV2(pending_v2_channel) => {
1906-
pending_v2_channel.interactive_tx_constructor.take().is_some()
1906+
let had_constructor = pending_v2_channel.interactive_tx_constructor.take().is_some();
1907+
(had_constructor, None)
1908+
},
1909+
ChannelPhase::Funded(funded_channel) => {
1910+
let funding_negotiation_opt = funded_channel
1911+
.pending_splice
1912+
.as_mut()
1913+
.and_then(|pending_splice| pending_splice.funding_negotiation.take());
1914+
1915+
let should_ack = funding_negotiation_opt.is_some();
1916+
let splice_funding_failed = funding_negotiation_opt.map(|funding_negotiation| {
1917+
// Create SpliceFundingFailed for the aborted splice
1918+
let (funding_txo, channel_type) = match &funding_negotiation {
1919+
FundingNegotiation::ConstructingTransaction { funding, .. } => {
1920+
(funding.get_funding_txo().map(|txo| txo.into_bitcoin_outpoint()), Some(funding.get_channel_type().clone()))
1921+
},
1922+
FundingNegotiation::AwaitingSignatures { funding } => {
1923+
(funding.get_funding_txo().map(|txo| txo.into_bitcoin_outpoint()), Some(funding.get_channel_type().clone()))
1924+
},
1925+
FundingNegotiation::AwaitingAck { .. } => {
1926+
(None, None)
1927+
},
1928+
};
1929+
1930+
SpliceFundingFailed {
1931+
channel_id: funded_channel.context.channel_id,
1932+
counterparty_node_id: funded_channel.context.counterparty_node_id,
1933+
user_channel_id: funded_channel.context.user_id,
1934+
funding_txo,
1935+
channel_type,
1936+
contributed_inputs: Vec::new(),
1937+
contributed_outputs: Vec::new(),
1938+
}
1939+
});
1940+
(should_ack, splice_funding_failed)
19071941
},
1908-
ChannelPhase::Funded(funded_channel) => funded_channel
1909-
.pending_splice
1910-
.as_mut()
1911-
.and_then(|pending_splice| pending_splice.funding_negotiation.take())
1912-
.is_some(),
19131942
};
19141943

19151944
// NOTE: Since at this point we have not sent a `tx_abort` message for this negotiation
@@ -1918,16 +1947,20 @@ where
19181947
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L560-L561
19191948
// For rationale why we echo back `tx_abort`:
19201949
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L578-L580
1921-
Ok(should_ack.then(|| {
1950+
let result = if should_ack {
19221951
let logger = WithChannelContext::from(logger, &self.context(), None);
19231952
let reason =
19241953
types::string::UntrustedString(String::from_utf8_lossy(&msg.data).to_string());
19251954
log_info!(logger, "Counterparty failed interactive transaction negotiation: {reason}");
1926-
msgs::TxAbort {
1955+
let tx_abort_response = msgs::TxAbort {
19271956
channel_id: msg.channel_id,
19281957
data: "Acknowledged tx_abort".to_string().into_bytes(),
1929-
}
1930-
}))
1958+
};
1959+
splice_funding_failed.map(|failed| (tx_abort_response, failed))
1960+
} else {
1961+
None
1962+
};
1963+
Ok(result)
19311964
}
19321965

19331966
#[rustfmt::skip]

lightning/src/ln/channelmanager.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10220,10 +10220,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1022010220
match peer_state.channel_by_id.entry(msg.channel_id) {
1022110221
hash_map::Entry::Occupied(mut chan_entry) => {
1022210222
let res = chan_entry.get_mut().tx_abort(msg, &self.logger);
10223-
if let Some(msg) = try_channel_entry!(self, peer_state, res, chan_entry) {
10223+
let tx_abort_and_splice_failed = try_channel_entry!(self, peer_state, res, chan_entry);
10224+
10225+
// Emit SpliceFailed event and send TxAbort response if we had an active splice negotiation
10226+
if let Some((tx_abort_msg, splice_funding_failed)) = tx_abort_and_splice_failed {
10227+
let pending_events = &mut self.pending_events.lock().unwrap();
10228+
pending_events.push_back((events::Event::SpliceFailed {
10229+
channel_id: splice_funding_failed.channel_id,
10230+
counterparty_node_id: splice_funding_failed.counterparty_node_id,
10231+
user_channel_id: splice_funding_failed.user_channel_id,
10232+
funding_txo: splice_funding_failed.funding_txo,
10233+
channel_type: splice_funding_failed.channel_type,
10234+
contributed_inputs: splice_funding_failed.contributed_inputs,
10235+
contributed_outputs: splice_funding_failed.contributed_outputs,
10236+
}, None));
10237+
1022410238
peer_state.pending_msg_events.push(MessageSendEvent::SendTxAbort {
1022510239
node_id: *counterparty_node_id,
10226-
msg,
10240+
msg: tx_abort_msg,
1022710241
});
1022810242
}
1022910243
Ok(())

0 commit comments

Comments
 (0)