Skip to content

Commit 560711b

Browse files
Support sending payments with a retry strategy in ChannelManager
1 parent 19c9ef3 commit 560711b

File tree

2 files changed

+57
-14
lines changed

2 files changed

+57
-14
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, No
4545
#[cfg(any(feature = "_test_utils", test))]
4646
use crate::ln::features::InvoiceFeatures;
4747
use crate::routing::gossip::NetworkGraph;
48-
use crate::routing::router::{DefaultRouter, InFlightHtlcs, PaymentParameters, Route, RouteHop, RoutePath, Router};
48+
use crate::routing::router::{DefaultRouter, InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router};
4949
use crate::routing::scoring::ProbabilisticScorer;
5050
use crate::ln::msgs;
5151
use crate::ln::onion_utils;
@@ -2446,6 +2446,18 @@ where
24462446
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
24472447
}
24482448

2449+
/// Similar to [`ChannelManager::send_payment`], but will automatically find a route based on
2450+
/// `route_params` and retry failed payment paths based on `retry_strategy`.
2451+
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> {
2452+
let best_block_height = self.best_block.read().unwrap().height();
2453+
self.pending_outbound_payments
2454+
.send_payment(payment_hash, payment_secret, payment_id, retry_strategy, route_params,
2455+
&self.router, self.list_usable_channels(), self.compute_inflight_htlcs(),
2456+
&self.entropy_source, &self.node_signer, best_block_height,
2457+
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
2458+
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
2459+
}
2460+
24492461
#[cfg(test)]
24502462
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> {
24512463
let best_block_height = self.best_block.read().unwrap().height();

lightning/src/ln/outbound_payment.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,30 @@ impl OutboundPayments {
378378
}
379379
}
380380

381+
pub(super) fn send_payment<R: Deref, ES: Deref, NS: Deref, F>(
382+
&self, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId,
383+
retry_strategy: Retry, route_params: RouteParameters, router: &R,
384+
first_hops: Vec<ChannelDetails>, inflight_htlcs: InFlightHtlcs, entropy_source: &ES,
385+
node_signer: &NS, best_block_height: u32, send_payment_along_path: F
386+
) -> Result<(), PaymentSendFailure>
387+
where
388+
R::Target: Router,
389+
ES::Target: EntropySource,
390+
NS::Target: NodeSigner,
391+
F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
392+
u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
393+
{
394+
self.pay_internal(payment_id, Some((payment_hash, payment_secret, retry_strategy)),
395+
route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer,
396+
best_block_height, &send_payment_along_path)
397+
.map_err(|e| { // TODO: use inspect_err instead when it's within MSRV
398+
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
399+
self.all_failed_remove_outbound(payment_id);
400+
}
401+
e
402+
})
403+
}
404+
381405
pub(super) fn send_payment_with_route<ES: Deref, NS: Deref, F>(
382406
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
383407
payment_id: PaymentId, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
@@ -391,7 +415,7 @@ impl OutboundPayments {
391415
{
392416
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)?;
393417
self.send_payment_internal(route, payment_hash, payment_secret, None, payment_id, None,
394-
onion_session_privs, node_signer, best_block_height, send_payment_along_path)
418+
onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
395419
.map_err(|e| { // TODO: use inspect_err instead when it's within MSRV
396420
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
397421
self.all_failed_remove_outbound(payment_id);
@@ -417,7 +441,7 @@ impl OutboundPayments {
417441
let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
418442
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
419443

420-
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) {
444+
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) {
421445
Ok(()) => Ok(payment_hash),
422446
Err(e) => {
423447
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
@@ -457,17 +481,19 @@ impl OutboundPayments {
457481
}
458482
if let Some((payment_id, route_params)) = retry_id_route_params {
459483
core::mem::drop(outbounds);
460-
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) {
484+
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) {
461485
log_trace!(logger, "Errored retrying payment: {:?}", e);
462486
}
463487
} else { break }
464488
}
465489
}
466490

467491
fn pay_internal<R: Deref, NS: Deref, ES: Deref, F>(
468-
&self, payment_id: PaymentId, route_params: RouteParameters, router: &R,
469-
first_hops: Vec<ChannelDetails>, inflight_htlcs: InFlightHtlcs, entropy_source: &ES,
470-
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
492+
&self, payment_id: PaymentId,
493+
initial_send_info: Option<(PaymentHash, &Option<PaymentSecret>, Retry)>,
494+
route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
495+
inflight_htlcs: InFlightHtlcs, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
496+
send_payment_along_path: &F
471497
) -> Result<(), PaymentSendFailure>
472498
where
473499
R::Target: Router,
@@ -491,7 +517,12 @@ impl OutboundPayments {
491517
err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
492518
}))?;
493519

494-
let res = self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path);
520+
let res = if let Some((payment_hash, payment_secret, retry_strategy)) = initial_send_info {
521+
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)?;
522+
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)
523+
} else {
524+
self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path)
525+
};
495526
match res {
496527
Err(PaymentSendFailure::AllFailedResendSafe(_)) | Err(PaymentSendFailure::PartialFailure { .. }) => {
497528
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
@@ -506,14 +537,14 @@ impl OutboundPayments {
506537
}
507538
match res {
508539
Err(PaymentSendFailure::AllFailedResendSafe(_)) => {
509-
self.pay_internal(payment_id, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path)
540+
self.pay_internal(payment_id, None, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path)
510541
},
511542
Err(PaymentSendFailure::PartialFailure { failed_paths_retry, .. }) => {
512543
if let Some(retry) = failed_paths_retry {
513544
// Some paths were sent, even if we failed to send the full MPP value our recipient may
514545
// misbehave and claim the funds, at which point we have to consider the payment sent, so
515546
// return `Ok()` here, ignoring any retry errors.
516-
let _ = self.pay_internal(payment_id, retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path);
547+
let _ = self.pay_internal(payment_id, None, retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path);
517548
Ok(())
518549
} else {
519550
// This may happen if we send a payment and some paths fail, but only due to a temporary
@@ -593,7 +624,7 @@ impl OutboundPayments {
593624
})),
594625
}
595626
};
596-
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)
627+
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)
597628
}
598629

599630
pub(super) fn send_probe<ES: Deref, NS: Deref, F>(
@@ -619,7 +650,7 @@ impl OutboundPayments {
619650
let route = Route { paths: vec![hops], payment_params: None };
620651
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
621652

622-
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) {
653+
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) {
623654
Ok(()) => Ok((payment_hash, payment_id)),
624655
Err(e) => {
625656
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
@@ -678,7 +709,7 @@ impl OutboundPayments {
678709
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
679710
keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
680711
onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32,
681-
send_payment_along_path: F
712+
send_payment_along_path: &F
682713
) -> Result<(), PaymentSendFailure>
683714
where
684715
NS::Target: NodeSigner,
@@ -793,7 +824,7 @@ impl OutboundPayments {
793824
{
794825
self.send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
795826
recv_value_msat, onion_session_privs, node_signer, best_block_height,
796-
send_payment_along_path)
827+
&send_payment_along_path)
797828
.map_err(|e| { // TODO: use inspect_err instead when it's within MSRV
798829
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
799830
self.all_failed_remove_outbound(payment_id);

0 commit comments

Comments
 (0)