Skip to content

Commit 150b609

Browse files
committed
Add a variant to PendingOutboundPayment for retries-exceeded
When a payer gives up trying to retry a payment, they don't know for sure what the current state of the event queue is. Specifically, they cannot be sure that there are not multiple additional `PaymentPathFailed` or even `PaymentSuccess` events pending which they will see later. Thus, they have a very hard time identifying whether a payment has truly failed (and informing the UI of that fact) or if it is still pending. See [1] for more information. In order to avoid this mess, we will resolve it here by having the payer give `ChannelManager` a bit more information - when they have given up on a payment - and using that to generate a `PaymentFailed` event when all paths have failed. This commit adds the neccessary storage and changes for the new state inside `ChannelManager` and a public method to mark a payment as failed, the next few commits will add the new `Event` and use the new features in our `PaymentRetrier`. [1] lightningdevkit#1164
1 parent ea89286 commit 150b609

File tree

1 file changed

+62
-6
lines changed

1 file changed

+62
-6
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,16 @@ pub(crate) enum PendingOutboundPayment {
454454
session_privs: HashSet<[u8; 32]>,
455455
payment_hash: Option<PaymentHash>,
456456
},
457+
/// When a payer gives up trying to retry a payment, they inform us, letting us generate a
458+
/// `PaymentFailed` event when all HTLCs have irrevocably failed. This avoids a number of race
459+
/// conditions in MPP-aware payment retriers[1], where the possibility of multiple
460+
/// `PaymentPathFaield` events with `all_paths_failed` can be pending at once, confusing a
461+
/// downstream event handler as to when a payment has actually failed.
462+
/// [1] https://github.com/lightningdevkit/rust-lightning/issues/1164
463+
RetriesExceeded {
464+
session_privs: HashSet<[u8; 32]>,
465+
payment_hash: PaymentHash,
466+
},
457467
}
458468

459469
impl PendingOutboundPayment {
@@ -469,6 +479,12 @@ impl PendingOutboundPayment {
469479
_ => false,
470480
}
471481
}
482+
fn retries_exceeded(&self) -> bool {
483+
match self {
484+
PendingOutboundPayment::RetriesExceeded { .. } => true,
485+
_ => false,
486+
}
487+
}
472488
fn get_pending_fee_msat(&self) -> Option<u64> {
473489
match self {
474490
PendingOutboundPayment::Retryable { pending_fee_msat, .. } => pending_fee_msat.clone(),
@@ -481,6 +497,7 @@ impl PendingOutboundPayment {
481497
PendingOutboundPayment::Legacy { .. } => None,
482498
PendingOutboundPayment::Retryable { payment_hash, .. } => Some(*payment_hash),
483499
PendingOutboundPayment::Fulfilled { payment_hash, .. } => *payment_hash,
500+
PendingOutboundPayment::RetriesExceeded { payment_hash, .. } => Some(*payment_hash),
484501
}
485502
}
486503

@@ -489,19 +506,38 @@ impl PendingOutboundPayment {
489506
core::mem::swap(&mut session_privs, match self {
490507
PendingOutboundPayment::Legacy { session_privs } |
491508
PendingOutboundPayment::Retryable { session_privs, .. } |
492-
PendingOutboundPayment::Fulfilled { session_privs, .. }
493-
=> session_privs
509+
PendingOutboundPayment::Fulfilled { session_privs, .. } |
510+
PendingOutboundPayment::RetriesExceeded { session_privs, .. }
511+
=> session_privs,
494512
});
495513
let payment_hash = self.payment_hash();
496514
*self = PendingOutboundPayment::Fulfilled { session_privs, payment_hash };
497515
}
498516

517+
fn mark_retries_exceeded(&mut self) -> Result<(), ()> {
518+
let mut session_privs = HashSet::new();
519+
let our_payment_hash;
520+
core::mem::swap(&mut session_privs, match self {
521+
PendingOutboundPayment::Legacy { .. } |
522+
PendingOutboundPayment::Fulfilled { .. } =>
523+
return Err(()),
524+
PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } |
525+
PendingOutboundPayment::RetriesExceeded { session_privs, payment_hash, .. } => {
526+
our_payment_hash = *payment_hash;
527+
session_privs
528+
},
529+
});
530+
*self = PendingOutboundPayment::RetriesExceeded { session_privs, payment_hash: our_payment_hash };
531+
Ok(())
532+
}
533+
499534
/// panics if path is None and !self.is_fulfilled
500535
fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Vec<RouteHop>>) -> bool {
501536
let remove_res = match self {
502537
PendingOutboundPayment::Legacy { session_privs } |
503538
PendingOutboundPayment::Retryable { session_privs, .. } |
504-
PendingOutboundPayment::Fulfilled { session_privs, .. } => {
539+
PendingOutboundPayment::Fulfilled { session_privs, .. } |
540+
PendingOutboundPayment::RetriesExceeded { session_privs, .. } => {
505541
session_privs.remove(session_priv)
506542
}
507543
};
@@ -524,7 +560,8 @@ impl PendingOutboundPayment {
524560
PendingOutboundPayment::Retryable { session_privs, .. } => {
525561
session_privs.insert(session_priv)
526562
}
527-
PendingOutboundPayment::Fulfilled { .. } => false
563+
PendingOutboundPayment::Fulfilled { .. } => false,
564+
PendingOutboundPayment::RetriesExceeded { .. } => false,
528565
};
529566
if insert_res {
530567
if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
@@ -542,7 +579,8 @@ impl PendingOutboundPayment {
542579
match self {
543580
PendingOutboundPayment::Legacy { session_privs } |
544581
PendingOutboundPayment::Retryable { session_privs, .. } |
545-
PendingOutboundPayment::Fulfilled { session_privs, .. } => {
582+
PendingOutboundPayment::Fulfilled { session_privs, .. } |
583+
PendingOutboundPayment::RetriesExceeded { session_privs, .. } => {
546584
session_privs.len()
547585
}
548586
}
@@ -2352,6 +2390,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
23522390
err: "Payment already completed"
23532391
}));
23542392
},
2393+
PendingOutboundPayment::RetriesExceeded { .. } => {
2394+
return Err(PaymentSendFailure::ParameterError(APIError::RouteError {
2395+
err: "Payment already marked timed out (with some HTLCs still pending)"
2396+
}));
2397+
},
23552398
}
23562399
} else {
23572400
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
@@ -2362,6 +2405,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
23622405
return self.send_payment_internal(route, payment_hash, &payment_secret, None, Some(payment_id), Some(total_msat)).map(|_| ())
23632406
}
23642407

2408+
/// Signals that no further retries for the given payment will occur.
2409+
pub fn no_further_payment_retries(&self, payment_id: PaymentId) {
2410+
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
2411+
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
2412+
let _ = payment.get_mut().mark_retries_exceeded();
2413+
}
2414+
}
2415+
23652416
/// Send a spontaneous payment, which is a payment that does not require the recipient to have
23662417
/// generated an invoice. Optionally, you may specify the preimage. If you do choose to specify
23672418
/// the preimage, it must be a cryptographically secure random value that no intermediate node
@@ -5601,6 +5652,10 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
56015652
(8, pending_amt_msat, required),
56025653
(10, starting_block_height, required),
56035654
},
5655+
(3, RetriesExceeded) => {
5656+
(0, session_privs, required),
5657+
(2, payment_hash, required),
5658+
},
56045659
);
56055660

56065661
impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable for ChannelManager<Signer, M, T, K, F, L>
@@ -5694,7 +5749,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
56945749
// For backwards compat, write the session privs and their total length.
56955750
let mut num_pending_outbounds_compat: u64 = 0;
56965751
for (_, outbound) in pending_outbound_payments.iter() {
5697-
if !outbound.is_fulfilled() {
5752+
if !outbound.is_fulfilled() && !outbound.retries_exceeded() {
56985753
num_pending_outbounds_compat += outbound.remaining_parts() as u64;
56995754
}
57005755
}
@@ -5708,6 +5763,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
57085763
}
57095764
}
57105765
PendingOutboundPayment::Fulfilled { .. } => {},
5766+
PendingOutboundPayment::RetriesExceeded { .. } => {},
57115767
}
57125768
}
57135769

0 commit comments

Comments
 (0)