Skip to content

Commit d6aa1bc

Browse files
authored
Merge pull request #1826 from TheBlueMatt/2022-10-idempotency-err
Add a separate PaymentSendFailure for idempotency violation
2 parents 4006717 + fcf73f0 commit d6aa1bc

File tree

6 files changed

+42
-22
lines changed

6 files changed

+42
-22
lines changed

fuzz/src/chanmon_consistency.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,13 @@ fn check_payment_err(send_err: PaymentSendFailure) {
283283
PaymentSendFailure::PathParameterError(per_path_results) => {
284284
for res in per_path_results { if let Err(api_err) = res { check_api_err(api_err); } }
285285
},
286-
PaymentSendFailure::AllFailedRetrySafe(per_path_results) => {
286+
PaymentSendFailure::AllFailedResendSafe(per_path_results) => {
287287
for api_err in per_path_results { check_api_err(api_err); }
288288
},
289289
PaymentSendFailure::PartialFailure { results, .. } => {
290290
for res in results { if let Err(api_err) = res { check_api_err(api_err); } }
291291
},
292+
PaymentSendFailure::DuplicatePayment => panic!(),
292293
}
293294
}
294295

lightning-invoice/src/payment.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,8 @@ where
562562
Err(e) => match e {
563563
PaymentSendFailure::ParameterError(_) => Err(e),
564564
PaymentSendFailure::PathParameterError(_) => Err(e),
565-
PaymentSendFailure::AllFailedRetrySafe(_) => {
565+
PaymentSendFailure::DuplicatePayment => Err(e),
566+
PaymentSendFailure::AllFailedResendSafe(_) => {
566567
let mut payment_cache = self.payment_cache.lock().unwrap();
567568
let payment_info = payment_cache.get_mut(&payment_hash).unwrap();
568569
payment_info.attempts.count += 1;
@@ -673,9 +674,13 @@ where
673674
log_trace!(self.logger, "Failed to retry for payment {} due to bogus route/payment data, not retrying.", log_bytes!(payment_hash.0));
674675
Err(())
675676
},
676-
Err(PaymentSendFailure::AllFailedRetrySafe(_)) => {
677+
Err(PaymentSendFailure::AllFailedResendSafe(_)) => {
677678
self.retry_payment(payment_id, payment_hash, params)
678679
},
680+
Err(PaymentSendFailure::DuplicatePayment) => {
681+
log_error!(self.logger, "Got a DuplicatePayment error when attempting to retry a payment, this shouldn't happen.");
682+
Err(())
683+
}
679684
Err(PaymentSendFailure::PartialFailure { failed_paths_retry, results, .. }) => {
680685
// If a `PartialFailure` error contains a result that is an `Ok()`, it means that
681686
// part of our payment is retried. When we receive `MonitorUpdateInProgress`, it

lightning/src/ln/channelmanager.rs

+27-13
Original file line numberDiff line numberDiff line change
@@ -1204,24 +1204,40 @@ impl ChannelDetails {
12041204
#[derive(Clone, Debug)]
12051205
pub enum PaymentSendFailure {
12061206
/// A parameter which was passed to send_payment was invalid, preventing us from attempting to
1207-
/// send the payment at all. No channel state has been changed or messages sent to peers, and
1208-
/// once you've changed the parameter at error, you can freely retry the payment in full.
1207+
/// send the payment at all.
1208+
///
1209+
/// You can freely resend the payment in full (with the parameter error fixed).
1210+
///
1211+
/// Because the payment failed outright, no payment tracking is done, you do not need to call
1212+
/// [`ChannelManager::abandon_payment`] and [`ChannelManager::retry_payment`] will *not* work
1213+
/// for this payment.
12091214
ParameterError(APIError),
12101215
/// A parameter in a single path which was passed to send_payment was invalid, preventing us
1211-
/// from attempting to send the payment at all. No channel state has been changed or messages
1212-
/// sent to peers, and once you've changed the parameter at error, you can freely retry the
1213-
/// payment in full.
1216+
/// from attempting to send the payment at all.
1217+
///
1218+
/// You can freely resend the payment in full (with the parameter error fixed).
12141219
///
12151220
/// The results here are ordered the same as the paths in the route object which was passed to
12161221
/// send_payment.
1222+
///
1223+
/// Because the payment failed outright, no payment tracking is done, you do not need to call
1224+
/// [`ChannelManager::abandon_payment`] and [`ChannelManager::retry_payment`] will *not* work
1225+
/// for this payment.
12171226
PathParameterError(Vec<Result<(), APIError>>),
12181227
/// All paths which were attempted failed to send, with no channel state change taking place.
1219-
/// You can freely retry the payment in full (though you probably want to do so over different
1228+
/// You can freely resend the payment in full (though you probably want to do so over different
12201229
/// paths than the ones selected).
12211230
///
1222-
/// [`ChannelManager::abandon_payment`] does *not* need to be called for this payment and
1223-
/// [`ChannelManager::retry_payment`] will *not* work for this payment.
1224-
AllFailedRetrySafe(Vec<APIError>),
1231+
/// Because the payment failed outright, no payment tracking is done, you do not need to call
1232+
/// [`ChannelManager::abandon_payment`] and [`ChannelManager::retry_payment`] will *not* work
1233+
/// for this payment.
1234+
AllFailedResendSafe(Vec<APIError>),
1235+
/// Indicates that a payment for the provided [`PaymentId`] is already in-flight and has not
1236+
/// yet completed (i.e. generated an [`Event::PaymentSent`]) or been abandoned (via
1237+
/// [`ChannelManager::abandon_payment`]).
1238+
///
1239+
/// [`Event::PaymentSent`]: events::Event::PaymentSent
1240+
DuplicatePayment,
12251241
/// Some paths which were attempted failed to send, though possibly not all. At least some
12261242
/// paths have irrevocably committed to the HTLC and retrying the payment in full would result
12271243
/// in over-/re-payment.
@@ -2632,9 +2648,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
26322648

26332649
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
26342650
match pending_outbounds.entry(payment_id) {
2635-
hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::ParameterError(APIError::RouteError {
2636-
err: "Payment already in progress"
2637-
})),
2651+
hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment),
26382652
hash_map::Entry::Vacant(entry) => {
26392653
let payment = entry.insert(PendingOutboundPayment::Retryable {
26402654
session_privs: HashSet::new(),
@@ -2748,7 +2762,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
27482762
// `pending_outbound_payments` map, as the user isn't expected to `abandon_payment`.
27492763
let removed = self.pending_outbound_payments.lock().unwrap().remove(&payment_id).is_some();
27502764
debug_assert!(removed, "We should always have a pending payment to remove here");
2751-
Err(PaymentSendFailure::AllFailedRetrySafe(results.drain(..).map(|r| r.unwrap_err()).collect()))
2765+
Err(PaymentSendFailure::AllFailedResendSafe(results.drain(..).map(|r| r.unwrap_err()).collect()))
27522766
} else {
27532767
Ok(())
27542768
}

lightning/src/ln/functional_test_utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ macro_rules! get_local_commitment_txn {
588588
macro_rules! unwrap_send_err {
589589
($res: expr, $all_failed: expr, $type: pat, $check: expr) => {
590590
match &$res {
591-
&Err(PaymentSendFailure::AllFailedRetrySafe(ref fails)) if $all_failed => {
591+
&Err(PaymentSendFailure::AllFailedResendSafe(ref fails)) if $all_failed => {
592592
assert_eq!(fails.len(), 1);
593593
match fails[0] {
594594
$type => { $check },

lightning/src/ln/functional_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,7 @@ fn test_basic_channel_reserve() {
13331333
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], max_can_send + 1);
13341334
let err = nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret), PaymentId(our_payment_hash.0)).err().unwrap();
13351335
match err {
1336-
PaymentSendFailure::AllFailedRetrySafe(ref fails) => {
1336+
PaymentSendFailure::AllFailedResendSafe(ref fails) => {
13371337
match &fails[0] {
13381338
&APIError::ChannelUnavailable{ref err} =>
13391339
assert!(regex::Regex::new(r"Cannot send value that would put our balance under counterparty-announced channel reserve value \(\d+\)").unwrap().is_match(err)),

lightning/src/ln/payment_tests.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1129,15 +1129,15 @@ fn claimed_send_payment_idempotent() {
11291129
// payment_id, it should be rejected.
11301130
let send_result = nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret), payment_id);
11311131
match send_result {
1132-
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
1132+
Err(PaymentSendFailure::DuplicatePayment) => {},
11331133
_ => panic!("Unexpected send result: {:?}", send_result),
11341134
}
11351135

11361136
// Further, if we try to send a spontaneous payment with the same payment_id it should
11371137
// also be rejected.
11381138
let send_result = nodes[0].node.send_spontaneous_payment(&route, None, payment_id);
11391139
match send_result {
1140-
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
1140+
Err(PaymentSendFailure::DuplicatePayment) => {},
11411141
_ => panic!("Unexpected send result: {:?}", send_result),
11421142
}
11431143
}
@@ -1201,15 +1201,15 @@ fn abandoned_send_payment_idempotent() {
12011201
// payment_id, it should be rejected.
12021202
let send_result = nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret), payment_id);
12031203
match send_result {
1204-
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
1204+
Err(PaymentSendFailure::DuplicatePayment) => {},
12051205
_ => panic!("Unexpected send result: {:?}", send_result),
12061206
}
12071207

12081208
// Further, if we try to send a spontaneous payment with the same payment_id it should
12091209
// also be rejected.
12101210
let send_result = nodes[0].node.send_spontaneous_payment(&route, None, payment_id);
12111211
match send_result {
1212-
Err(PaymentSendFailure::ParameterError(APIError::RouteError { err: "Payment already in progress" })) => {},
1212+
Err(PaymentSendFailure::DuplicatePayment) => {},
12131213
_ => panic!("Unexpected send result: {:?}", send_result),
12141214
}
12151215
}

0 commit comments

Comments
 (0)