Skip to content

Commit f857ace

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 7ea6894 commit f857ace

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
@@ -837,6 +837,10 @@ const CHECK_CLTV_EXPIRY_SANITY: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRA
837837
#[allow(dead_code)]
838838
const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER;
839839

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

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

@@ -3245,22 +3260,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32453260
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
32463261
})
32473262
} else { None };
3248-
self.pending_events.lock().unwrap().push(
3249-
events::Event::PaymentPathFailed {
3250-
payment_id: Some(payment_id),
3251-
payment_hash,
3252-
rejected_by_dest: false,
3253-
network_update: None,
3254-
all_paths_failed: payment.get().remaining_parts() == 0,
3255-
path: path.clone(),
3256-
short_channel_id: None,
3257-
retry,
3258-
#[cfg(test)]
3259-
error_code: None,
3260-
#[cfg(test)]
3261-
error_data: None,
3262-
}
3263-
);
3263+
let mut pending_events = self.pending_events.lock().unwrap();
3264+
pending_events.push(events::Event::PaymentPathFailed {
3265+
payment_id: Some(payment_id),
3266+
payment_hash,
3267+
rejected_by_dest: false,
3268+
network_update: None,
3269+
all_paths_failed: payment.get().remaining_parts() == 0,
3270+
path: path.clone(),
3271+
short_channel_id: None,
3272+
retry,
3273+
#[cfg(test)]
3274+
error_code: None,
3275+
#[cfg(test)]
3276+
error_data: None,
3277+
});
3278+
if payment.get().retries_exceeded() && payment.get().remaining_parts() == 0 {
3279+
pending_events.push(events::Event::PaymentFailed {
3280+
payment_id,
3281+
payment_hash: payment.get().payment_hash().expect("RetriesExceeded PendintOutboundPayments always have a payment hash set"),
3282+
});
3283+
payment.remove();
3284+
}
32643285
}
32653286
} else {
32663287
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3291,6 +3312,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32913312
session_priv_bytes.copy_from_slice(&session_priv[..]);
32923313
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
32933314
let mut all_paths_failed = false;
3315+
let mut full_failure_ev = None;
32943316
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
32953317
if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
32963318
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3302,6 +3324,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33023324
}
33033325
if payment.get().remaining_parts() == 0 {
33043326
all_paths_failed = true;
3327+
if payment.get().retries_exceeded() {
3328+
full_failure_ev = Some(events::Event::PaymentFailed {
3329+
payment_id,
3330+
payment_hash: payment.get().payment_hash().expect("RetriesExceeded PendintOutboundPayments always have a payment hash set"),
3331+
});
3332+
payment.remove();
3333+
}
33053334
}
33063335
} else {
33073336
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3317,6 +3346,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33173346
})
33183347
} else { None };
33193348
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
3349+
33203350
match &onion_error {
33213351
&HTLCFailReason::LightningError { ref err } => {
33223352
#[cfg(test)]
@@ -3326,22 +3356,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33263356
// TODO: If we decided to blame ourselves (or one of our channels) in
33273357
// process_onion_failure we should close that channel as it implies our
33283358
// next-hop is needlessly blaming us!
3329-
self.pending_events.lock().unwrap().push(
3330-
events::Event::PaymentPathFailed {
3331-
payment_id: Some(payment_id),
3332-
payment_hash: payment_hash.clone(),
3333-
rejected_by_dest: !payment_retryable,
3334-
network_update,
3335-
all_paths_failed,
3336-
path: path.clone(),
3337-
short_channel_id,
3338-
retry,
3359+
let mut pending_events = self.pending_events.lock().unwrap();
3360+
pending_events.push(events::Event::PaymentPathFailed {
3361+
payment_id: Some(payment_id),
3362+
payment_hash: payment_hash.clone(),
3363+
rejected_by_dest: !payment_retryable,
3364+
network_update,
3365+
all_paths_failed,
3366+
path: path.clone(),
3367+
short_channel_id,
3368+
retry,
33393369
#[cfg(test)]
3340-
error_code: onion_error_code,
3370+
error_code: onion_error_code,
33413371
#[cfg(test)]
3342-
error_data: onion_error_data
3343-
}
3344-
);
3372+
error_data: onion_error_data
3373+
});
3374+
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
33453375
},
33463376
&HTLCFailReason::Reason {
33473377
#[cfg(test)]
@@ -3356,22 +3386,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33563386
// ChannelDetails.
33573387
// TODO: For non-temporary failures, we really should be closing the
33583388
// channel here as we apparently can't relay through them anyway.
3359-
self.pending_events.lock().unwrap().push(
3360-
events::Event::PaymentPathFailed {
3361-
payment_id: Some(payment_id),
3362-
payment_hash: payment_hash.clone(),
3363-
rejected_by_dest: path.len() == 1,
3364-
network_update: None,
3365-
all_paths_failed,
3366-
path: path.clone(),
3367-
short_channel_id: Some(path.first().unwrap().short_channel_id),
3368-
retry,
3389+
let mut pending_events = self.pending_events.lock().unwrap();
3390+
pending_events.push(events::Event::PaymentPathFailed {
3391+
payment_id: Some(payment_id),
3392+
payment_hash: payment_hash.clone(),
3393+
rejected_by_dest: path.len() == 1,
3394+
network_update: None,
3395+
all_paths_failed,
3396+
path: path.clone(),
3397+
short_channel_id: Some(path.first().unwrap().short_channel_id),
3398+
retry,
33693399
#[cfg(test)]
3370-
error_code: Some(*failure_code),
3400+
error_code: Some(*failure_code),
33713401
#[cfg(test)]
3372-
error_data: Some(data.clone()),
3373-
}
3374-
);
3402+
error_data: Some(data.clone()),
3403+
});
3404+
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
33753405
}
33763406
}
33773407
},
@@ -4900,14 +4930,19 @@ where
49004930
inbound_payment.expiry_time > header.time as u64
49014931
});
49024932

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

lightning/src/ln/functional_test_utils.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,8 +1183,9 @@ macro_rules! expect_payment_failed_with_update {
11831183
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $scid: expr, $chan_closed: expr) => {
11841184
let events = $node.node.get_and_clear_pending_events();
11851185
assert_eq!(events.len(), 1);
1186+
let our_payment_id;
11861187
match events[0] {
1187-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, .. } => {
1188+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, ref payment_id, .. } => {
11881189
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
11891190
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
11901191
assert!(retry.is_some(), "expected retry.is_some()");
@@ -1204,19 +1205,34 @@ macro_rules! expect_payment_failed_with_update {
12041205
Some(_) => panic!("Unexpected update type"),
12051206
None => panic!("Expected update"),
12061207
}
1208+
our_payment_id = payment_id.unwrap();
12071209
},
12081210
_ => panic!("Unexpected event"),
12091211
}
1212+
$node.node.no_further_payment_retries(our_payment_id);
1213+
let events = $node.node.get_and_clear_pending_events();
1214+
assert_eq!(events.len(), 1);
1215+
match events[0] {
1216+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1217+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected second payment_hash");
1218+
assert_eq!(*payment_id, our_payment_id);
1219+
}
1220+
_ => panic!("Unexpected second event"),
1221+
}
12101222
}
12111223
}
12121224

12131225
#[cfg(test)]
12141226
macro_rules! expect_payment_failed {
12151227
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
1228+
expect_payment_failed!($node, $expected_payment_hash, $rejected_by_dest, false $(, $expected_error_code, $expected_error_data)*);
1229+
};
1230+
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $mpp_parts_remain: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
12161231
let events = $node.node.get_and_clear_pending_events();
12171232
assert_eq!(events.len(), 1);
1233+
let our_payment_id;
12181234
match events[0] {
1219-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, ref path, ref retry, .. } => {
1235+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data, ref path, ref retry, ref payment_id, .. } => {
12201236
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
12211237
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
12221238
assert!(retry.is_some(), "expected retry.is_some()");
@@ -1228,9 +1244,22 @@ macro_rules! expect_payment_failed {
12281244
assert_eq!(error_code.unwrap(), $expected_error_code, "unexpected error code");
12291245
assert_eq!(&error_data.as_ref().unwrap()[..], $expected_error_data, "unexpected error data");
12301246
)*
1247+
our_payment_id = payment_id.unwrap();
12311248
},
12321249
_ => panic!("Unexpected event"),
12331250
}
1251+
if !$mpp_parts_remain {
1252+
$node.node.no_further_payment_retries(our_payment_id);
1253+
let events = $node.node.get_and_clear_pending_events();
1254+
assert_eq!(events.len(), 1);
1255+
match events[0] {
1256+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1257+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected second payment_hash");
1258+
assert_eq!(*payment_id, our_payment_id);
1259+
}
1260+
_ => panic!("Unexpected second event"),
1261+
}
1262+
}
12341263
}
12351264
}
12361265

@@ -1528,17 +1557,31 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
15281557
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
15291558
let events = origin_node.node.get_and_clear_pending_events();
15301559
assert_eq!(events.len(), 1);
1560+
let our_payment_id;
15311561
match events[0] {
1532-
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, .. } => {
1562+
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, ref payment_id, .. } => {
15331563
assert_eq!(payment_hash, our_payment_hash);
15341564
assert!(rejected_by_dest);
15351565
assert_eq!(all_paths_failed, i == expected_paths.len() - 1);
15361566
for (idx, hop) in expected_route.iter().enumerate() {
15371567
assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey);
15381568
}
1569+
our_payment_id = payment_id.unwrap();
15391570
},
15401571
_ => panic!("Unexpected event"),
15411572
}
1573+
if i == expected_paths.len() - 1 {
1574+
origin_node.node.no_further_payment_retries(our_payment_id);
1575+
let events = origin_node.node.get_and_clear_pending_events();
1576+
assert_eq!(events.len(), 1);
1577+
match events[0] {
1578+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1579+
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
1580+
assert_eq!(*payment_id, our_payment_id);
1581+
}
1582+
_ => panic!("Unexpected second event"),
1583+
}
1584+
}
15421585
}
15431586
}
15441587
}

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)