Skip to content

Commit 9e08f7c

Browse files
committed
Expose an event when a payment has failed and retries complete
When a payment fails, a payer needs to know when they can consider a payment as fully-failed, and when only some of the HTLCs in the payment have failed. This isn't possible with the current event scheme, as discovered recently and as described in the previous commit. This adds a new event which describes when a payment is fully and irrevocably failed, generating it only after the payment has expired or been marked as expired with `ChannelManager::mark_retries_exceeded` *and* all HTLCs for it have failed. With this, a payer can more simply deduce when a payment has failed and use that to remove payment state or finalize a payment failure.
1 parent b18f8cb commit 9e08f7c

File tree

5 files changed

+228
-64
lines changed

5 files changed

+228
-64
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 87 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,10 @@ const CHECK_CLTV_EXPIRY_SANITY: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRA
838838
#[allow(dead_code)]
839839
const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER;
840840

841+
/// The number of blocks before we consider an outbound payment for expiry if it doesn't have any
842+
/// pending HTLCs in flight.
843+
pub(crate) const PAYMENT_EXPIRY_BLOCKS: u32 = 3;
844+
841845
/// Information needed for constructing an invoice route hint for this channel.
842846
#[derive(Clone, Debug, PartialEq)]
843847
pub struct CounterpartyForwardingInfo {
@@ -2409,15 +2413,26 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
24092413
/// Signals that no further retries for the given payment will occur.
24102414
///
24112415
/// After this method returns, any future calls to [`retry_payment`] for the given `payment_id`
2412-
/// will fail with an [`PaymentSendFailure::ParameterError`] error.
2416+
/// will fail with an [`PaymentSendFailure::ParameterError`] error. If no such event has been
2417+
/// generated, an [`Event::PaymentFailed`] event will be generated as soon as there are no
2418+
/// remaining pending HTLCs for this payment.
24132419
///
24142420
/// [`retry_payment`]: Self::retry_payment
2421+
/// [`Event::PaymentFailed`]: events::Event::PaymentFailed
24152422
pub fn no_further_payment_retries(&self, payment_id: PaymentId) {
24162423
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
24172424

24182425
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
24192426
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
2420-
let _ = payment.get_mut().mark_retries_exceeded();
2427+
if let Ok(()) = payment.get_mut().mark_retries_exceeded() {
2428+
if payment.get().remaining_parts() == 0 {
2429+
self.pending_events.lock().unwrap().push(events::Event::PaymentFailed {
2430+
payment_id,
2431+
payment_hash: payment.get().payment_hash().expect("RetriesExceeded PendintOutboundPayments always have a payment hash set"),
2432+
});
2433+
payment.remove();
2434+
}
2435+
}
24212436
}
24222437
}
24232438

@@ -3246,22 +3261,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32463261
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
32473262
})
32483263
} else { None };
3249-
self.pending_events.lock().unwrap().push(
3250-
events::Event::PaymentPathFailed {
3251-
payment_id: Some(payment_id),
3252-
payment_hash,
3253-
rejected_by_dest: false,
3254-
network_update: None,
3255-
all_paths_failed: payment.get().remaining_parts() == 0,
3256-
path: path.clone(),
3257-
short_channel_id: None,
3258-
retry,
3259-
#[cfg(test)]
3260-
error_code: None,
3261-
#[cfg(test)]
3262-
error_data: None,
3263-
}
3264-
);
3264+
let mut pending_events = self.pending_events.lock().unwrap();
3265+
pending_events.push(events::Event::PaymentPathFailed {
3266+
payment_id: Some(payment_id),
3267+
payment_hash,
3268+
rejected_by_dest: false,
3269+
network_update: None,
3270+
all_paths_failed: payment.get().remaining_parts() == 0,
3271+
path: path.clone(),
3272+
short_channel_id: None,
3273+
retry,
3274+
#[cfg(test)]
3275+
error_code: None,
3276+
#[cfg(test)]
3277+
error_data: None,
3278+
});
3279+
if payment.get().retries_exceeded() && payment.get().remaining_parts() == 0 {
3280+
pending_events.push(events::Event::PaymentFailed {
3281+
payment_id,
3282+
payment_hash: payment.get().payment_hash().expect("RetriesExceeded PendintOutboundPayments always have a payment hash set"),
3283+
});
3284+
payment.remove();
3285+
}
32653286
}
32663287
} else {
32673288
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3292,6 +3313,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32923313
session_priv_bytes.copy_from_slice(&session_priv[..]);
32933314
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
32943315
let mut all_paths_failed = false;
3316+
let mut full_failure_ev = None;
32953317
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
32963318
if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
32973319
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3303,6 +3325,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33033325
}
33043326
if payment.get().remaining_parts() == 0 {
33053327
all_paths_failed = true;
3328+
if payment.get().retries_exceeded() {
3329+
full_failure_ev = Some(events::Event::PaymentFailed {
3330+
payment_id,
3331+
payment_hash: payment.get().payment_hash().expect("RetriesExceeded PendintOutboundPayments always have a payment hash set"),
3332+
});
3333+
payment.remove();
3334+
}
33063335
}
33073336
} else {
33083337
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3318,6 +3347,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33183347
})
33193348
} else { None };
33203349
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
3350+
33213351
match &onion_error {
33223352
&HTLCFailReason::LightningError { ref err } => {
33233353
#[cfg(test)]
@@ -3327,22 +3357,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33273357
// TODO: If we decided to blame ourselves (or one of our channels) in
33283358
// process_onion_failure we should close that channel as it implies our
33293359
// next-hop is needlessly blaming us!
3330-
self.pending_events.lock().unwrap().push(
3331-
events::Event::PaymentPathFailed {
3332-
payment_id: Some(payment_id),
3333-
payment_hash: payment_hash.clone(),
3334-
rejected_by_dest: !payment_retryable,
3335-
network_update,
3336-
all_paths_failed,
3337-
path: path.clone(),
3338-
short_channel_id,
3339-
retry,
3360+
let mut pending_events = self.pending_events.lock().unwrap();
3361+
pending_events.push(events::Event::PaymentPathFailed {
3362+
payment_id: Some(payment_id),
3363+
payment_hash: payment_hash.clone(),
3364+
rejected_by_dest: !payment_retryable,
3365+
network_update,
3366+
all_paths_failed,
3367+
path: path.clone(),
3368+
short_channel_id,
3369+
retry,
33403370
#[cfg(test)]
3341-
error_code: onion_error_code,
3371+
error_code: onion_error_code,
33423372
#[cfg(test)]
3343-
error_data: onion_error_data
3344-
}
3345-
);
3373+
error_data: onion_error_data
3374+
});
3375+
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
33463376
},
33473377
&HTLCFailReason::Reason {
33483378
#[cfg(test)]
@@ -3357,22 +3387,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33573387
// ChannelDetails.
33583388
// TODO: For non-temporary failures, we really should be closing the
33593389
// channel here as we apparently can't relay through them anyway.
3360-
self.pending_events.lock().unwrap().push(
3361-
events::Event::PaymentPathFailed {
3362-
payment_id: Some(payment_id),
3363-
payment_hash: payment_hash.clone(),
3364-
rejected_by_dest: path.len() == 1,
3365-
network_update: None,
3366-
all_paths_failed,
3367-
path: path.clone(),
3368-
short_channel_id: Some(path.first().unwrap().short_channel_id),
3369-
retry,
3390+
let mut pending_events = self.pending_events.lock().unwrap();
3391+
pending_events.push(events::Event::PaymentPathFailed {
3392+
payment_id: Some(payment_id),
3393+
payment_hash: payment_hash.clone(),
3394+
rejected_by_dest: path.len() == 1,
3395+
network_update: None,
3396+
all_paths_failed,
3397+
path: path.clone(),
3398+
short_channel_id: Some(path.first().unwrap().short_channel_id),
3399+
retry,
33703400
#[cfg(test)]
3371-
error_code: Some(*failure_code),
3401+
error_code: Some(*failure_code),
33723402
#[cfg(test)]
3373-
error_data: Some(data.clone()),
3374-
}
3375-
);
3403+
error_data: Some(data.clone()),
3404+
});
3405+
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
33763406
}
33773407
}
33783408
},
@@ -4901,14 +4931,19 @@ where
49014931
inbound_payment.expiry_time > header.time as u64
49024932
});
49034933

4934+
let mut pending_events = self.pending_events.lock().unwrap();
49044935
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
4905-
outbounds.retain(|_, payment| {
4906-
const PAYMENT_EXPIRY_BLOCKS: u32 = 3;
4936+
outbounds.retain(|payment_id, payment| {
49074937
if payment.remaining_parts() != 0 { return true }
4908-
if let PendingOutboundPayment::Retryable { starting_block_height, .. } = payment {
4909-
return *starting_block_height + PAYMENT_EXPIRY_BLOCKS > height
4910-
}
4911-
true
4938+
if let PendingOutboundPayment::Retryable { starting_block_height, payment_hash, .. } = payment {
4939+
if *starting_block_height + PAYMENT_EXPIRY_BLOCKS <= height {
4940+
log_info!(self.logger, "Timing out payment with id {} and hash {}", log_bytes!(payment_id.0), log_bytes!(payment_hash.0));
4941+
pending_events.push(events::Event::PaymentFailed {
4942+
payment_id: *payment_id, payment_hash: *payment_hash,
4943+
});
4944+
false
4945+
} else { true }
4946+
} else { true }
49124947
});
49134948
}
49144949

lightning/src/ln/functional_test_utils.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,8 +1189,9 @@ macro_rules! expect_payment_failed_with_update {
11891189
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $scid: expr, $chan_closed: expr) => {
11901190
let events = $node.node.get_and_clear_pending_events();
11911191
assert_eq!(events.len(), 1);
1192+
let our_payment_id;
11921193
match events[0] {
1193-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, .. } => {
1194+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, ref payment_id, .. } => {
11941195
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
11951196
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
11961197
assert!(retry.is_some(), "expected retry.is_some()");
@@ -1210,19 +1211,34 @@ macro_rules! expect_payment_failed_with_update {
12101211
Some(_) => panic!("Unexpected update type"),
12111212
None => panic!("Expected update"),
12121213
}
1214+
our_payment_id = payment_id.unwrap();
12131215
},
12141216
_ => panic!("Unexpected event"),
12151217
}
1218+
$node.node.no_further_payment_retries(our_payment_id);
1219+
let events = $node.node.get_and_clear_pending_events();
1220+
assert_eq!(events.len(), 1);
1221+
match events[0] {
1222+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1223+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected second payment_hash");
1224+
assert_eq!(*payment_id, our_payment_id);
1225+
}
1226+
_ => panic!("Unexpected second event"),
1227+
}
12161228
}
12171229
}
12181230

12191231
#[cfg(test)]
12201232
macro_rules! expect_payment_failed {
12211233
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
1234+
expect_payment_failed!($node, $expected_payment_hash, $rejected_by_dest, false $(, $expected_error_code, $expected_error_data)*);
1235+
};
1236+
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $mpp_parts_remain: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
12221237
let events = $node.node.get_and_clear_pending_events();
12231238
assert_eq!(events.len(), 1);
1239+
let our_payment_id;
12241240
match events[0] {
1225-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, ref path, ref retry, .. } => {
1241+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data, ref path, ref retry, ref payment_id, .. } => {
12261242
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
12271243
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
12281244
assert!(retry.is_some(), "expected retry.is_some()");
@@ -1234,9 +1250,22 @@ macro_rules! expect_payment_failed {
12341250
assert_eq!(error_code.unwrap(), $expected_error_code, "unexpected error code");
12351251
assert_eq!(&error_data.as_ref().unwrap()[..], $expected_error_data, "unexpected error data");
12361252
)*
1253+
our_payment_id = payment_id.unwrap();
12371254
},
12381255
_ => panic!("Unexpected event"),
12391256
}
1257+
if !$mpp_parts_remain {
1258+
$node.node.no_further_payment_retries(our_payment_id);
1259+
let events = $node.node.get_and_clear_pending_events();
1260+
assert_eq!(events.len(), 1);
1261+
match events[0] {
1262+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1263+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected second payment_hash");
1264+
assert_eq!(*payment_id, our_payment_id);
1265+
}
1266+
_ => panic!("Unexpected second event"),
1267+
}
1268+
}
12401269
}
12411270
}
12421271

@@ -1534,17 +1563,31 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
15341563
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
15351564
let events = origin_node.node.get_and_clear_pending_events();
15361565
assert_eq!(events.len(), 1);
1566+
let our_payment_id;
15371567
match events[0] {
1538-
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, .. } => {
1568+
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, ref payment_id, .. } => {
15391569
assert_eq!(payment_hash, our_payment_hash);
15401570
assert!(rejected_by_dest);
15411571
assert_eq!(all_paths_failed, i == expected_paths.len() - 1);
15421572
for (idx, hop) in expected_route.iter().enumerate() {
15431573
assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey);
15441574
}
1575+
our_payment_id = payment_id.unwrap();
15451576
},
15461577
_ => panic!("Unexpected event"),
15471578
}
1579+
if i == expected_paths.len() - 1 {
1580+
origin_node.node.no_further_payment_retries(our_payment_id);
1581+
let events = origin_node.node.get_and_clear_pending_events();
1582+
assert_eq!(events.len(), 1);
1583+
match events[0] {
1584+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1585+
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
1586+
assert_eq!(*payment_id, our_payment_id);
1587+
}
1588+
_ => panic!("Unexpected second event"),
1589+
}
1590+
}
15481591
}
15491592
}
15501593
}

lightning/src/ln/functional_tests.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use chain::transaction::OutPoint;
1919
use chain::keysinterface::BaseSign;
2020
use ln::{PaymentPreimage, PaymentSecret, PaymentHash};
2121
use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT};
22-
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, PaymentId, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
22+
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, PaymentId, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, PAYMENT_EXPIRY_BLOCKS };
2323
use ln::channel::{Channel, ChannelError};
2424
use ln::{chan_utils, onion_utils};
2525
use ln::chan_utils::{HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, HTLCOutputInCommitment};
@@ -3152,9 +3152,10 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
31523152
mine_transaction(&nodes[1], &revoked_local_txn[0]);
31533153
check_added_monitors!(nodes[1], 1);
31543154
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
3155+
assert!(ANTI_REORG_DELAY > PAYMENT_EXPIRY_BLOCKS); // We assume payments will also expire
31553156

31563157
let events = nodes[1].node.get_and_clear_pending_events();
3157-
assert_eq!(events.len(), if deliver_bs_raa { 2 } else { 3 });
3158+
assert_eq!(events.len(), if deliver_bs_raa { 2 } else { 4 });
31583159
match events[0] {
31593160
Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => { },
31603161
_ => panic!("Unexepected event"),
@@ -3167,6 +3168,12 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
31673168
}
31683169
if !deliver_bs_raa {
31693170
match events[2] {
3171+
Event::PaymentFailed { ref payment_hash, .. } => {
3172+
assert_eq!(*payment_hash, fourth_payment_hash);
3173+
},
3174+
_ => panic!("Unexpected event"),
3175+
}
3176+
match events[3] {
31703177
Event::PendingHTLCsForwardable { .. } => { },
31713178
_ => panic!("Unexpected event"),
31723179
};
@@ -4184,7 +4191,14 @@ fn do_test_holding_cell_htlc_add_timeouts(forwarded_htlc: bool) {
41844191
}
41854192
expect_payment_failed_with_update!(nodes[0], second_payment_hash, false, chan_2.0.contents.short_channel_id, false);
41864193
} else {
4187-
expect_payment_failed!(nodes[1], second_payment_hash, true);
4194+
let events = nodes[1].node.get_and_clear_pending_events();
4195+
assert_eq!(events.len(), 2);
4196+
if let Event::PaymentPathFailed { ref payment_hash, .. } = events[0] {
4197+
assert_eq!(*payment_hash, second_payment_hash);
4198+
} else { panic!("Unexpected event"); }
4199+
if let Event::PaymentFailed { ref payment_hash, .. } = events[1] {
4200+
assert_eq!(*payment_hash, second_payment_hash);
4201+
} else { panic!("Unexpected event"); }
41884202
}
41894203
}
41904204

@@ -5840,7 +5854,14 @@ fn do_htlc_claim_previous_remote_commitment_only(use_dust: bool, check_revoke_no
58405854
check_added_monitors!(nodes[0], 1);
58415855
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
58425856
} else {
5843-
expect_payment_failed!(nodes[0], our_payment_hash, true);
5857+
let events = nodes[0].node.get_and_clear_pending_events();
5858+
assert_eq!(events.len(), 2);
5859+
if let Event::PaymentPathFailed { ref payment_hash, .. } = events[0] {
5860+
assert_eq!(*payment_hash, our_payment_hash);
5861+
} else { panic!("Unexpected event"); }
5862+
if let Event::PaymentFailed { ref payment_hash, .. } = events[1] {
5863+
assert_eq!(*payment_hash, our_payment_hash);
5864+
} else { panic!("Unexpected event"); }
58445865
}
58455866
}
58465867

0 commit comments

Comments
 (0)