+ Err(e) => {
+ self.remove_outbound_if_all_failed(payment_id, &e);
+ Err(e)
+ }
+ }
+ }
+
+ pub(super) fn check_retry_payments<R: Deref, ES: Deref, NS: Deref, SP, IH, FH, L: Deref>(
+ &self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
+ best_block_height: u32, logger: &L, send_payment_along_path: SP,
+ )
+ where
+ R::Target: Router,
+ ES::Target: EntropySource,
+ NS::Target: NodeSigner,
+ SP: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
+ u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
+ IH: Fn() -> InFlightHtlcs,
+ FH: Fn() -> Vec<ChannelDetails>,
+ L::Target: Logger,
+ {
+ loop {
+ let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+ let mut retry_id_route_params = None;
+ for (pmt_id, pmt) in outbounds.iter_mut() {
+ if pmt.is_retryable_now() {
+ if let PendingOutboundPayment::Retryable { pending_amt_msat, total_msat, route_params: Some(params), .. } = pmt {
+ if pending_amt_msat < total_msat {
+ retry_id_route_params = Some((*pmt_id, params.clone()));
+ pmt.increment_attempts();
+ break
+ }
+ }
+ }
+ }
+ if let Some((payment_id, route_params)) = retry_id_route_params {
+ core::mem::drop(outbounds);
+ 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 }
+ }
+ }
+
+ fn pay_internal<R: Deref, NS: Deref, ES: Deref, 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,
+ ES::Target: EntropySource,
+ NS::Target: NodeSigner,
+ F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
+ u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
+ {
+ #[cfg(feature = "std")] {
+ if has_expired(&route_params) {
+ return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
+ err: format!("Invoice expired for payment id {}", log_bytes!(payment_id.0)),
+ }))
+ }
+ }
+
+ let route = router.find_route(
+ &node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
+ Some(&first_hops.iter().collect::<Vec<_>>()), &inflight_htlcs
+ ).map_err(|e| PaymentSendFailure::ParameterError(APIError::APIMisuseError {
+ err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
+ }))?;
+
+ 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();
+ if let Some(payment) = outbounds.get_mut(&payment_id) {
+ let retryable = payment.is_retryable_now();
+ if retryable {
+ payment.increment_attempts();
+ } else { return res }
+ } else { return res }
+ core::mem::drop(outbounds);
+ 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();
+ if let Some(payment) = outbounds.get_mut(&payment_id) {
+ let retryable = payment.is_retryable_now();
+ if retryable {
+ payment.increment_attempts();
+ } else { return Err(PaymentSendFailure::PartialFailure { failed_paths_retry: Some(retry), results, payment_id }) }
+ } else { return Err(PaymentSendFailure::PartialFailure { failed_paths_retry: Some(retry), results, payment_id }) }
+ core::mem::drop(outbounds);
+
+ // 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, 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, .. }) => {
+ // This may happen if we send a payment and some paths fail, but only due to a temporary
+ // monitor failure or the like, implying they're really in-flight, but we haven't sent the
+ // initial HTLC-Add messages yet.
+ Ok(())
+ },
+ res => res,