Skip to content

Commit 0882ed5

Browse files
committed
events+ln: surface failure reason in HTLCDestination
A failure reason is only added for NextHopChannel and FailedPayment becuase the other variants already surface sufficient useful information.
1 parent 2400fbe commit 0882ed5

File tree

3 files changed

+82
-28
lines changed

3 files changed

+82
-28
lines changed

lightning/src/events/mod.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, Paym
2323
use crate::chain::transaction;
2424
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
2525
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
26+
use crate::ln::onion_utils::{LocalHTLCFailureReason};
2627
use crate::types::features::ChannelTypeFeatures;
2728
use crate::ln::msgs;
2829
use crate::ln::types::ChannelId;
@@ -465,6 +466,25 @@ impl_writeable_tlv_based_enum_upgradable!(ClosureReason,
465466
},
466467
);
467468

469+
/// The reason for HTLC failures in [`HTLCDestination`].
470+
#[derive(Clone, Debug, PartialEq, Eq)]
471+
pub enum HTLCDestinationFailure {
472+
/// The forwarded HTLC was failed back by the downstream node with an encrypted error reason.
473+
Downstream,
474+
/// The HTLC was failed locally by our node.
475+
Local{
476+
/// The reason that our node chose to fail the HTLC.
477+
reason: LocalHTLCFailureReason
478+
},
479+
}
480+
481+
impl_writeable_tlv_based_enum!(HTLCDestinationFailure,
482+
(0, Downstream) => {},
483+
(1, Local) => {
484+
(0, reason, required),
485+
},
486+
);
487+
468488
/// Intended destination of a failed HTLC as indicated in [`Event::HTLCHandlingFailed`].
469489
#[derive(Clone, Debug, PartialEq, Eq)]
470490
pub enum HTLCDestination {
@@ -477,6 +497,9 @@ pub enum HTLCDestination {
477497
node_id: Option<PublicKey>,
478498
/// The outgoing `channel_id` between us and the next node.
479499
channel_id: ChannelId,
500+
/// The reason that the HTLC forward was failed. For backwards compatibility, this field is
501+
/// marked as optional, versions prior to 0.1.1 will set this value to None.
502+
reason: Option<HTLCDestinationFailure>
480503
},
481504
/// Scenario where we are unsure of the next node to forward the HTLC to.
482505
UnknownNextHop {
@@ -503,13 +526,17 @@ pub enum HTLCDestination {
503526
/// recipient for a payment.
504527
FailedPayment {
505528
/// The payment hash of the payment we attempted to process.
506-
payment_hash: PaymentHash
529+
payment_hash: PaymentHash,
530+
/// The reason that the payment was rejected. For backwards compatibility, this field is
531+
/// marked as optional, versions prior to 0.1.1 will set this value to None.
532+
reason: Option<LocalHTLCFailureReason>
507533
},
508534
}
509535

510536
impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
511537
(0, NextHopChannel) => {
512538
(0, node_id, required),
539+
(1, reason, option),
513540
(2, channel_id, required),
514541
},
515542
(1, InvalidForward) => {
@@ -521,6 +548,7 @@ impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
521548
(3, InvalidOnion) => {},
522549
(4, FailedPayment) => {
523550
(0, payment_hash, required),
551+
(1, reason, option),
524552
},
525553
);
526554

lightning/src/ln/channelmanager.rs

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3294,7 +3294,8 @@ macro_rules! handle_monitor_update_completion {
32943294
}
32953295
$self.finalize_claims(updates.finalized_claimed_htlcs);
32963296
for failure in updates.failed_htlcs.drain(..) {
3297-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
3297+
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id),
3298+
channel_id, reason: Some(failure.2.clone().into()) };
32983299
$self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver);
32993300
}
33003301
} }
@@ -3914,7 +3915,7 @@ where
39143915
for htlc_source in failed_htlcs.drain(..) {
39153916
let failure_reason = LocalHTLCFailureReason::ChannelClosed;
39163917
let reason = HTLCFailReason::from_failure_code(failure_reason);
3917-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: *channel_id };
3918+
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: *channel_id, reason: Some(failure_reason.into()) };
39183919
self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
39193920
}
39203921

@@ -4038,7 +4039,7 @@ where
40384039
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
40394040
let failure_reason = LocalHTLCFailureReason::ChannelClosed;
40404041
let reason = HTLCFailReason::from_failure_code(failure_reason);
4041-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
4042+
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id, reason: Some(failure_reason.into()) };
40424043
self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
40434044
}
40444045
if let Some((_, funding_txo, _channel_id, monitor_update)) = shutdown_res.monitor_update {
@@ -5749,20 +5750,21 @@ where
57495750
let mut decode_update_add_htlcs = new_hash_map();
57505751
mem::swap(&mut decode_update_add_htlcs, &mut self.decode_update_add_htlcs.lock().unwrap());
57515752

5752-
let get_failed_htlc_destination = |outgoing_scid_opt: Option<u64>, payment_hash: PaymentHash| {
5753+
let get_failed_htlc_destination = |outgoing_scid_opt: Option<u64>, payment_hash: PaymentHash, reason: LocalHTLCFailureReason| {
57535754
if let Some(outgoing_scid) = outgoing_scid_opt {
57545755
match self.short_to_chan_info.read().unwrap().get(&outgoing_scid) {
57555756
Some((outgoing_counterparty_node_id, outgoing_channel_id)) =>
57565757
HTLCDestination::NextHopChannel {
57575758
node_id: Some(*outgoing_counterparty_node_id),
57585759
channel_id: *outgoing_channel_id,
5760+
reason: Some(reason.into()),
57595761
},
57605762
None => HTLCDestination::UnknownNextHop {
57615763
requested_forward_scid: outgoing_scid,
57625764
},
57635765
}
57645766
} else {
5765-
HTLCDestination::FailedPayment { payment_hash }
5767+
HTLCDestination::FailedPayment { payment_hash, reason: Some(reason) }
57665768
}
57675769
};
57685770

@@ -5817,10 +5819,10 @@ where
58175819
Some(Ok(_)) => {},
58185820
Some(Err((err, reason))) => {
58195821
let htlc_fail = self.htlc_failure_from_update_add_err(
5820-
&update_add_htlc, &incoming_counterparty_node_id, err, reason,
5822+
&update_add_htlc, &incoming_counterparty_node_id, err, reason.clone(),
58215823
is_intro_node_blinded_forward, &shared_secret,
58225824
);
5823-
let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash);
5825+
let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash, reason);
58245826
htlc_fails.push((htlc_fail, htlc_destination));
58255827
continue;
58265828
},
@@ -5837,7 +5839,7 @@ where
58375839
&update_add_htlc, &incoming_counterparty_node_id, err, reason,
58385840
is_intro_node_blinded_forward, &shared_secret,
58395841
);
5840-
let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash);
5842+
let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash, reason);
58415843
htlc_fails.push((htlc_fail, htlc_destination));
58425844
continue;
58435845
}
@@ -5849,7 +5851,7 @@ where
58495851
) {
58505852
Ok(info) => htlc_forwards.push((info, update_add_htlc.htlc_id)),
58515853
Err(inbound_err) => {
5852-
let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash);
5854+
let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash, inbound_err.reason);
58535855
htlc_fails.push((self.construct_pending_htlc_fail_msg(&update_add_htlc, &incoming_counterparty_node_id, shared_secret, inbound_err), htlc_destination));
58545856
},
58555857
}
@@ -5935,7 +5937,7 @@ where
59355937
let reason = if $next_hop_unknown {
59365938
HTLCDestination::UnknownNextHop { requested_forward_scid: short_chan_id }
59375939
} else {
5938-
HTLCDestination::FailedPayment{ payment_hash }
5940+
HTLCDestination::FailedPayment{ payment_hash, reason: Some($reason) }
59395941
};
59405942

59415943
failed_forwards.push((htlc_source, payment_hash,
@@ -6125,7 +6127,7 @@ where
61256127
let data = self.get_htlc_inbound_temp_fail_data(reason);
61266128
failed_forwards.push((htlc_source, payment_hash,
61276129
HTLCFailReason::reason(reason, data),
6128-
HTLCDestination::NextHopChannel { node_id: Some(chan.context.get_counterparty_node_id()), channel_id: forward_chan_id }
6130+
HTLCDestination::NextHopChannel { node_id: Some(chan.context.get_counterparty_node_id()), channel_id: forward_chan_id, reason: Some(reason.into()) }
61296131
));
61306132
} else {
61316133
forwarding_channel_not_found!(core::iter::once(forward_info).chain(draining_pending_forwards));
@@ -6266,6 +6268,7 @@ where
62666268
htlc_msat_height_data.extend_from_slice(
62676269
&self.best_block.read().unwrap().height.to_be_bytes(),
62686270
);
6271+
let reason = LocalHTLCFailureReason::IncorrectPaymentDetails;
62696272
failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData {
62706273
short_channel_id: $htlc.prev_hop.short_channel_id,
62716274
user_channel_id: $htlc.prev_hop.user_channel_id,
@@ -6278,8 +6281,8 @@ where
62786281
blinded_failure,
62796282
cltv_expiry: Some(cltv_expiry),
62806283
}), payment_hash,
6281-
HTLCFailReason::reason(LocalHTLCFailureReason::IncorrectPaymentDetails, htlc_msat_height_data),
6282-
HTLCDestination::FailedPayment { payment_hash: $payment_hash },
6284+
HTLCFailReason::reason(reason, htlc_msat_height_data),
6285+
HTLCDestination::FailedPayment { payment_hash: $payment_hash, reason: Some(reason) },
62836286
));
62846287
continue 'next_forwardable_htlc;
62856288
}
@@ -6837,7 +6840,7 @@ where
68376840
let source = HTLCSource::PreviousHopData(htlc_source.0.clone());
68386841
let failure_reason = LocalHTLCFailureReason::MPPTimeout;
68396842
let reason = HTLCFailReason::from_failure_code(failure_reason);
6840-
let receiver = HTLCDestination::FailedPayment { payment_hash: htlc_source.1 };
6843+
let receiver = HTLCDestination::FailedPayment { payment_hash: htlc_source.1, reason: Some(failure_reason) };
68416844
self.fail_htlc_backwards_internal(&source, &htlc_source.1, &reason, receiver);
68426845
}
68436846

@@ -6902,7 +6905,7 @@ where
69026905
for htlc in payment.htlcs {
69036906
let reason = self.get_htlc_fail_reason_from_failure_code(failure_code, &htlc);
69046907
let source = HTLCSource::PreviousHopData(htlc.prev_hop);
6905-
let receiver = HTLCDestination::FailedPayment { payment_hash: *payment_hash };
6908+
let receiver = HTLCDestination::FailedPayment { payment_hash: *payment_hash, reason: Some(failure_code.into()) };
69066909
self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
69076910
}
69086911
}
@@ -6980,8 +6983,12 @@ where
69806983
};
69816984

69826985
for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
6983-
let reason = HTLCFailReason::reason(failure_reason, onion_failure_data.clone());
6984-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id };
6986+
let reason = HTLCFailReason::reason(failure_reason.clone(), onion_failure_data.clone());
6987+
let receiver = HTLCDestination::NextHopChannel {
6988+
node_id: Some(counterparty_node_id.clone()),
6989+
channel_id,
6990+
reason: Some(failure_reason.into()),
6991+
};
69856992
self.fail_htlc_backwards_internal(&htlc_src, &payment_hash, &reason, receiver);
69866993
}
69876994
}
@@ -7128,9 +7135,10 @@ where
71287135
Ok((htlcs, payment_info)) => (htlcs, payment_info),
71297136
Err(htlcs) => {
71307137
for htlc in htlcs {
7131-
let reason = self.get_htlc_fail_reason_from_failure_code(FailureCode::InvalidOnionPayload(None), &htlc);
7138+
let failure_code = FailureCode::InvalidOnionPayload(None);
7139+
let reason = self.get_htlc_fail_reason_from_failure_code(failure_code, &htlc);
71327140
let source = HTLCSource::PreviousHopData(htlc.prev_hop);
7133-
let receiver = HTLCDestination::FailedPayment { payment_hash };
7141+
let receiver = HTLCDestination::FailedPayment { payment_hash, reason: Some(failure_code.into()) };
71347142
self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
71357143
}
71367144
return;
@@ -7228,8 +7236,9 @@ where
72287236
let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
72297237
htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height.to_be_bytes());
72307238
let source = HTLCSource::PreviousHopData(htlc.prev_hop);
7231-
let reason = HTLCFailReason::reason(LocalHTLCFailureReason::IncorrectPaymentDetails, htlc_msat_height_data);
7232-
let receiver = HTLCDestination::FailedPayment { payment_hash };
7239+
let failure_code = LocalHTLCFailureReason::IncorrectPaymentDetails;
7240+
let reason = HTLCFailReason::reason(failure_code, htlc_msat_height_data);
7241+
let receiver = HTLCDestination::FailedPayment { payment_hash, reason: Some(failure_code.into()) };
72337242
self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
72347243
}
72357244
self.claimable_payments.lock().unwrap().pending_claiming_payments.remove(&payment_hash);
@@ -8766,8 +8775,9 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
87668775
}
87678776
}
87688777
for htlc_source in dropped_htlcs.drain(..) {
8769-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id: msg.channel_id };
8770-
let reason = HTLCFailReason::from_failure_code(LocalHTLCFailureReason::ShutdownSent);
8778+
let failure_reason = LocalHTLCFailureReason::ShutdownSent;
8779+
let reason = HTLCFailReason::from_failure_code(failure_reason);
8780+
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id: msg.channel_id, reason: Some(failure_reason.into()) };
87718781
self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
87728782
}
87738783
if let Some(shutdown_res) = finish_shutdown {
@@ -9593,7 +9603,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
95939603
} else {
95949604
log_trace!(logger, "Failing HTLC with hash {} from our monitor", &htlc_update.payment_hash);
95959605
let failure_reason = LocalHTLCFailureReason::ChannelClosed;
9596-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
9606+
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id, reason: Some(failure_reason.into()) };
95979607
let reason = HTLCFailReason::from_failure_code(failure_reason);
95989608
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
95999609
}
@@ -11687,7 +11697,7 @@ where
1168711697
let reason = LocalHTLCFailureReason::CLTVExpiryTooSoon;
1168811698
let data = self.get_htlc_inbound_temp_fail_data(reason);
1168911699
timed_out_htlcs.push((source, payment_hash, HTLCFailReason::reason(reason, data),
11690-
HTLCDestination::NextHopChannel { node_id: Some(funded_channel.context.get_counterparty_node_id()), channel_id: funded_channel.context.channel_id() }));
11700+
HTLCDestination::NextHopChannel { node_id: Some(funded_channel.context.get_counterparty_node_id()), channel_id: funded_channel.context.channel_id(), reason: Some(reason.into()) }));
1169111701
}
1169211702
let logger = WithChannelContext::from(&self.logger, &funded_channel.context, None);
1169311703
if let Some(channel_ready) = channel_ready_opt {
@@ -11811,7 +11821,7 @@ where
1181111821
let reason = LocalHTLCFailureReason::PaymentClaimBuffer;
1181211822
timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(),
1181311823
HTLCFailReason::reason(reason, htlc_msat_height_data),
11814-
HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
11824+
HTLCDestination::FailedPayment { payment_hash: payment_hash.clone(), reason: Some(reason)}));
1181511825
false
1181611826
} else { true }
1181711827
});
@@ -14913,7 +14923,7 @@ where
1491314923
for htlc_source in failed_htlcs.drain(..) {
1491414924
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
1491514925
let failure_reason = LocalHTLCFailureReason::ChannelClosed;
14916-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
14926+
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id, reason: Some(failure_reason.into()) };
1491714927
let reason = HTLCFailReason::from_failure_code(failure_reason);
1491814928
channel_manager.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
1491914929
}

lightning/src/ln/onion_utils.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use crate::blinded_path::BlindedHop;
1111
use crate::crypto::chacha20::ChaCha20;
1212
use crate::crypto::streams::ChaChaReader;
13+
use crate::events::HTLCDestinationFailure;
1314
use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
1415
use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
1516
use crate::ln::msgs;
@@ -1459,6 +1460,21 @@ impl_writeable_tlv_based_enum!(LocalHTLCFailureReason,
14591460
#[cfg_attr(test, derive(PartialEq))]
14601461
pub(super) struct HTLCFailReason(HTLCFailReasonRepr);
14611462

1463+
impl Into<HTLCDestinationFailure> for HTLCFailReason {
1464+
fn into(self) -> HTLCDestinationFailure {
1465+
match self.0 {
1466+
HTLCFailReasonRepr::LightningError { .. } => HTLCDestinationFailure::Downstream,
1467+
HTLCFailReasonRepr::Reason { reason, .. } => HTLCDestinationFailure::Local { reason },
1468+
}
1469+
}
1470+
}
1471+
1472+
impl Into<HTLCDestinationFailure> for LocalHTLCFailureReason {
1473+
fn into(self) -> HTLCDestinationFailure {
1474+
HTLCDestinationFailure::Local { reason: self }
1475+
}
1476+
}
1477+
14621478
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
14631479
#[cfg_attr(test, derive(PartialEq))]
14641480
enum HTLCFailReasonRepr {

0 commit comments

Comments
 (0)