Skip to content

Commit 8843bde

Browse files
Retry htlcs in process_pending_htlc_forwards
1 parent b9815db commit 8843bde

File tree

2 files changed

+80
-3
lines changed

2 files changed

+80
-3
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,6 +2410,16 @@ where
24102410
pub fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
24112411
let best_block_height = self.best_block.read().unwrap().height();
24122412
self.pending_outbound_payments.retry_payment_with_route(route, payment_id, &self.keys_manager, best_block_height,
2413+
&|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
2414+
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
2415+
}
2416+
2417+
/// Similar to [`ChannelManager::retry_payment`], but will automatically find a route based on
2418+
/// `route_params`. If paths fail then additional retries may be attempted, based on the retry
2419+
/// strategy that was specified on initial send.
2420+
fn retry_payment_auto_route(&self, payment_id: PaymentId, route_params: RouteParameters) -> Result<(), PaymentSendFailure> {
2421+
let best_block_height = self.best_block.read().unwrap().height();
2422+
self.pending_outbound_payments.retry_payment(payment_id, route_params, &self.router, self.list_usable_channels(), self.compute_inflight_htlcs(), &self.keys_manager, best_block_height,
24132423
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
24142424
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
24152425
}
@@ -3163,6 +3173,17 @@ where
31633173
}
31643174
}
31653175
}
3176+
{
3177+
let mut retryable_htlcs = Vec::new();
3178+
mem::swap(&mut retryable_htlcs, &mut self.pending_outbound_payments.retryable_htlcs.lock().unwrap());
3179+
for (payment_id, params) in retryable_htlcs {
3180+
// TODO: do we need to generate a PendingHTLCsForwardable event?
3181+
if let Err(e) = self.retry_payment_auto_route(payment_id, params) {
3182+
log_trace!(self.logger, "Errored retrying payment, marking as abandoned: {:?}", e);
3183+
self.abandon_payment(payment_id);
3184+
}
3185+
}
3186+
}
31663187

31673188
for (htlc_source, payment_hash, failure_reason, destination) in failed_forwards.drain(..) {
31683189
self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);

lightning/src/ln/outbound_payment.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
1515

1616
use crate::chain::keysinterface::{EntropySource, KeysInterface, NodeSigner, Recipient};
1717
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
18-
use crate::ln::channelmanager::{HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, MIN_HTLC_RELAY_HOLDING_CELL_MILLIS, PaymentId};
18+
use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, MIN_HTLC_RELAY_HOLDING_CELL_MILLIS, PaymentId};
1919
use crate::ln::msgs::DecodeError;
2020
use crate::ln::onion_utils::HTLCFailReason;
21-
use crate::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters, RoutePath};
21+
use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router};
2222
use crate::util::errors::APIError;
2323
use crate::util::events;
2424
use crate::util::logger::Logger;
@@ -233,6 +233,16 @@ impl Retry {
233233
}
234234
}
235235

236+
#[cfg(feature = "std")]
237+
pub(crate) fn has_expired(route_params: &RouteParameters) -> bool {
238+
if let Some(expiry_time) = route_params.payment_params.expiry_time {
239+
if let Ok(elapsed) = std::time::SystemTime::UNIX_EPOCH.elapsed() {
240+
return elapsed > core::time::Duration::from_secs(expiry_time)
241+
}
242+
}
243+
false
244+
}
245+
236246
pub(crate) type PaymentAttempts = PaymentAttemptsUsingTime<ConfiguredTime>;
237247

238248
/// Storing minimal payment attempts information required for determining if a outbound payment can
@@ -412,9 +422,55 @@ impl OutboundPayments {
412422
}
413423
}
414424

425+
pub(super) fn retry_payment<R: Deref, K: Deref, F>(&self, payment_id: PaymentId,
426+
route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
427+
inflight_htlcs: InFlightHtlcs, keys_manager: &K, best_block_height: u32,
428+
send_payment_along_path: F) -> Result<(), PaymentSendFailure>
429+
where
430+
R::Target: Router,
431+
K::Target: KeysInterface,
432+
F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
433+
u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
434+
{
435+
#[cfg(feature = "std")] {
436+
if has_expired(&route_params) {
437+
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
438+
err: format!("Invoice expired for payment id {}; not retrying", log_bytes!(payment_id.0)),
439+
}))
440+
}
441+
}
442+
443+
self.pending_outbound_payments.lock().unwrap().get_mut(&payment_id).map(|pmt| pmt.increment_attempts());
444+
if !self.pending_outbound_payments.lock().unwrap().get(&payment_id)
445+
.map_or(false, |payment| payment.is_retryable())
446+
{
447+
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
448+
err: format!("Payment {} is not retryable", log_bytes!(payment_id.0)),
449+
}))
450+
}
451+
452+
let route = router.find_route(
453+
&keys_manager.get_node_id(Recipient::Node).unwrap(), &route_params,
454+
Some(&first_hops.iter().collect::<Vec<_>>()), &inflight_htlcs
455+
).map_err(|e| PaymentSendFailure::ParameterError(APIError::APIMisuseError {
456+
err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
457+
}))?;
458+
459+
match self.retry_payment_with_route(&route, payment_id, keys_manager, best_block_height, &send_payment_along_path) {
460+
Err(PaymentSendFailure::AllFailedResendSafe(_)) => {
461+
self.retry_payment(payment_id, route_params, router, first_hops, inflight_htlcs, keys_manager, best_block_height, send_payment_along_path)
462+
},
463+
Err(PaymentSendFailure::PartialFailure { failed_paths_retry: Some(retry), .. }) => {
464+
let _ = self.retry_payment(payment_id, retry, router, first_hops, inflight_htlcs, keys_manager, best_block_height, send_payment_along_path);
465+
Ok(())
466+
},
467+
res => res,
468+
}
469+
}
470+
415471
pub(super) fn retry_payment_with_route<K: Deref, F>(
416472
&self, route: &Route, payment_id: PaymentId, keys_manager: &K, best_block_height: u32,
417-
send_payment_along_path: F
473+
send_payment_along_path: &F
418474
) -> Result<(), PaymentSendFailure>
419475
where
420476
K::Target: KeysInterface,

0 commit comments

Comments
 (0)