Skip to content

Commit dea4e85

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 be56077 commit dea4e85

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
@@ -1883,7 +1883,7 @@ where
18831883

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

19111940
// NOTE: Since at this point we have not sent a `tx_abort` message for this negotiation
@@ -1914,16 +1943,20 @@ where
19141943
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L560-L561
19151944
// For rationale why we echo back `tx_abort`:
19161945
// https://github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L578-L580
1917-
Ok(should_ack.then(|| {
1946+
let result = if should_ack {
19181947
let logger = WithChannelContext::from(logger, &self.context(), None);
19191948
let reason =
19201949
types::string::UntrustedString(String::from_utf8_lossy(&msg.data).to_string());
19211950
log_info!(logger, "Counterparty failed interactive transaction negotiation: {reason}");
1922-
msgs::TxAbort {
1951+
let tx_abort_response = msgs::TxAbort {
19231952
channel_id: msg.channel_id,
19241953
data: "Acknowledged tx_abort".to_string().into_bytes(),
1925-
}
1926-
}))
1954+
};
1955+
splice_funding_failed.map(|failed| (tx_abort_response, failed))
1956+
} else {
1957+
None
1958+
};
1959+
Ok(result)
19271960
}
19281961

19291962
#[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)