Skip to content

Commit 22b11c0

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 e09137b commit 22b11c0

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
@@ -1908,7 +1908,7 @@ where
19081908

19091909
pub fn tx_abort<L: Deref>(
19101910
&mut self, msg: &msgs::TxAbort, logger: &L,
1911-
) -> Result<Option<msgs::TxAbort>, ChannelError>
1911+
) -> Result<Option<(msgs::TxAbort, SpliceFundingFailed)>, ChannelError>
19121912
where
19131913
L::Target: Logger,
19141914
{
@@ -1917,20 +1917,49 @@ where
19171917
// phase for this interactively constructed transaction and hence we have not exchanged
19181918
// `tx_signatures`. Either way, we never close the channel upon receiving a `tx_abort`:
19191919
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L574-L576
1920-
let should_ack = match &mut self.phase {
1920+
let (should_ack, splice_funding_failed) = match &mut self.phase {
19211921
ChannelPhase::Undefined => unreachable!(),
19221922
ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => {
19231923
let err = "Got an unexpected tx_abort message: This is an unfunded channel created with V1 channel establishment";
19241924
return Err(ChannelError::Warn(err.into()));
19251925
},
19261926
ChannelPhase::UnfundedV2(pending_v2_channel) => {
1927-
pending_v2_channel.interactive_tx_constructor.take().is_some()
1927+
let had_constructor = pending_v2_channel.interactive_tx_constructor.take().is_some();
1928+
(had_constructor, None)
1929+
},
1930+
ChannelPhase::Funded(funded_channel) => {
1931+
let funding_negotiation_opt = funded_channel
1932+
.pending_splice
1933+
.as_mut()
1934+
.and_then(|pending_splice| pending_splice.funding_negotiation.take());
1935+
1936+
let should_ack = funding_negotiation_opt.is_some();
1937+
let splice_funding_failed = funding_negotiation_opt.map(|funding_negotiation| {
1938+
// Create SpliceFundingFailed for the aborted splice
1939+
let (funding_txo, channel_type) = match &funding_negotiation {
1940+
FundingNegotiation::ConstructingTransaction { funding, .. } => {
1941+
(funding.get_funding_txo().map(|txo| txo.into_bitcoin_outpoint()), Some(funding.get_channel_type().clone()))
1942+
},
1943+
FundingNegotiation::AwaitingSignatures { funding } => {
1944+
(funding.get_funding_txo().map(|txo| txo.into_bitcoin_outpoint()), Some(funding.get_channel_type().clone()))
1945+
},
1946+
FundingNegotiation::AwaitingAck { .. } => {
1947+
(None, None)
1948+
},
1949+
};
1950+
1951+
SpliceFundingFailed {
1952+
channel_id: funded_channel.context.channel_id,
1953+
counterparty_node_id: funded_channel.context.counterparty_node_id,
1954+
user_channel_id: funded_channel.context.user_id,
1955+
funding_txo,
1956+
channel_type,
1957+
contributed_inputs: Vec::new(),
1958+
contributed_outputs: Vec::new(),
1959+
}
1960+
});
1961+
(should_ack, splice_funding_failed)
19281962
},
1929-
ChannelPhase::Funded(funded_channel) => funded_channel
1930-
.pending_splice
1931-
.as_mut()
1932-
.and_then(|pending_splice| pending_splice.funding_negotiation.take())
1933-
.is_some(),
19341963
};
19351964

19361965
// NOTE: Since at this point we have not sent a `tx_abort` message for this negotiation
@@ -1939,16 +1968,20 @@ where
19391968
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L560-L561
19401969
// For rationale why we echo back `tx_abort`:
19411970
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L578-L580
1942-
Ok(should_ack.then(|| {
1971+
let result = if should_ack {
19431972
let logger = WithChannelContext::from(logger, &self.context(), None);
19441973
let reason =
19451974
types::string::UntrustedString(String::from_utf8_lossy(&msg.data).to_string());
19461975
log_info!(logger, "Counterparty failed interactive transaction negotiation: {reason}");
1947-
msgs::TxAbort {
1976+
let tx_abort_response = msgs::TxAbort {
19481977
channel_id: msg.channel_id,
19491978
data: "Acknowledged tx_abort".to_string().into_bytes(),
1950-
}
1951-
}))
1979+
};
1980+
splice_funding_failed.map(|failed| (tx_abort_response, failed))
1981+
} else {
1982+
None
1983+
};
1984+
Ok(result)
19521985
}
19531986

19541987
#[rustfmt::skip]

lightning/src/ln/channelmanager.rs

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

0 commit comments

Comments
 (0)