@@ -89,9 +89,9 @@ use crate::ln::outbound_payment;
8989#[cfg(any(test, feature = "_externalize_tests"))]
9090use crate::ln::outbound_payment::PaymentSendFailure;
9191use crate::ln::outbound_payment::{
92- Bolt11PaymentError, Bolt12PaymentError, OutboundPayments, PendingOutboundPayment ,
93- ProbeSendFailure, RecipientCustomTlvs, RecipientOnionFields, Retry, RetryableInvoiceRequest ,
94- RetryableSendFailure, SendAlongPathArgs, StaleExpiration,
92+ Bolt11PaymentError, Bolt12PaymentError, NextTrampolineHopInfo, OutboundPayments ,
93+ PendingOutboundPayment, ProbeSendFailure, RecipientCustomTlvs, RecipientOnionFields, Retry,
94+ RetryableInvoiceRequest, RetryableSendFailure, SendAlongPathArgs, StaleExpiration,
9595};
9696use crate::ln::types::ChannelId;
9797use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
@@ -8470,6 +8470,130 @@ impl<
84708470 }
84718471 }
84728472
8473+ // Handles the addition of a HTLC associated with a trampoline forward that we need to accumulate
8474+ // on the incoming link before forwarding onwards. If the HTLC is failed, it returns the source
8475+ // and error that should be used to fail the HTLC(s) back.
8476+ fn handle_trampoline_htlc(
8477+ &self, mpp_part: MppPart, onion_fields: RecipientOnionFields, payment_hash: PaymentHash,
8478+ next_hop_info: NextTrampolineHopInfo, _next_node_id: PublicKey,
8479+ ) -> Result<(), (HTLCSource, HTLCFailReason)> {
8480+ let mut trampoline_payments = self.awaiting_trampoline_forwards.lock().unwrap();
8481+
8482+ let mut committed_to_claimable = false;
8483+ let trampoline_payment = trampoline_payments.entry(payment_hash).or_insert_with(|| {
8484+ committed_to_claimable = true;
8485+ TrampolinePayment { htlcs: Vec::new(), onion_fields: onion_fields.clone() }
8486+ });
8487+
8488+ // If MPP hasn't fully arrived yet, return early (saving indentation below).
8489+ let prev_hop = mpp_part.prev_hop.clone();
8490+ match self.check_incoming_mpp_part(
8491+ &mut trampoline_payment.htlcs,
8492+ &mut trampoline_payment.onion_fields,
8493+ mpp_part,
8494+ onion_fields,
8495+ payment_hash,
8496+ ) {
8497+ Ok(false) => return Ok(()),
8498+ Err(()) => {
8499+ if committed_to_claimable {
8500+ // If this was the first HTLC for this payment hash and check failed
8501+ // (eg, total_intended_recvd_value >= MAX_VALUE_MSAT), clean up the
8502+ // empty entry we just inserted.
8503+ trampoline_payments.remove(&payment_hash);
8504+ }
8505+ return Err((
8506+ // When we couldn't add a new HTLC, we just fail back our last received htlc,
8507+ // allowing others to wait for more MPP parts to arrive.
8508+ HTLCSource::TrampolineForward {
8509+ previous_hop_data: vec![prev_hop],
8510+ outbound_payment: None,
8511+ },
8512+ HTLCFailReason::reason(
8513+ LocalHTLCFailureReason::InvalidTrampolineForward,
8514+ vec![],
8515+ ),
8516+ ));
8517+ },
8518+ Ok(true) => {},
8519+ };
8520+
8521+ let incoming_amt_msat: u64 = trampoline_payment.htlcs.iter().map(|h| h.value).sum();
8522+ let incoming_cltv_expiry =
8523+ trampoline_payment.htlcs.iter().map(|h| h.cltv_expiry).min().unwrap();
8524+
8525+ let (forwarding_fee_proportional_millionths, forwarding_fee_base_msat, cltv_delta) = {
8526+ let config = self.config.read().unwrap();
8527+ (
8528+ config.channel_config.forwarding_fee_proportional_millionths,
8529+ config.channel_config.forwarding_fee_base_msat,
8530+ config.channel_config.cltv_expiry_delta as u32,
8531+ )
8532+ };
8533+
8534+ let proportional_fee = (forwarding_fee_proportional_millionths as u128
8535+ * next_hop_info.amount_msat as u128
8536+ / 1_000_000) as u64;
8537+ let our_forwarding_fee_msat = proportional_fee + forwarding_fee_base_msat as u64;
8538+
8539+ let trampoline_source = || -> HTLCSource {
8540+ HTLCSource::TrampolineForward {
8541+ previous_hop_data: trampoline_payment
8542+ .htlcs
8543+ .iter()
8544+ .map(|htlc| htlc.prev_hop.clone())
8545+ .collect(),
8546+ outbound_payment: None,
8547+ }
8548+ };
8549+ let trampoline_failure = || -> HTLCFailReason {
8550+ let mut err_data = Vec::with_capacity(10);
8551+ err_data.extend_from_slice(&forwarding_fee_base_msat.to_be_bytes());
8552+ err_data.extend_from_slice(&forwarding_fee_proportional_millionths.to_be_bytes());
8553+ err_data.extend_from_slice(&(cltv_delta as u16).to_be_bytes());
8554+ HTLCFailReason::reason(
8555+ LocalHTLCFailureReason::TrampolineFeeOrExpiryInsufficient,
8556+ err_data,
8557+ )
8558+ };
8559+
8560+ let _max_total_routing_fee_msat = match incoming_amt_msat
8561+ .checked_sub(our_forwarding_fee_msat + next_hop_info.amount_msat)
8562+ {
8563+ Some(amount) => amount,
8564+ None => {
8565+ return Err((trampoline_source(), trampoline_failure()));
8566+ },
8567+ };
8568+
8569+ let _max_total_cltv_expiry_delta =
8570+ match incoming_cltv_expiry.checked_sub(next_hop_info.cltv_expiry_height + cltv_delta) {
8571+ Some(cltv_delta) => cltv_delta,
8572+ None => {
8573+ return Err((trampoline_source(), trampoline_failure()));
8574+ },
8575+ };
8576+
8577+ log_debug!(
8578+ self.logger,
8579+ "Rejecting trampoline forward because we do not fully support forwarding yet.",
8580+ );
8581+
8582+ let source = trampoline_source();
8583+ if trampoline_payments.remove(&payment_hash).is_none() {
8584+ log_error!(
8585+ &self.logger,
8586+ "Dispatched trampoline payment: {} was not present in awaiting inbound",
8587+ payment_hash
8588+ );
8589+ }
8590+
8591+ Err((
8592+ source,
8593+ HTLCFailReason::reason(LocalHTLCFailureReason::TemporaryTrampolineFailure, vec![]),
8594+ ))
8595+ }
8596+
84738597 fn process_receive_htlcs(
84748598 &self, pending_forwards: &mut Vec<HTLCForwardInfo>,
84758599 new_events: &mut VecDeque<(Event, Option<EventCompletionAction>)>,
@@ -8504,6 +8628,7 @@ impl<
85048628 has_recipient_created_payment_secret,
85058629 invoice_request_opt,
85068630 trampoline_shared_secret,
8631+ trampoline_info,
85078632 ) = match routing {
85088633 PendingHTLCRouting::Receive {
85098634 payment_data,
@@ -8532,6 +8657,7 @@ impl<
85328657 true,
85338658 None,
85348659 trampoline_shared_secret,
8660+ None,
85358661 )
85368662 },
85378663 PendingHTLCRouting::ReceiveKeysend {
@@ -8566,13 +8692,98 @@ impl<
85668692 has_recipient_created_payment_secret,
85678693 invoice_request,
85688694 None,
8695+ None,
8696+ )
8697+ },
8698+ PendingHTLCRouting::TrampolineForward {
8699+ trampoline_shared_secret: incoming_trampoline_shared_secret,
8700+ onion_packet,
8701+ node_id: next_trampoline,
8702+ blinded,
8703+ incoming_cltv_expiry,
8704+ incoming_multipath_data,
8705+ next_trampoline_amt_msat,
8706+ next_trampoline_cltv_expiry,
8707+ } => {
8708+ // Trampoline forwards only *need* to have MPP data if they're
8709+ // multi-part.
8710+ let onion_fields = match incoming_multipath_data {
8711+ Some(ref final_mpp) => RecipientOnionFields::secret_only(
8712+ final_mpp.payment_secret,
8713+ final_mpp.total_msat,
8714+ ),
8715+ None => RecipientOnionFields::spontaneous_empty(outgoing_amt_msat),
8716+ };
8717+
8718+ let next_hop_info = NextTrampolineHopInfo {
8719+ onion_packet,
8720+ blinding_point: blinded.and_then(|b| {
8721+ b.next_blinding_override.or_else(|| {
8722+ let encrypted_tlvs_ss = self
8723+ .node_signer
8724+ .ecdh(Recipient::Node, &b.inbound_blinding_point, None)
8725+ .unwrap()
8726+ .secret_bytes();
8727+ onion_utils::next_hop_pubkey(
8728+ &self.secp_ctx,
8729+ b.inbound_blinding_point,
8730+ &encrypted_tlvs_ss,
8731+ )
8732+ .ok()
8733+ })
8734+ }),
8735+ amount_msat: next_trampoline_amt_msat,
8736+ cltv_expiry_height: next_trampoline_cltv_expiry,
8737+ };
8738+ (
8739+ incoming_cltv_expiry,
8740+ // Unused for trampoline forwards; MppPart is constructed
8741+ // directly below.
8742+ OnionPayload::Invoice { _legacy_hop_data: None },
8743+ incoming_multipath_data,
8744+ None,
8745+ None,
8746+ onion_fields,
8747+ false,
8748+ None,
8749+ Some(incoming_trampoline_shared_secret),
8750+ Some((next_hop_info, next_trampoline)),
85698751 )
85708752 },
85718753 _ => {
85728754 panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
85738755 },
85748756 };
85758757 let htlc_value = incoming_amt_msat.unwrap_or(outgoing_amt_msat);
8758+ // For trampoline forwards, construct MppPart directly and handle separately
8759+ // from claimable HTLCs.
8760+ if let Some((next_hop_info, next_trampoline)) = trampoline_info {
8761+ let mpp_part = MppPart {
8762+ prev_hop,
8763+ cltv_expiry,
8764+ value: htlc_value,
8765+ sender_intended_value: outgoing_amt_msat,
8766+ timer_ticks: 0,
8767+ total_value_received: None,
8768+ };
8769+ if let Err((htlc_source, failure_reason)) = self.handle_trampoline_htlc(
8770+ mpp_part,
8771+ onion_fields,
8772+ payment_hash,
8773+ next_hop_info,
8774+ next_trampoline,
8775+ ) {
8776+ failed_forwards.push((
8777+ htlc_source,
8778+ payment_hash,
8779+ failure_reason,
8780+ HTLCHandlingFailureType::TrampolineForward {},
8781+ ));
8782+ }
8783+ continue 'next_forwardable_htlc;
8784+ }
8785+
8786+ // If we don't have a trampoline forward, we're dealing with a MPP receive.
85768787 let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
85778788 prev_outbound_scid_alias: prev_hop.prev_outbound_scid_alias,
85788789 user_channel_id: prev_hop.user_channel_id,
0 commit comments