Skip to content

Commit ad60f71

Browse files
committed
ln: surface LocalHTLCFailureReason in queue_add_htlc
1 parent 751c693 commit ad60f71

File tree

3 files changed

+56
-42
lines changed

3 files changed

+56
-42
lines changed

lightning/src/ln/channel.rs

+23-31
Original file line numberDiff line numberDiff line change
@@ -5852,22 +5852,14 @@ impl<SP: Deref> FundedChannel<SP> where
58525852
);
58535853
update_add_count += 1;
58545854
},
5855-
Err(e) => {
5856-
match e {
5857-
ChannelError::Ignore(ref msg) => {
5858-
log_info!(logger, "Failed to send HTLC with payment_hash {} due to {} in channel {}", &payment_hash, msg, &self.context.channel_id());
5859-
// If we fail to send here, then this HTLC should
5860-
// be failed backwards. Failing to send here
5861-
// indicates that this HTLC may keep being put back
5862-
// into the holding cell without ever being
5863-
// successfully forwarded/failed/fulfilled, causing
5864-
// our counterparty to eventually close on us.
5865-
htlcs_to_fail.push((source.clone(), *payment_hash));
5866-
},
5867-
_ => {
5868-
panic!("Got a non-IgnoreError action trying to send holding cell HTLC");
5869-
},
5870-
}
5855+
Err((_, msg)) => {
5856+
log_info!(logger, "Failed to send HTLC with payment_hash {} due to {} in channel {}", &payment_hash, msg, &self.context.channel_id());
5857+
// If we fail to send here, then this HTLC should be failed
5858+
// backwards. Failing to send here indicates that this HTLC may
5859+
// keep being put back into the holding cell without ever being
5860+
// successfully forwarded/failed/fulfilled, causing our
5861+
// counterparty to eventually close on us.
5862+
htlcs_to_fail.push((source.clone(), *payment_hash));
58715863
}
58725864
}
58735865
None
@@ -8524,22 +8516,19 @@ impl<SP: Deref> FundedChannel<SP> where
85248516
/// Queues up an outbound HTLC to send by placing it in the holding cell. You should call
85258517
/// [`Self::maybe_free_holding_cell_htlcs`] in order to actually generate and send the
85268518
/// commitment update.
8527-
///
8528-
/// `Err`s will only be [`ChannelError::Ignore`].
85298519
pub fn queue_add_htlc<F: Deref, L: Deref>(
85308520
&mut self, amount_msat: u64, payment_hash: PaymentHash, cltv_expiry: u32, source: HTLCSource,
85318521
onion_routing_packet: msgs::OnionPacket, skimmed_fee_msat: Option<u64>,
85328522
blinding_point: Option<PublicKey>, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
8533-
) -> Result<(), ChannelError>
8523+
) -> Result<(), (LocalHTLCFailureReason, String)>
85348524
where F::Target: FeeEstimator, L::Target: Logger
85358525
{
85368526
self
85378527
.send_htlc(amount_msat, payment_hash, cltv_expiry, source, onion_routing_packet, true,
85388528
skimmed_fee_msat, blinding_point, fee_estimator, logger)
85398529
.map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?"))
85408530
.map_err(|err| {
8541-
if let ChannelError::Ignore(_) = err { /* fine */ }
8542-
else { debug_assert!(false, "Queueing cannot trigger channel failure"); }
8531+
debug_assert!(err.0.is_temporary(), "Queuing HTLC should return temporary error");
85438532
err
85448533
})
85458534
}
@@ -8559,38 +8548,40 @@ impl<SP: Deref> FundedChannel<SP> where
85598548
/// You MUST call [`Self::send_commitment_no_state_update`] prior to calling any other methods
85608549
/// on this [`FundedChannel`] if `force_holding_cell` is false.
85618550
///
8562-
/// `Err`s will only be [`ChannelError::Ignore`].
8551+
/// `Err`'s will always be temporary channel failures.
85638552
fn send_htlc<F: Deref, L: Deref>(
85648553
&mut self, amount_msat: u64, payment_hash: PaymentHash, cltv_expiry: u32, source: HTLCSource,
85658554
onion_routing_packet: msgs::OnionPacket, mut force_holding_cell: bool,
85668555
skimmed_fee_msat: Option<u64>, blinding_point: Option<PublicKey>,
85678556
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
8568-
) -> Result<Option<msgs::UpdateAddHTLC>, ChannelError>
8557+
) -> Result<Option<msgs::UpdateAddHTLC>, (LocalHTLCFailureReason, String)>
85698558
where F::Target: FeeEstimator, L::Target: Logger
85708559
{
85718560
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) ||
85728561
self.context.channel_state.is_local_shutdown_sent() ||
85738562
self.context.channel_state.is_remote_shutdown_sent()
85748563
{
8575-
return Err(ChannelError::Ignore("Cannot send HTLC until channel is fully established and we haven't started shutting down".to_owned()));
8564+
return Err((LocalHTLCFailureReason::ChannelNotReady,
8565+
"Cannot send HTLC until channel is fully established and we haven't started shutting down".to_owned()));
85768566
}
85778567
let channel_total_msat = self.funding.get_value_satoshis() * 1000;
85788568
if amount_msat > channel_total_msat {
8579-
return Err(ChannelError::Ignore(format!("Cannot send amount {}, because it is more than the total value of the channel {}", amount_msat, channel_total_msat)));
8569+
return Err((LocalHTLCFailureReason::AmountExceedsCapacity,
8570+
format!("Cannot send amount {}, because it is more than the total value of the channel {}", amount_msat, channel_total_msat)));
85808571
}
85818572

85828573
if amount_msat == 0 {
8583-
return Err(ChannelError::Ignore("Cannot send 0-msat HTLC".to_owned()));
8574+
return Err((LocalHTLCFailureReason::ZeroAmount, "Cannot send 0-msat HTLC".to_owned()));
85848575
}
85858576

85868577
let available_balances = self.context.get_available_balances(&self.funding, fee_estimator);
85878578
if amount_msat < available_balances.next_outbound_htlc_minimum_msat {
8588-
return Err(ChannelError::Ignore(format!("Cannot send less than our next-HTLC minimum - {} msat",
8579+
return Err((LocalHTLCFailureReason::LiquidityMinimum, format!("Cannot send less than our next-HTLC minimum - {} msat",
85898580
available_balances.next_outbound_htlc_minimum_msat)));
85908581
}
85918582

85928583
if amount_msat > available_balances.next_outbound_htlc_limit_msat {
8593-
return Err(ChannelError::Ignore(format!("Cannot send more than our next-HTLC maximum - {} msat",
8584+
return Err((LocalHTLCFailureReason::LiquidityMaximum, format!("Cannot send more than our next-HTLC maximum - {} msat",
85948585
available_balances.next_outbound_htlc_limit_msat)));
85958586
}
85968587

@@ -8601,7 +8592,8 @@ impl<SP: Deref> FundedChannel<SP> where
86018592
// disconnected during the time the previous hop was doing the commitment dance we may
86028593
// end up getting here after the forwarding delay. In any case, returning an
86038594
// IgnoreError will get ChannelManager to do the right thing and fail backwards now.
8604-
return Err(ChannelError::Ignore("Cannot send an HTLC while disconnected from channel counterparty".to_owned()));
8595+
return Err((LocalHTLCFailureReason::PeerOffline,
8596+
"Cannot send an HTLC while disconnected from channel counterparty".to_owned()));
86058597
}
86068598

86078599
let need_holding_cell = !self.context.channel_state.can_generate_new_commitment();
@@ -8821,8 +8813,8 @@ impl<SP: Deref> FundedChannel<SP> where
88218813
{
88228814
let send_res = self.send_htlc(amount_msat, payment_hash, cltv_expiry, source,
88238815
onion_routing_packet, false, skimmed_fee_msat, None, fee_estimator, logger);
8824-
if let Err(e) = &send_res { if let ChannelError::Ignore(_) = e {} else { debug_assert!(false, "Sending cannot trigger channel failure"); } }
8825-
match send_res? {
8816+
// All [`LocalHTLCFailureReason`] errors are temporary, so they are [`ChannelError::Ignore`].
8817+
match send_res.map_err(|(_, msg)| ChannelError::Ignore(msg))? {
88268818
Some(_) => {
88278819
let monitor_update = self.build_commitment_no_status_check(logger);
88288820
self.monitor_updating_paused(false, true, false, Vec::new(), Vec::new(), Vec::new());

lightning/src/ln/channelmanager.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -6090,22 +6090,17 @@ where
60906090
};
60916091
log_trace!(logger, "Forwarding HTLC from SCID {} with payment_hash {} and next hop SCID {} over {} channel {} with corresponding peer {}",
60926092
prev_short_channel_id, &payment_hash, short_chan_id, channel_description, optimal_channel.context.channel_id(), &counterparty_node_id);
6093-
if let Err(e) = optimal_channel.queue_add_htlc(outgoing_amt_msat,
6093+
if let Err((reason, msg)) = optimal_channel.queue_add_htlc(outgoing_amt_msat,
60946094
payment_hash, outgoing_cltv_value, htlc_source.clone(),
60956095
onion_packet.clone(), skimmed_fee_msat, next_blinding_point, &self.fee_estimator,
60966096
&&logger)
60976097
{
6098-
if let ChannelError::Ignore(msg) = e {
6099-
log_trace!(logger, "Failed to forward HTLC with payment_hash {} to peer {}: {}", &payment_hash, &counterparty_node_id, msg);
6100-
} else {
6101-
panic!("Stated return value requirements in send_htlc() were not met");
6102-
}
6098+
log_trace!(logger, "Failed to forward HTLC with payment_hash {} to peer {}: {}", &payment_hash, &counterparty_node_id, msg);
61036099

61046100
if let Some(chan) = peer_state.channel_by_id
61056101
.get_mut(&forward_chan_id)
61066102
.and_then(Channel::as_funded_mut)
61076103
{
6108-
let reason = LocalHTLCFailureReason::TemporaryChannelFailure;
61096104
let data = self.get_htlc_inbound_temp_fail_data(reason);
61106105
failed_forwards.push((htlc_source, payment_hash,
61116106
HTLCFailReason::reason(reason, data),

lightning/src/ln/onion_utils.rs

+31-4
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,19 @@ pub enum LocalHTLCFailureReason {
14971497
DroppedPending,
14981498
/// The HTLC was failed back because its channel is closed and it has timed out on chain.
14991499
ChannelClosed,
1500-
/// UnknownFailureCode represents BOLT04 failure codes that we are not familiar with. We will
1500+
/// The HTLC was failed because its amount is greater than the capacity of the channel.
1501+
AmountExceedsCapacity,
1502+
/// The HTLC was failed because zero amount HTLCs are not allowed.
1503+
ZeroAmount,
1504+
/// The HTLC was failed because its amount is less than the smallest HTLC that the channel
1505+
/// can currently accept.
1506+
LiquidityMinimum,
1507+
/// The HTLC was failed because its amount is more than then largest HTLC that the channel
1508+
/// can currently accept.
1509+
LiquidityMaximum,
1510+
/// The HTLC was failed because our remote peer is offline.
1511+
PeerOffline,
1512+
/// UnknownFailureCode represents BOLT04 failure codes that we are not familiar with. We will
15011513
/// encounter this if:
15021514
/// - A peer sends us a new failure code that LDK has not yet been upgraded to understand.
15031515
/// - We read a deprecated failure code from disk that LDK no longer uses.
@@ -1523,7 +1535,12 @@ impl LocalHTLCFailureReason {
15231535
| Self::DustLimitHolder
15241536
| Self::DustLimitCounterparty
15251537
| Self::FeeSpikeBuffer
1526-
| Self::ChannelNotReady => UPDATE | 7,
1538+
| Self::ChannelNotReady
1539+
| Self::AmountExceedsCapacity
1540+
| Self::ZeroAmount
1541+
| Self::LiquidityMinimum
1542+
| Self::LiquidityMaximum
1543+
| Self::PeerOffline => UPDATE | 7,
15271544
Self::PermanentChannelFailure | Self::ChannelClosed | Self::DroppedPending => PERM | 8,
15281545
Self::RequiredChannelFeature => PERM | 9,
15291546
Self::UnknownNextPeer
@@ -1650,7 +1667,12 @@ impl_writeable_tlv_based_enum!(LocalHTLCFailureReason,
16501667
(72, ChannelClosed) => {},
16511668
(74, UnknownFailureCode) => {
16521669
(0, code, required),
1653-
}
1670+
},
1671+
(76, AmountExceedsCapacity) => {},
1672+
(78, ZeroAmount) => {},
1673+
(80, LiquidityMinimum) => {},
1674+
(82, LiquidityMaximum) => {},
1675+
(84, PeerOffline) => {},
16541676
);
16551677

16561678
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
@@ -1731,7 +1753,12 @@ impl HTLCFailReason {
17311753
| LocalHTLCFailureReason::DustLimitHolder
17321754
| LocalHTLCFailureReason::DustLimitCounterparty
17331755
| LocalHTLCFailureReason::FeeSpikeBuffer
1734-
| LocalHTLCFailureReason::ChannelNotReady => {
1756+
| LocalHTLCFailureReason::ChannelNotReady
1757+
| LocalHTLCFailureReason::AmountExceedsCapacity
1758+
| LocalHTLCFailureReason::ZeroAmount
1759+
| LocalHTLCFailureReason::LiquidityMinimum
1760+
| LocalHTLCFailureReason::LiquidityMaximum
1761+
| LocalHTLCFailureReason::PeerOffline => {
17351762
debug_assert_eq!(
17361763
data.len() - 2,
17371764
u16::from_be_bytes(data[0..2].try_into().unwrap()) as usize

0 commit comments

Comments
 (0)