Skip to content

Commit a5d670c

Browse files
jkczyzclaude
andcommitted
Return InteractiveTxMsgError from splice_init and tx_init_rbf
The prior two commits manually intercepted ChannelError::Abort in the channelmanager handlers for splice_init and tx_init_rbf to exit quiescence before returning, since the channel methods didn't signal this themselves. The interactive TX message handlers already solved this by returning InteractiveTxMsgError which bundles exited_quiescence into the error type. Apply the same pattern: change splice_init and tx_init_rbf to return InteractiveTxMsgError, adding a quiescent_negotiation_err helper on FundedChannel that exits quiescence for Abort errors and passes through other variants unchanged. Extract handle_interactive_tx_msg_err in channelmanager to deduplicate the error handling across internal_tx_msg, internal_splice_init, and internal_tx_init_rbf. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f545601 commit a5d670c

File tree

2 files changed

+105
-78
lines changed

2 files changed

+105
-78
lines changed

lightning/src/ln/channel.rs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12524,13 +12524,15 @@ where
1252412524
pub(crate) fn splice_init<ES: EntropySource, L: Logger>(
1252512525
&mut self, msg: &msgs::SpliceInit, entropy_source: &ES, holder_node_id: &PublicKey,
1252612526
logger: &L,
12527-
) -> Result<msgs::SpliceAck, ChannelError> {
12527+
) -> Result<msgs::SpliceAck, InteractiveTxMsgError> {
1252812528
let feerate = FeeRate::from_sat_per_kwu(msg.funding_feerate_per_kw as u64);
12529-
let (our_funding_contribution, holder_balance) =
12530-
self.resolve_queued_contribution(feerate, logger)?;
12529+
let (our_funding_contribution, holder_balance) = self
12530+
.resolve_queued_contribution(feerate, logger)
12531+
.map_err(|e| self.quiescent_negotiation_err(e))?;
1253112532

12532-
let splice_funding =
12533-
self.validate_splice_init(msg, our_funding_contribution.unwrap_or(SignedAmount::ZERO))?;
12533+
let splice_funding = self
12534+
.validate_splice_init(msg, our_funding_contribution.unwrap_or(SignedAmount::ZERO))
12535+
.map_err(|e| self.quiescent_negotiation_err(e))?;
1253412536

1253512537
// Adjust for the feerate and clone so we can store it for future RBF re-use.
1253612538
let (adjusted_contribution, our_funding_inputs, our_funding_outputs) =
@@ -12678,10 +12680,11 @@ where
1267812680
pub(crate) fn tx_init_rbf<ES: EntropySource, F: FeeEstimator, L: Logger>(
1267912681
&mut self, msg: &msgs::TxInitRbf, entropy_source: &ES, holder_node_id: &PublicKey,
1268012682
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
12681-
) -> Result<msgs::TxAckRbf, ChannelError> {
12683+
) -> Result<msgs::TxAckRbf, InteractiveTxMsgError> {
1268212684
let feerate = FeeRate::from_sat_per_kwu(msg.feerate_sat_per_1000_weight as u64);
12683-
let (queued_net_value, holder_balance) =
12684-
self.resolve_queued_contribution(feerate, logger)?;
12685+
let (queued_net_value, holder_balance) = self
12686+
.resolve_queued_contribution(feerate, logger)
12687+
.map_err(|e| self.quiescent_negotiation_err(e))?;
1268512688

1268612689
// If no queued contribution, try prior contribution from previous negotiation.
1268712690
// Failing here means the RBF would erase our splice — reject it.
@@ -12698,19 +12701,22 @@ where
1269812701
prior
1269912702
.net_value_for_acceptor_at_feerate(feerate, holder_balance)
1270012703
.map_err(|_| ChannelError::Abort(AbortReason::InsufficientRbfFeerate))
12701-
})?;
12704+
})
12705+
.map_err(|e| self.quiescent_negotiation_err(e))?;
1270212706
Some(net_value)
1270312707
} else {
1270412708
None
1270512709
};
1270612710

1270712711
let our_funding_contribution = queued_net_value.or(prior_net_value);
1270812712

12709-
let rbf_funding = self.validate_tx_init_rbf(
12710-
msg,
12711-
our_funding_contribution.unwrap_or(SignedAmount::ZERO),
12712-
fee_estimator,
12713-
)?;
12713+
let rbf_funding = self
12714+
.validate_tx_init_rbf(
12715+
msg,
12716+
our_funding_contribution.unwrap_or(SignedAmount::ZERO),
12717+
fee_estimator,
12718+
)
12719+
.map_err(|e| self.quiescent_negotiation_err(e))?;
1271412720

1271512721
// Consume the appropriate contribution source.
1271612722
let (our_funding_inputs, our_funding_outputs) = if queued_net_value.is_some() {
@@ -13863,6 +13869,12 @@ where
1386313869
was_quiescent
1386413870
}
1386513871

13872+
fn quiescent_negotiation_err(&mut self, err: ChannelError) -> InteractiveTxMsgError {
13873+
let exited_quiescence =
13874+
if matches!(err, ChannelError::Abort(_)) { self.exit_quiescence() } else { false };
13875+
InteractiveTxMsgError { err, splice_funding_failed: None, exited_quiescence }
13876+
}
13877+
1386613878
pub fn remove_legacy_scids_before_block(&mut self, height: u32) -> alloc::vec::Drain<'_, u64> {
1386713879
let end = self
1386813880
.funding

lightning/src/ln/channelmanager.rs

Lines changed: 79 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -11650,6 +11650,39 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1165011650
}
1165111651
}
1165211652

11653+
fn handle_interactive_tx_msg_err(
11654+
&self, err: InteractiveTxMsgError, channel_id: ChannelId, counterparty_node_id: &PublicKey,
11655+
user_channel_id: u128,
11656+
) -> MsgHandleErrInternal {
11657+
if let Some(splice_funding_failed) = err.splice_funding_failed {
11658+
let pending_events = &mut self.pending_events.lock().unwrap();
11659+
pending_events.push_back((
11660+
events::Event::SpliceFailed {
11661+
channel_id,
11662+
counterparty_node_id: *counterparty_node_id,
11663+
user_channel_id,
11664+
abandoned_funding_txo: splice_funding_failed.funding_txo,
11665+
channel_type: splice_funding_failed.channel_type.clone(),
11666+
},
11667+
None,
11668+
));
11669+
pending_events.push_back((
11670+
events::Event::DiscardFunding {
11671+
channel_id,
11672+
funding_info: FundingInfo::Contribution {
11673+
inputs: splice_funding_failed.contributed_inputs,
11674+
outputs: splice_funding_failed.contributed_outputs,
11675+
},
11676+
},
11677+
None,
11678+
));
11679+
}
11680+
debug_assert!(!err.exited_quiescence || matches!(err.err, ChannelError::Abort(_)));
11681+
11682+
MsgHandleErrInternal::from_chan_no_close(err.err, channel_id)
11683+
.with_exited_quiescence(err.exited_quiescence)
11684+
}
11685+
1165311686
fn internal_tx_msg<
1165411687
HandleTxMsgFn: Fn(&mut Channel<SP>) -> Result<InteractiveTxMessageSend, InteractiveTxMsgError>,
1165511688
>(
@@ -11671,38 +11704,14 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1167111704
peer_state.pending_msg_events.push(msg_send_event);
1167211705
Ok(())
1167311706
},
11674-
Err(InteractiveTxMsgError {
11675-
err,
11676-
splice_funding_failed,
11677-
exited_quiescence,
11678-
}) => {
11679-
if let Some(splice_funding_failed) = splice_funding_failed {
11680-
let pending_events = &mut self.pending_events.lock().unwrap();
11681-
pending_events.push_back((
11682-
events::Event::SpliceFailed {
11683-
channel_id,
11684-
counterparty_node_id: *counterparty_node_id,
11685-
user_channel_id: channel.context().get_user_id(),
11686-
abandoned_funding_txo: splice_funding_failed.funding_txo,
11687-
channel_type: splice_funding_failed.channel_type.clone(),
11688-
},
11689-
None,
11690-
));
11691-
pending_events.push_back((
11692-
events::Event::DiscardFunding {
11693-
channel_id,
11694-
funding_info: FundingInfo::Contribution {
11695-
inputs: splice_funding_failed.contributed_inputs,
11696-
outputs: splice_funding_failed.contributed_outputs,
11697-
},
11698-
},
11699-
None,
11700-
));
11701-
}
11702-
debug_assert!(!exited_quiescence || matches!(err, ChannelError::Abort(_)));
11703-
11704-
Err(MsgHandleErrInternal::from_chan_no_close(err, channel_id)
11705-
.with_exited_quiescence(exited_quiescence))
11707+
Err(err) => {
11708+
let user_channel_id = channel.context().get_user_id();
11709+
Err(self.handle_interactive_tx_msg_err(
11710+
err,
11711+
channel_id,
11712+
counterparty_node_id,
11713+
user_channel_id,
11714+
))
1170611715
},
1170711716
}
1170811717
},
@@ -13067,27 +13076,30 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1306713076
}
1306813077

1306913078
if let Some(ref mut funded_channel) = chan_entry.get_mut().as_funded_mut() {
13070-
let init_res = funded_channel.splice_init(
13079+
let user_channel_id = funded_channel.context.get_user_id();
13080+
match funded_channel.splice_init(
1307113081
msg,
1307213082
&self.entropy_source,
1307313083
&self.get_our_node_id(),
1307413084
&self.logger,
13075-
);
13076-
if let Err(ChannelError::Abort(_)) = &init_res {
13077-
funded_channel.exit_quiescence();
13078-
let chan_id = funded_channel.context.channel_id();
13079-
let res = MsgHandleErrInternal::from_chan_no_close(
13080-
init_res.unwrap_err(),
13081-
chan_id,
13082-
);
13083-
return Err(res.with_exited_quiescence(true));
13085+
) {
13086+
Ok(splice_ack_msg) => {
13087+
peer_state.pending_msg_events.push(MessageSendEvent::SendSpliceAck {
13088+
node_id: *counterparty_node_id,
13089+
msg: splice_ack_msg,
13090+
});
13091+
Ok(())
13092+
},
13093+
Err(err) => {
13094+
debug_assert!(err.splice_funding_failed.is_none());
13095+
Err(self.handle_interactive_tx_msg_err(
13096+
err,
13097+
msg.channel_id,
13098+
counterparty_node_id,
13099+
user_channel_id,
13100+
))
13101+
},
1308413102
}
13085-
let splice_ack_msg = try_channel_entry!(self, peer_state, init_res, chan_entry);
13086-
peer_state.pending_msg_events.push(MessageSendEvent::SendSpliceAck {
13087-
node_id: *counterparty_node_id,
13088-
msg: splice_ack_msg,
13089-
});
13090-
Ok(())
1309113103
} else {
1309213104
try_channel_entry!(
1309313105
self,
@@ -13120,28 +13132,31 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1312013132
},
1312113133
hash_map::Entry::Occupied(mut chan_entry) => {
1312213134
if let Some(ref mut funded_channel) = chan_entry.get_mut().as_funded_mut() {
13123-
let init_res = funded_channel.tx_init_rbf(
13135+
let user_channel_id = funded_channel.context.get_user_id();
13136+
match funded_channel.tx_init_rbf(
1312413137
msg,
1312513138
&self.entropy_source,
1312613139
&self.get_our_node_id(),
1312713140
&self.fee_estimator,
1312813141
&self.logger,
13129-
);
13130-
if let Err(ChannelError::Abort(_)) = &init_res {
13131-
funded_channel.exit_quiescence();
13132-
let chan_id = funded_channel.context.channel_id();
13133-
let res = MsgHandleErrInternal::from_chan_no_close(
13134-
init_res.unwrap_err(),
13135-
chan_id,
13136-
);
13137-
return Err(res.with_exited_quiescence(true));
13142+
) {
13143+
Ok(tx_ack_rbf_msg) => {
13144+
peer_state.pending_msg_events.push(MessageSendEvent::SendTxAckRbf {
13145+
node_id: *counterparty_node_id,
13146+
msg: tx_ack_rbf_msg,
13147+
});
13148+
Ok(())
13149+
},
13150+
Err(err) => {
13151+
debug_assert!(err.splice_funding_failed.is_none());
13152+
Err(self.handle_interactive_tx_msg_err(
13153+
err,
13154+
msg.channel_id,
13155+
counterparty_node_id,
13156+
user_channel_id,
13157+
))
13158+
},
1313813159
}
13139-
let tx_ack_rbf_msg = try_channel_entry!(self, peer_state, init_res, chan_entry);
13140-
peer_state.pending_msg_events.push(MessageSendEvent::SendTxAckRbf {
13141-
node_id: *counterparty_node_id,
13142-
msg: tx_ack_rbf_msg,
13143-
});
13144-
Ok(())
1314513160
} else {
1314613161
try_channel_entry!(
1314713162
self,

0 commit comments

Comments
 (0)