@@ -402,6 +402,9 @@ pub(crate) struct HTLCPreviousHopData {
402402 // channel with a preimage provided by the forward channel.
403403 outpoint: OutPoint,
404404 counterparty_node_id: Option<PublicKey>,
405+ /// Used to preserve our backwards channel by failing back in case an HTLC claim in the forward
406+ /// channel remains unconfirmed for too long.
407+ cltv_expiry: Option<u32>,
405408}
406409
407410#[derive(PartialEq, Eq)]
@@ -704,6 +707,15 @@ impl HTLCSource {
704707 true
705708 }
706709 }
710+
711+ /// Returns the CLTV expiry of the inbound HTLC (i.e. the source referred to by this object),
712+ /// if the source was a forwarded HTLC and the HTLC was first forwarded on LDK 0.1.1 or later.
713+ pub(crate) fn inbound_htlc_expiry(&self) -> Option<u32> {
714+ match self {
715+ Self::PreviousHopData(HTLCPreviousHopData { cltv_expiry, .. }) => *cltv_expiry,
716+ _ => None,
717+ }
718+ }
707719}
708720
709721/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -5559,7 +5571,7 @@ where
55595571 err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
55605572 })?;
55615573
5562- if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
5574+ if let PendingHTLCRouting::Forward { short_channel_id, incoming_cltv_expiry, .. } = payment.forward_info.routing {
55635575 let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
55645576 short_channel_id: payment.prev_short_channel_id,
55655577 user_channel_id: Some(payment.prev_user_channel_id),
@@ -5570,6 +5582,7 @@ where
55705582 incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret,
55715583 phantom_shared_secret: None,
55725584 blinded_failure: payment.forward_info.routing.blinded_failure(),
5585+ cltv_expiry: incoming_cltv_expiry,
55735586 });
55745587
55755588 let failure_reason = HTLCFailReason::from_failure_code(0x4000 | 10);
@@ -5744,6 +5757,7 @@ where
57445757 outgoing_cltv_value, ..
57455758 }
57465759 }) => {
5760+ let cltv_expiry = routing.incoming_cltv_expiry();
57475761 macro_rules! failure_handler {
57485762 ($msg: expr, $err_code: expr, $err_data: expr, $phantom_ss: expr, $next_hop_unknown: expr) => {
57495763 let logger = WithContext::from(&self.logger, forwarding_counterparty, Some(prev_channel_id), Some(payment_hash));
@@ -5759,6 +5773,7 @@ where
57595773 incoming_packet_shared_secret: incoming_shared_secret,
57605774 phantom_shared_secret: $phantom_ss,
57615775 blinded_failure: routing.blinded_failure(),
5776+ cltv_expiry,
57625777 });
57635778
57645779 let reason = if $next_hop_unknown {
@@ -5868,7 +5883,7 @@ where
58685883 prev_user_channel_id, prev_counterparty_node_id, forward_info: PendingHTLCInfo {
58695884 incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value,
58705885 routing: PendingHTLCRouting::Forward {
5871- ref onion_packet, blinded, ..
5886+ ref onion_packet, blinded, incoming_cltv_expiry, ..
58725887 }, skimmed_fee_msat, ..
58735888 },
58745889 }) => {
@@ -5883,6 +5898,7 @@ where
58835898 // Phantom payments are only PendingHTLCRouting::Receive.
58845899 phantom_shared_secret: None,
58855900 blinded_failure: blinded.map(|b| b.failure),
5901+ cltv_expiry: incoming_cltv_expiry,
58865902 });
58875903 let next_blinding_point = blinded.and_then(|b| {
58885904 b.next_blinding_override.or_else(|| {
@@ -6073,6 +6089,7 @@ where
60736089 incoming_packet_shared_secret: incoming_shared_secret,
60746090 phantom_shared_secret,
60756091 blinded_failure,
6092+ cltv_expiry: Some(cltv_expiry),
60766093 },
60776094 // We differentiate the received value from the sender intended value
60786095 // if possible so that we don't prematurely mark MPP payments complete
@@ -6106,6 +6123,7 @@ where
61066123 incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
61076124 phantom_shared_secret,
61086125 blinded_failure,
6126+ cltv_expiry: Some(cltv_expiry),
61096127 }), payment_hash,
61106128 HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
61116129 HTLCDestination::FailedPayment { payment_hash: $payment_hash },
@@ -8889,6 +8907,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
88898907 incoming_packet_shared_secret: forward_info.incoming_shared_secret,
88908908 phantom_shared_secret: None,
88918909 blinded_failure: forward_info.routing.blinded_failure(),
8910+ cltv_expiry: forward_info.routing.incoming_cltv_expiry(),
88928911 });
88938912
88948913 failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
@@ -11154,6 +11173,7 @@ where
1115411173 outpoint: htlc.prev_funding_outpoint,
1115511174 channel_id: htlc.prev_channel_id,
1115611175 blinded_failure: htlc.forward_info.routing.blinded_failure(),
11176+ cltv_expiry: htlc.forward_info.routing.incoming_cltv_expiry(),
1115711177 });
1115811178
1115911179 let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
@@ -12494,6 +12514,7 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
1249412514 (2, outpoint, required),
1249512515 (3, blinded_failure, option),
1249612516 (4, htlc_id, required),
12517+ (5, cltv_expiry, option),
1249712518 (6, incoming_packet_shared_secret, required),
1249812519 (7, user_channel_id, option),
1249912520 // Note that by the time we get past the required read for type 2 above, outpoint will be
0 commit comments