@@ -399,6 +399,9 @@ pub(crate) struct HTLCPreviousHopData {
399399 // channel with a preimage provided by the forward channel.
400400 outpoint: OutPoint,
401401 counterparty_node_id: Option<PublicKey>,
402+ /// Used to preserve our backwards channel by failing back in case an HTLC claim in the forward
403+ /// channel remains unconfirmed for too long.
404+ cltv_expiry: Option<u32>,
402405}
403406
404407#[derive(PartialEq, Eq)]
@@ -701,6 +704,15 @@ impl HTLCSource {
701704 true
702705 }
703706 }
707+
708+ /// Returns the CLTV expiry of the inbound HTLC (i.e. the source referred to by this object),
709+ /// if the source was a forwarded HTLC and the HTLC was first forwarded on LDK 0.1.1 or later.
710+ pub(crate) fn inbound_htlc_expiry(&self) -> Option<u32> {
711+ match self {
712+ Self::PreviousHopData(HTLCPreviousHopData { cltv_expiry, .. }) => *cltv_expiry,
713+ _ => None,
714+ }
715+ }
704716}
705717
706718/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -5573,7 +5585,7 @@ where
55735585 err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
55745586 })?;
55755587
5576- if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
5588+ if let PendingHTLCRouting::Forward { short_channel_id, incoming_cltv_expiry, .. } = payment.forward_info.routing {
55775589 let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
55785590 short_channel_id: payment.prev_short_channel_id,
55795591 user_channel_id: Some(payment.prev_user_channel_id),
@@ -5584,6 +5596,7 @@ where
55845596 incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret,
55855597 phantom_shared_secret: None,
55865598 blinded_failure: payment.forward_info.routing.blinded_failure(),
5599+ cltv_expiry: incoming_cltv_expiry,
55875600 });
55885601
55895602 let failure_reason = HTLCFailReason::from_failure_code(0x4000 | 10);
@@ -5758,6 +5771,7 @@ where
57585771 outgoing_cltv_value, ..
57595772 }
57605773 }) => {
5774+ let cltv_expiry = routing.incoming_cltv_expiry();
57615775 macro_rules! failure_handler {
57625776 ($msg: expr, $err_code: expr, $err_data: expr, $phantom_ss: expr, $next_hop_unknown: expr) => {
57635777 let logger = WithContext::from(&self.logger, forwarding_counterparty, Some(prev_channel_id), Some(payment_hash));
@@ -5773,6 +5787,7 @@ where
57735787 incoming_packet_shared_secret: incoming_shared_secret,
57745788 phantom_shared_secret: $phantom_ss,
57755789 blinded_failure: routing.blinded_failure(),
5790+ cltv_expiry,
57765791 });
57775792
57785793 let reason = if $next_hop_unknown {
@@ -5882,7 +5897,7 @@ where
58825897 prev_user_channel_id, prev_counterparty_node_id, forward_info: PendingHTLCInfo {
58835898 incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value,
58845899 routing: PendingHTLCRouting::Forward {
5885- ref onion_packet, blinded, ..
5900+ ref onion_packet, blinded, incoming_cltv_expiry, ..
58865901 }, skimmed_fee_msat, ..
58875902 },
58885903 }) => {
@@ -5897,6 +5912,7 @@ where
58975912 // Phantom payments are only PendingHTLCRouting::Receive.
58985913 phantom_shared_secret: None,
58995914 blinded_failure: blinded.map(|b| b.failure),
5915+ cltv_expiry: incoming_cltv_expiry,
59005916 });
59015917 let next_blinding_point = blinded.and_then(|b| {
59025918 b.next_blinding_override.or_else(|| {
@@ -6086,6 +6102,7 @@ where
60866102 incoming_packet_shared_secret: incoming_shared_secret,
60876103 phantom_shared_secret,
60886104 blinded_failure,
6105+ cltv_expiry: Some(cltv_expiry),
60896106 },
60906107 // We differentiate the received value from the sender intended value
60916108 // if possible so that we don't prematurely mark MPP payments complete
@@ -6119,6 +6136,7 @@ where
61196136 incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
61206137 phantom_shared_secret,
61216138 blinded_failure,
6139+ cltv_expiry: Some(cltv_expiry),
61226140 }), payment_hash,
61236141 HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
61246142 HTLCDestination::FailedPayment { payment_hash: $payment_hash },
@@ -8979,6 +8997,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
89798997 incoming_packet_shared_secret: forward_info.incoming_shared_secret,
89808998 phantom_shared_secret: None,
89818999 blinded_failure: forward_info.routing.blinded_failure(),
9000+ cltv_expiry: forward_info.routing.incoming_cltv_expiry(),
89829001 });
89839002
89849003 failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
@@ -11221,6 +11240,7 @@ where
1122111240 outpoint: htlc.prev_funding_outpoint,
1122211241 channel_id: htlc.prev_channel_id,
1122311242 blinded_failure: htlc.forward_info.routing.blinded_failure(),
11243+ cltv_expiry: htlc.forward_info.routing.incoming_cltv_expiry(),
1122411244 });
1122511245
1122611246 let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
@@ -12559,6 +12579,7 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
1255912579 (2, outpoint, required),
1256012580 (3, blinded_failure, option),
1256112581 (4, htlc_id, required),
12582+ (5, cltv_expiry, option),
1256212583 (6, incoming_packet_shared_secret, required),
1256312584 (7, user_channel_id, option),
1256412585 // Note that by the time we get past the required read for type 2 above, outpoint will be
0 commit comments