+ let params = RouteParameters {
+ payee: Payee::for_keysend(pubkey),
+ final_value_msat: amount_msats,
+ final_cltv_expiry_delta,
+ };
+
+ let send_payment = |route: &Route| {
+ self.payer.send_spontaneous_payment(route, payment_preimage)
+ };
+ self.pay_internal(¶ms, payment_hash, send_payment)
+ .map_err(|e| { self.payment_cache.lock().unwrap().remove(&payment_hash); e })
+ }
+
+ fn pay_internal<F: FnOnce(&Route) -> Result<PaymentId, PaymentSendFailure> + Copy>(
+ &self, params: &RouteParameters, payment_hash: PaymentHash, send_payment: F,
+ ) -> Result<PaymentId, PaymentError> {
+ if has_expired(params) {
+ log_trace!(self.logger, "Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0));
+ return Err(PaymentError::Invoice("Invoice expired prior to send"));
+ }
+
+ let payer = self.payer.node_id();
+ let first_hops = self.payer.first_hops();
+ let route = self.router.find_route(
+ &payer, params, &payment_hash, Some(&first_hops.iter().collect::<Vec<_>>()),
+ &self.scorer.lock()
+ ).map_err(|e| PaymentError::Routing(e))?;
+
+ match send_payment(&route) {
+ Ok(payment_id) => Ok(payment_id),
+ Err(e) => match e {
+ PaymentSendFailure::ParameterError(_) => Err(e),
+ PaymentSendFailure::PathParameterError(_) => Err(e),
+ PaymentSendFailure::AllFailedRetrySafe(_) => {
+ let mut payment_cache = self.payment_cache.lock().unwrap();
+ let retry_count = payment_cache.get_mut(&payment_hash).unwrap();
+ if *retry_count >= self.retry_attempts.0 {
+ Err(e)
+ } else {
+ *retry_count += 1;
+ std::mem::drop(payment_cache);
+ Ok(self.pay_internal(params, payment_hash, send_payment)?)
+ }
+ },
+ PaymentSendFailure::PartialFailure { failed_paths_retry, payment_id, .. } => {
+ if let Some(retry_data) = failed_paths_retry {
+ // 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.retry_payment(payment_id, payment_hash, &retry_data);
+ Ok(payment_id)
+ } else {
+ // 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(payment_id)
+ }
+ },
+ },
+ }.map_err(|e| PaymentError::Sending(e))
+ }
+
+ fn retry_payment(
+ &self, payment_id: PaymentId, payment_hash: PaymentHash, params: &RouteParameters
+ ) -> Result<(), ()> {
+ let max_payment_attempts = self.retry_attempts.0 + 1;
+ let attempts = *self.payment_cache.lock().unwrap()
+ .entry(payment_hash)
+ .and_modify(|attempts| *attempts += 1)
+ .or_insert(1);
+
+ if attempts >= max_payment_attempts {
+ log_trace!(self.logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ return Err(());
+ }
+
+ if has_expired(params) {
+ log_trace!(self.logger, "Invoice expired for payment {}; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ return Err(());