Skip to content

Commit 8c4590f

Browse files
Support sending payments with a retry strategy in ChannelManager
1 parent 8843bde commit 8c4590f

File tree

2 files changed

+72
-8
lines changed

2 files changed

+72
-8
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2382,6 +2382,18 @@ where
23822382
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
23832383
}
23842384

2385+
/// Similar to [`ChannelManager::send_payment`], but will automatically find a route based on
2386+
/// `route_params` and retry failed payment paths based on `retry_strategy`.
2387+
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> {
2388+
let best_block_height = self.best_block.read().unwrap().height();
2389+
self.pending_outbound_payments
2390+
.send_payment(payment_hash, payment_secret, payment_id, retry_strategy, route_params,
2391+
&self.router, self.list_usable_channels(), self.compute_inflight_htlcs(),
2392+
&self.keys_manager, best_block_height, &self.logger,
2393+
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
2394+
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
2395+
}
2396+
23852397
#[cfg(test)]
23862398
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> {
23872399
let best_block_height = self.best_block.read().unwrap().height();
@@ -2420,7 +2432,7 @@ where
24202432
fn retry_payment_auto_route(&self, payment_id: PaymentId, route_params: RouteParameters) -> Result<(), PaymentSendFailure> {
24212433
let best_block_height = self.best_block.read().unwrap().height();
24222434
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,
2423-
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
2435+
&|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
24242436
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
24252437
}
24262438

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>(
391442
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
392443
payment_id: PaymentId, keys_manager: &K, 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>(
@@ -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
},
@@ -559,7 +611,7 @@ impl OutboundPayments {
559611
let route = Route { paths: vec![hops], payment_params: None };
560612
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), keys_manager, best_block_height)?;
561613

562-
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) {
614+
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) {
563615
Ok(()) => Ok((payment_hash, payment_id)),
564616
Err(e) => Err(e)
565617
}
@@ -603,7 +655,7 @@ impl OutboundPayments {
603655
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
604656
keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
605657
onion_session_privs: Vec<[u8; 32]>, keys_manager: &K, best_block_height: u32,
606-
send_payment_along_path: F) -> Result<(), PaymentSendFailure>
658+
send_payment_along_path: &F) -> Result<(), PaymentSendFailure>
607659
where
608660
K::Target: KeysInterface,
609661
F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
@@ -726,7 +778,7 @@ impl OutboundPayments {
726778
{
727779
self.send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
728780
recv_value_msat, onion_session_privs, keys_manager, best_block_height,
729-
send_payment_along_path)
781+
&send_payment_along_path)
730782
}
731783

732784
pub(super) fn claim_htlc<L: Deref>(

0 commit comments

Comments
 (0)