Drop completed blocked `ChannelMonitorUpdate`s on startup
[rust-lightning] / lightning / src / ln / outbound_payment.rs
index b449e27983ecd09efdc88a90ff22235d0f7d0f67..47193bea3f197b2fcd41d261202c612c5f06526a 100644 (file)
@@ -19,7 +19,7 @@ use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
 use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, PaymentId};
 use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
 use crate::offers::invoice::Bolt12Invoice;
-use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
+use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
 use crate::util::errors::APIError;
 use crate::util::logger::Logger;
 use crate::util::time::Time;
@@ -129,6 +129,11 @@ impl PendingOutboundPayment {
                        params.previously_failed_channels.push(scid);
                }
        }
+       pub fn insert_previously_failed_blinded_path(&mut self, blinded_tail: &BlindedTail) {
+               if let PendingOutboundPayment::Retryable { payment_params: Some(params), .. } = self {
+                       params.insert_previously_failed_blinded_path(blinded_tail);
+               }
+       }
        fn is_awaiting_invoice(&self) -> bool {
                match self {
                        PendingOutboundPayment::AwaitingInvoice { .. } => true,
@@ -243,7 +248,7 @@ impl PendingOutboundPayment {
                if insert_res {
                        if let PendingOutboundPayment::Retryable {
                                ref mut pending_amt_msat, ref mut pending_fee_msat,
-                               ref mut remaining_max_total_routing_fee_msat, .. 
+                               ref mut remaining_max_total_routing_fee_msat, ..
                        } = self {
                                        *pending_amt_msat += path.final_value_msat();
                                        let path_fee_msat = path.fee_msat();
@@ -1337,6 +1342,13 @@ impl OutboundPayments {
                                        continue 'path_check;
                                }
                        }
+                       for (i, hop) in path.hops.iter().enumerate() {
+                               // Check for duplicate channel_id in the remaining hops of the path
+                               if path.hops.iter().skip(i + 1).any(|other_hop| other_hop.short_channel_id == hop.short_channel_id) {
+                                       path_errs.push(Err(APIError::InvalidRoute{err: "Path went through the same channel twice".to_owned()}));
+                                       continue 'path_check;
+                               }
+                       }
                        total_value += path.final_value_msat();
                        path_errs.push(Ok(()));
                }
@@ -1597,11 +1609,12 @@ impl OutboundPayments {
                #[cfg(test)]
                let DecodedOnionFailure {
                        network_update, short_channel_id, payment_failed_permanently, onion_error_code,
-                       onion_error_data
+                       onion_error_data, failed_within_blinded_path
                } = onion_error.decode_onion_failure(secp_ctx, logger, &source);
                #[cfg(not(test))]
-               let DecodedOnionFailure { network_update, short_channel_id, payment_failed_permanently } =
-                       onion_error.decode_onion_failure(secp_ctx, logger, &source);
+               let DecodedOnionFailure {
+                       network_update, short_channel_id, payment_failed_permanently, failed_within_blinded_path
+               } = onion_error.decode_onion_failure(secp_ctx, logger, &source);
 
                let payment_is_probe = payment_is_probe(payment_hash, &payment_id, probing_cookie_secret);
                let mut session_priv_bytes = [0; 32];
@@ -1640,6 +1653,12 @@ impl OutboundPayments {
                                // next-hop is needlessly blaming us!
                                payment.get_mut().insert_previously_failed_scid(scid);
                        }
+                       if failed_within_blinded_path {
+                               debug_assert!(short_channel_id.is_none());
+                               if let Some(bt) = &path.blinded_tail {
+                                       payment.get_mut().insert_previously_failed_blinded_path(&bt);
+                               } else { debug_assert!(false); }
+                       }
 
                        if payment_is_probe || !is_retryable_now || payment_failed_permanently {
                                let reason = if payment_failed_permanently {