Skip to content

Commit d5a82c2

Browse files
Support sending payments with a retry strategy in ChannelManager
1 parent c50d1c0 commit d5a82c2

File tree

2 files changed

+58
-14
lines changed

2 files changed

+58
-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, &self.logger,
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: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,31 @@ impl OutboundPayments {
378378
}
379379
}
380380

381+
pub(super) fn send_payment<R: Deref, ES: Deref, NS: Deref, L: 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, logger: &L, 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+
L::Target: Logger,
394+
{
395+
self.pay_internal(payment_id, Some((payment_hash, payment_secret, retry_strategy)),
396+
route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer,
397+
best_block_height, &send_payment_along_path)
398+
.map_err(|e| { // TODO: use inspect_err instead when it's within MSRV
399+
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
400+
self.all_failed_remove_outbound(payment_id);
401+
}
402+
e
403+
})
404+
}
405+
381406
pub(super) fn send_payment_with_route<ES: Deref, NS: Deref, F>(
382407
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
383408
payment_id: PaymentId, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
@@ -391,7 +416,7 @@ impl OutboundPayments {
391416
{
392417
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)?;
393418
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)
419+
onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
395420
.map_err(|e| { // TODO: use inspect_err instead when it's within MSRV
396421
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
397422
self.all_failed_remove_outbound(payment_id);
@@ -417,7 +442,7 @@ impl OutboundPayments {
417442
let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
418443
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
419444

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) {
445+
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) {
421446
Ok(()) => Ok(payment_hash),
422447
Err(e) => {
423448
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
@@ -457,17 +482,19 @@ impl OutboundPayments {
457482
}
458483
if let Some((payment_id, route_params)) = retry_id_route_params {
459484
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) {
485+
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) {
461486
log_trace!(logger, "Errored retrying payment: {:?}", e);
462487
}
463488
} else { break }
464489
}
465490
}
466491

467492
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
493+
&self, payment_id: PaymentId,
494+
initial_send_info: Option<(PaymentHash, &Option<PaymentSecret>, Retry)>,
495+
route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
496+
inflight_htlcs: InFlightHtlcs, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
497+
send_payment_along_path: &F
471498
) -> Result<(), PaymentSendFailure>
472499
where
473500
R::Target: Router,
@@ -491,7 +518,12 @@ impl OutboundPayments {
491518
err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
492519
}))?;
493520

494-
let res = self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path);
521+
let res = if let Some((payment_hash, payment_secret, retry_strategy)) = initial_send_info {
522+
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)?;
523+
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)
524+
} else {
525+
self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path)
526+
};
495527
match res {
496528
Err(PaymentSendFailure::AllFailedResendSafe(_)) | Err(PaymentSendFailure::PartialFailure { .. }) => {
497529
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
@@ -506,14 +538,14 @@ impl OutboundPayments {
506538
}
507539
match res {
508540
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)
541+
self.pay_internal(payment_id, None, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path)
510542
},
511543
Err(PaymentSendFailure::PartialFailure { failed_paths_retry, .. }) => {
512544
if let Some(retry) = failed_paths_retry {
513545
// Some paths were sent, even if we failed to send the full MPP value our recipient may
514546
// misbehave and claim the funds, at which point we have to consider the payment sent, so
515547
// 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);
548+
let _ = self.pay_internal(payment_id, None, retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path);
517549
Ok(())
518550
} else {
519551
// This may happen if we send a payment and some paths fail, but only due to a temporary
@@ -593,7 +625,7 @@ impl OutboundPayments {
593625
})),
594626
}
595627
};
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)
628+
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)
597629
}
598630

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

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) {
654+
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) {
623655
Ok(()) => Ok((payment_hash, payment_id)),
624656
Err(e) => {
625657
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
@@ -678,7 +710,7 @@ impl OutboundPayments {
678710
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
679711
keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
680712
onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32,
681-
send_payment_along_path: F
713+
send_payment_along_path: &F
682714
) -> Result<(), PaymentSendFailure>
683715
where
684716
NS::Target: NodeSigner,
@@ -793,7 +825,7 @@ impl OutboundPayments {
793825
{
794826
self.send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
795827
recv_value_msat, onion_session_privs, node_signer, best_block_height,
796-
send_payment_along_path)
828+
&send_payment_along_path)
797829
.map_err(|e| { // TODO: use inspect_err instead when it's within MSRV
798830
if let PaymentSendFailure::AllFailedResendSafe(_) = e {
799831
self.all_failed_remove_outbound(payment_id);

0 commit comments

Comments
 (0)