Skip to content

Commit fcf4c32

Browse files
Support sending payments with a retry strategy in ChannelManager
1 parent 508363f commit fcf4c32

File tree

2 files changed

+74
-10
lines changed

2 files changed

+74
-10
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ use crate::ln::msgs;
5151
use crate::ln::onion_utils;
5252
use crate::ln::onion_utils::HTLCFailReason;
5353
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError, MAX_VALUE_MSAT};
54-
pub use crate::ln::outbound_payment::PaymentSendFailure;
54+
pub use crate::ln::outbound_payment::{PaymentSendFailure, Retry};
5555
#[cfg(test)]
5656
use crate::ln::outbound_payment;
57-
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, Retry};
57+
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment};
5858
use crate::ln::wire::Encode;
5959
use crate::chain::keysinterface::{Sign, KeysInterface, KeysManager, Recipient};
6060
use crate::util::config::{UserConfig, ChannelConfig};
@@ -2380,6 +2380,18 @@ where
23802380
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
23812381
}
23822382

2383+
/// Similar to [`ChannelManager::send_payment`], but will automatically find a route based on
2384+
/// `route_params` and retry failed payment paths based on `retry_strategy`.
2385+
pub fn send_payment_with_retry(&self, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<(), PaymentSendFailure> {
2386+
let best_block_height = self.best_block.read().unwrap().height();
2387+
self.pending_outbound_payments
2388+
.send_payment(payment_hash, payment_secret, payment_id, retry_strategy, route_params,
2389+
&self.router, self.list_usable_channels(), self.compute_inflight_htlcs(),
2390+
&self.keys_manager, best_block_height, &self.logger,
2391+
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
2392+
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
2393+
}
2394+
23832395
#[cfg(test)]
23842396
fn test_send_payment_internal(&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]>) -> Result<(), PaymentSendFailure> {
23852397
let best_block_height = self.best_block.read().unwrap().height();
@@ -2418,7 +2430,7 @@ where
24182430
fn retry_payment_auto_route(&self, payment_id: PaymentId, route_params: RouteParameters) -> Result<(), PaymentSendFailure> {
24192431
let best_block_height = self.best_block.read().unwrap().height();
24202432
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,
2421-
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
2433+
&|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
24222434
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
24232435
}
24242436

lightning/src/ln/outbound_payment.rs

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,57 @@ impl OutboundPayments {
387387
}
388388
}
389389

390+
pub(super) fn send_payment<R: Deref, K: Deref, L: Deref, F>(&self, payment_hash: PaymentHash,
391+
payment_secret: &Option<PaymentSecret>, payment_id: PaymentId, retry_strategy: Retry,
392+
route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
393+
inflight_htlcs: InFlightHtlcs, keys_manager: &K, best_block_height: u32, logger: &L,
394+
send_payment_along_path: F)
395+
-> Result<(), PaymentSendFailure>
396+
where
397+
R::Target: Router,
398+
K::Target: KeysInterface,
399+
F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
400+
u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
401+
L::Target: Logger,
402+
{
403+
#[cfg(feature = "std")] {
404+
if has_expired(&route_params) {
405+
log_trace!(logger, "Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0));
406+
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { err: format!("Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0))}));
407+
}
408+
}
409+
410+
let route = router.find_route(
411+
&keys_manager.get_node_id(Recipient::Node).unwrap(), &route_params,
412+
Some(&first_hops.iter().collect::<Vec<_>>()), &inflight_htlcs
413+
).map_err(|e| PaymentSendFailure::ParameterError(APIError::APIMisuseError {
414+
err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
415+
}))?;
416+
417+
log_trace!(logger, "Attempting to send payment with id {} and hash {} over route {}", log_bytes!(payment_id.0), log_bytes!(payment_hash.0), log_route!(route));
418+
let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, &route, retry_strategy, keys_manager, best_block_height)?;
419+
match self.send_payment_internal(&route, payment_hash, payment_secret, None, payment_id, None, onion_session_privs, keys_manager, best_block_height, &send_payment_along_path) {
420+
Err(PaymentSendFailure::AllFailedResendSafe(_)) => {
421+
self.retry_payment(payment_id, route_params, router, first_hops, inflight_htlcs, keys_manager, best_block_height, &send_payment_along_path)
422+
},
423+
Err(PaymentSendFailure::PartialFailure { failed_paths_retry, .. }) => {
424+
if let Some(retry) = failed_paths_retry {
425+
// Some paths were sent, even if we failed to send the full MPP value our recipient may
426+
// misbehave and claim the funds, at which point we have to consider the payment sent, so
427+
// return `Ok()` here, ignoring any retry errors.
428+
let _ = self.retry_payment(payment_id, retry, router, first_hops, inflight_htlcs, keys_manager, best_block_height, &send_payment_along_path);
429+
Ok(())
430+
} else {
431+
// This may happen if we send a payment and some paths fail, but only due to a temporary
432+
// monitor failure or the like, implying they're really in-flight, but we haven't sent the
433+
// initial HTLC-Add messages yet.
434+
Ok(())
435+
}
436+
},
437+
res => res,
438+
}
439+
}
440+
390441
pub(super) fn send_payment_with_route<K: Deref, F>(&self, route: &Route, payment_hash: PaymentHash,
391442
payment_secret: &Option<PaymentSecret>, payment_id: PaymentId, keys_manager: &K,
392443
best_block_height: u32, send_payment_along_path: F)
@@ -397,7 +448,7 @@ impl OutboundPayments {
397448
u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
398449
{
399450
let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, route, Retry::Attempts(0), keys_manager, best_block_height)?;
400-
self.send_payment_internal(route, payment_hash, payment_secret, None, payment_id, None, onion_session_privs, keys_manager, best_block_height, send_payment_along_path)
451+
self.send_payment_internal(route, payment_hash, payment_secret, None, payment_id, None, onion_session_privs, keys_manager, best_block_height, &send_payment_along_path)
401452
}
402453

403454
pub(super) fn send_spontaneous_payment<K: Deref, F>(&self, route: &Route,
@@ -416,7 +467,7 @@ impl OutboundPayments {
416467
let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
417468
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), keys_manager, best_block_height)?;
418469

419-
match self.send_payment_internal(route, payment_hash, &None, Some(preimage), payment_id, None, onion_session_privs, keys_manager, best_block_height, send_payment_along_path) {
470+
match self.send_payment_internal(route, payment_hash, &None, Some(preimage), payment_id, None, onion_session_privs, keys_manager, best_block_height, &send_payment_along_path) {
420471
Ok(()) => Ok(payment_hash),
421472
Err(e) => Err(e)
422473
}
@@ -425,7 +476,7 @@ impl OutboundPayments {
425476
pub(super) fn retry_payment<R: Deref, K: Deref, F>(&self, payment_id: PaymentId,
426477
route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
427478
inflight_htlcs: InFlightHtlcs, keys_manager: &K, best_block_height: u32,
428-
send_payment_along_path: F) -> Result<(), PaymentSendFailure>
479+
send_payment_along_path: &F) -> Result<(), PaymentSendFailure>
429480
where
430481
R::Target: Router,
431482
K::Target: KeysInterface,
@@ -456,11 +507,12 @@ impl OutboundPayments {
456507
err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
457508
}))?;
458509

459-
match self.retry_payment_with_route(&route, payment_id, keys_manager, best_block_height, &send_payment_along_path) {
510+
match self.retry_payment_with_route(&route, payment_id, keys_manager, best_block_height, send_payment_along_path) {
460511
Err(PaymentSendFailure::AllFailedResendSafe(_)) => {
461512
self.retry_payment(payment_id, route_params, router, first_hops, inflight_htlcs, keys_manager, best_block_height, send_payment_along_path)
462513
},
463514
Err(PaymentSendFailure::PartialFailure { failed_paths_retry: Some(retry), .. }) => {
515+
// Always return Ok for the same reason as noted in send_payment.
464516
let _ = self.retry_payment(payment_id, retry, router, first_hops, inflight_htlcs, keys_manager, best_block_height, send_payment_along_path);
465517
Ok(())
466518
},
@@ -557,7 +609,7 @@ impl OutboundPayments {
557609
let route = Route { paths: vec![hops], payment_params: None };
558610
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), keys_manager, best_block_height)?;
559611

560-
match self.send_payment_internal(&route, payment_hash, &None, None, payment_id, None, onion_session_privs, keys_manager, best_block_height, send_payment_along_path) {
612+
match self.send_payment_internal(&route, payment_hash, &None, None, payment_id, None, onion_session_privs, keys_manager, best_block_height, &send_payment_along_path) {
561613
Ok(()) => Ok((payment_hash, payment_id)),
562614
Err(e) => Err(e)
563615
}
@@ -601,7 +653,7 @@ impl OutboundPayments {
601653
(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
602654
keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
603655
onion_session_privs: Vec<[u8; 32]>, keys_manager: &K, best_block_height: u32,
604-
send_payment_along_path: F) -> Result<(), PaymentSendFailure>
656+
send_payment_along_path: &F) -> Result<(), PaymentSendFailure>
605657
where
606658
K::Target: KeysInterface,
607659
F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
@@ -724,7 +776,7 @@ impl OutboundPayments {
724776
{
725777
self.send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
726778
recv_value_msat, onion_session_privs, keys_manager, best_block_height,
727-
send_payment_along_path)
779+
&send_payment_along_path)
728780
}
729781

730782
pub(super) fn claim_htlc<L: Deref>(&self, payment_id: PaymentId,

0 commit comments

Comments
 (0)