Remove stale comment in test
[rust-lightning] / lightning / src / ln / outbound_payment.rs
index 4b6278d194904419dcb189ffdb1c125215f3e7c4..8867b2e96ae14c3e75bb960d5dd9fa97ed10d746 100644 (file)
@@ -378,6 +378,25 @@ impl OutboundPayments {
                }
        }
 
+       pub(super) fn send_payment<R: Deref, ES: Deref, NS: Deref, F>(
+               &self, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId,
+               retry_strategy: Retry, route_params: RouteParameters, router: &R,
+               first_hops: Vec<ChannelDetails>, inflight_htlcs: InFlightHtlcs, entropy_source: &ES,
+               node_signer: &NS, best_block_height: u32, send_payment_along_path: F
+       ) -> Result<(), PaymentSendFailure>
+       where
+               R::Target: Router,
+               ES::Target: EntropySource,
+               NS::Target: NodeSigner,
+               F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
+                        u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
+       {
+               self.pay_internal(payment_id, Some((payment_hash, payment_secret, retry_strategy)),
+                       route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer,
+                       best_block_height, &send_payment_along_path)
+                       .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
+       }
+
        pub(super) fn send_payment_with_route<ES: Deref, NS: Deref, F>(
                &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
                payment_id: PaymentId, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
@@ -390,8 +409,8 @@ impl OutboundPayments {
                   u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
        {
                let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
-               self.send_payment_internal(route, payment_hash, payment_secret, None, payment_id, None,
-                       onion_session_privs, node_signer, best_block_height, send_payment_along_path)
+               self.pay_route_internal(route, payment_hash, payment_secret, None, payment_id, None,
+                       onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
                        .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
        }
 
@@ -412,7 +431,7 @@ impl OutboundPayments {
                let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
                let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
 
-               match self.send_payment_internal(route, payment_hash, &None, Some(preimage), payment_id, None, onion_session_privs, node_signer, best_block_height, send_payment_along_path) {
+               match self.pay_route_internal(route, payment_hash, &None, Some(preimage), payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
                        Ok(()) => Ok(payment_hash),
                        Err(e) => {
                                self.remove_outbound_if_all_failed(payment_id, &e);
@@ -451,7 +470,7 @@ impl OutboundPayments {
                        }
                        if let Some((payment_id, route_params)) = retry_id_route_params {
                                core::mem::drop(outbounds);
-                               if let Err(e) = self.pay_internal(payment_id, route_params, router, first_hops(), inflight_htlcs(), entropy_source, node_signer, best_block_height, &send_payment_along_path) {
+                               if let Err(e) = self.pay_internal(payment_id, None, route_params, router, first_hops(), inflight_htlcs(), entropy_source, node_signer, best_block_height, &send_payment_along_path) {
                                        log_trace!(logger, "Errored retrying payment: {:?}", e);
                                }
                        } else { break }
@@ -459,9 +478,11 @@ impl OutboundPayments {
        }
 
        fn pay_internal<R: Deref, NS: Deref, ES: Deref, F>(
-               &self, payment_id: PaymentId, route_params: RouteParameters, router: &R,
-               first_hops: Vec<ChannelDetails>, inflight_htlcs: InFlightHtlcs, entropy_source: &ES,
-               node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
+               &self, payment_id: PaymentId,
+               initial_send_info: Option<(PaymentHash, &Option<PaymentSecret>, Retry)>,
+               route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
+               inflight_htlcs: InFlightHtlcs, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
+               send_payment_along_path: &F
        ) -> Result<(), PaymentSendFailure>
        where
                R::Target: Router,
@@ -485,7 +506,12 @@ impl OutboundPayments {
                        err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
                }))?;
 
-               let res = self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path);
+               let res = if let Some((payment_hash, payment_secret, retry_strategy)) = initial_send_info {
+                       let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, &route, retry_strategy, Some(route_params.clone()), entropy_source, best_block_height)?;
+                       self.pay_route_internal(&route, payment_hash, payment_secret, None, payment_id, None, onion_session_privs, node_signer, best_block_height, send_payment_along_path)
+               } else {
+                       self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path)
+               };
                match res {
                        Err(PaymentSendFailure::AllFailedResendSafe(_)) => {
                                let mut outbounds = self.pending_outbound_payments.lock().unwrap();
@@ -496,7 +522,7 @@ impl OutboundPayments {
                                        } else { return res }
                                } else { return res }
                                core::mem::drop(outbounds);
-                               self.pay_internal(payment_id, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path)
+                               self.pay_internal(payment_id, None, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path)
                        },
                        Err(PaymentSendFailure::PartialFailure { failed_paths_retry: Some(retry), results, .. }) => {
                                let mut outbounds = self.pending_outbound_payments.lock().unwrap();
@@ -511,7 +537,7 @@ impl OutboundPayments {
                                // Some paths were sent, even if we failed to send the full MPP value our recipient may
                                // misbehave and claim the funds, at which point we have to consider the payment sent, so
                                // return `Ok()` here, ignoring any retry errors.
-                               let _ = self.pay_internal(payment_id, retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path);
+                               let _ = self.pay_internal(payment_id, None, retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path);
                                Ok(())
                        },
                        Err(PaymentSendFailure::PartialFailure { failed_paths_retry: None, .. }) => {
@@ -591,7 +617,7 @@ impl OutboundPayments {
                                        })),
                        }
                };
-               self.send_payment_internal(route, payment_hash, &payment_secret, None, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, send_payment_along_path)
+               self.pay_route_internal(route, payment_hash, &payment_secret, None, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
        }
 
        pub(super) fn send_probe<ES: Deref, NS: Deref, F>(
@@ -617,7 +643,7 @@ impl OutboundPayments {
                let route = Route { paths: vec![hops], payment_params: None };
                let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
 
-               match self.send_payment_internal(&route, payment_hash, &None, None, payment_id, None, onion_session_privs, node_signer, best_block_height, send_payment_along_path) {
+               match self.pay_route_internal(&route, payment_hash, &None, None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
                        Ok(()) => Ok((payment_hash, payment_id)),
                        Err(e) => {
                                self.remove_outbound_if_all_failed(payment_id, &e);
@@ -670,11 +696,11 @@ impl OutboundPayments {
                }
        }
 
-       fn send_payment_internal<NS: Deref, F>(
+       fn pay_route_internal<NS: Deref, F>(
                &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
                keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
                onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32,
-               send_payment_along_path: F
+               send_payment_along_path: &F
        ) -> Result<(), PaymentSendFailure>
        where
                NS::Target: NodeSigner,
@@ -787,9 +813,9 @@ impl OutboundPayments {
                F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
                   u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
        {
-               self.send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
+               self.pay_route_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
                        recv_value_msat, onion_session_privs, node_signer, best_block_height,
-                       send_payment_along_path)
+                       &send_payment_along_path)
                        .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
        }
 
@@ -1088,3 +1114,99 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
                (2, payment_hash, required),
        },
 );
+
+#[cfg(test)]
+mod tests {
+       use bitcoin::blockdata::constants::genesis_block;
+       use bitcoin::network::constants::Network;
+       use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
+
+       use crate::ln::PaymentHash;
+       use crate::ln::channelmanager::{PaymentId, PaymentSendFailure};
+       use crate::ln::msgs::{ErrorAction, LightningError};
+       use crate::ln::outbound_payment::{OutboundPayments, Retry};
+       use crate::routing::gossip::NetworkGraph;
+       use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters};
+       use crate::sync::Arc;
+       use crate::util::errors::APIError;
+       use crate::util::test_utils;
+
+       #[test]
+       #[cfg(feature = "std")]
+       fn fails_paying_after_expiration() {
+               do_fails_paying_after_expiration(false);
+               do_fails_paying_after_expiration(true);
+       }
+       #[cfg(feature = "std")]
+       fn do_fails_paying_after_expiration(on_retry: bool) {
+               let outbound_payments = OutboundPayments::new();
+               let logger = test_utils::TestLogger::new();
+               let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+               let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
+               let router = test_utils::TestRouter::new(network_graph);
+               let secp_ctx = Secp256k1::new();
+               let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
+
+               let past_expiry_time = std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap().as_secs() - 2;
+               let payment_params = PaymentParameters::from_node_id(
+                       PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()))
+                       .with_expiry_time(past_expiry_time);
+               let expired_route_params = RouteParameters {
+                       payment_params,
+                       final_value_msat: 0,
+                       final_cltv_expiry_delta: 0,
+               };
+               let err = if on_retry {
+                       outbound_payments.pay_internal(
+                               PaymentId([0; 32]), None, expired_route_params, &&router, vec![], InFlightHtlcs::new(),
+                               &&keys_manager, &&keys_manager, 0, &|_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+               } else {
+                       outbound_payments.send_payment(
+                               PaymentHash([0; 32]), &None, PaymentId([0; 32]), Retry::Attempts(0), expired_route_params,
+                               &&router, vec![], InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, |_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+               };
+               if let PaymentSendFailure::ParameterError(APIError::APIMisuseError { err }) = err {
+                       assert!(err.contains("Invoice expired"));
+               } else { panic!("Unexpected error"); }
+       }
+
+       #[test]
+       fn find_route_error() {
+               do_find_route_error(false);
+               do_find_route_error(true);
+       }
+       fn do_find_route_error(on_retry: bool) {
+               let outbound_payments = OutboundPayments::new();
+               let logger = test_utils::TestLogger::new();
+               let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+               let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
+               let router = test_utils::TestRouter::new(network_graph);
+               let secp_ctx = Secp256k1::new();
+               let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
+
+               router.expect_find_route(Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }));
+
+               let payment_params = PaymentParameters::from_node_id(
+                       PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()));
+               let route_params = RouteParameters {
+                       payment_params,
+                       final_value_msat: 0,
+                       final_cltv_expiry_delta: 0,
+               };
+               let err = if on_retry {
+                       outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), None, PaymentId([0; 32]),
+                       &Route { paths: vec![], payment_params: None }, Retry::Attempts(1), Some(route_params.clone()),
+                       &&keys_manager, 0).unwrap();
+                       outbound_payments.pay_internal(
+                               PaymentId([0; 32]), None, route_params, &&router, vec![], InFlightHtlcs::new(),
+                               &&keys_manager, &&keys_manager, 0, &|_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+               } else {
+                       outbound_payments.send_payment(
+                               PaymentHash([0; 32]), &None, PaymentId([0; 32]), Retry::Attempts(0), route_params,
+                               &&router, vec![], InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, |_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+               };
+               if let PaymentSendFailure::ParameterError(APIError::APIMisuseError { err }) = err {
+                       assert!(err.contains("Failed to find a route"));
+               } else { panic!("Unexpected error"); }
+       }
+}