Skip to content

Commit 7ad9ae0

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 51ea3af commit 7ad9ae0

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 [`PaymentSendFailure::ParameterError`].
2416+
/// will fail with [`PaymentSendFailure::ParameterError`]. If no such event has been generated,
2417+
/// an [`Event::PaymentFailed`] event will be generated as soon as there are no remaining
2418+
/// 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

@@ -3248,22 +3263,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32483263
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
32493264
})
32503265
} else { None };
3251-
self.pending_events.lock().unwrap().push(
3252-
events::Event::PaymentPathFailed {
3253-
payment_id: Some(payment_id),
3254-
payment_hash,
3255-
rejected_by_dest: false,
3256-
network_update: None,
3257-
all_paths_failed: payment.get().remaining_parts() == 0,
3258-
path: path.clone(),
3259-
short_channel_id: None,
3260-
retry,
3261-
#[cfg(test)]
3262-
error_code: None,
3263-
#[cfg(test)]
3264-
error_data: None,
3265-
}
3266-
);
3266+
let mut pending_events = self.pending_events.lock().unwrap();
3267+
pending_events.push(events::Event::PaymentPathFailed {
3268+
payment_id: Some(payment_id),
3269+
payment_hash,
3270+
rejected_by_dest: false,
3271+
network_update: None,
3272+
all_paths_failed: payment.get().remaining_parts() == 0,
3273+
path: path.clone(),
3274+
short_channel_id: None,
3275+
retry,
3276+
#[cfg(test)]
3277+
error_code: None,
3278+
#[cfg(test)]
3279+
error_data: None,
3280+
});
3281+
if payment.get().retries_exceeded() && payment.get().remaining_parts() == 0 {
3282+
pending_events.push(events::Event::PaymentFailed {
3283+
payment_id,
3284+
payment_hash: payment.get().payment_hash().expect("RetriesExceeded PendintOutboundPayments always have a payment hash set"),
3285+
});
3286+
payment.remove();
3287+
}
32673288
}
32683289
} else {
32693290
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3294,6 +3315,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32943315
session_priv_bytes.copy_from_slice(&session_priv[..]);
32953316
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
32963317
let mut all_paths_failed = false;
3318+
let mut full_failure_ev = None;
32973319
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
32983320
if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
32993321
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3305,6 +3327,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33053327
}
33063328
if payment.get().remaining_parts() == 0 {
33073329
all_paths_failed = true;
3330+
if payment.get().retries_exceeded() {
3331+
full_failure_ev = Some(events::Event::PaymentFailed {
3332+
payment_id,
3333+
payment_hash: payment.get().payment_hash().expect("RetriesExceeded PendintOutboundPayments always have a payment hash set"),
3334+
});
3335+
payment.remove();
3336+
}
33083337
}
33093338
} else {
33103339
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3320,6 +3349,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33203349
})
33213350
} else { None };
33223351
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
3352+
33233353
match &onion_error {
33243354
&HTLCFailReason::LightningError { ref err } => {
33253355
#[cfg(test)]
@@ -3329,22 +3359,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33293359
// TODO: If we decided to blame ourselves (or one of our channels) in
33303360
// process_onion_failure we should close that channel as it implies our
33313361
// next-hop is needlessly blaming us!
3332-
self.pending_events.lock().unwrap().push(
3333-
events::Event::PaymentPathFailed {
3334-
payment_id: Some(payment_id),
3335-
payment_hash: payment_hash.clone(),
3336-
rejected_by_dest: !payment_retryable,
3337-
network_update,
3338-
all_paths_failed,
3339-
path: path.clone(),
3340-
short_channel_id,
3341-
retry,
3362+
let mut pending_events = self.pending_events.lock().unwrap();
3363+
pending_events.push(events::Event::PaymentPathFailed {
3364+
payment_id: Some(payment_id),
3365+
payment_hash: payment_hash.clone(),
3366+
rejected_by_dest: !payment_retryable,
3367+
network_update,
3368+
all_paths_failed,
3369+
path: path.clone(),
3370+
short_channel_id,
3371+
retry,
33423372
#[cfg(test)]
3343-
error_code: onion_error_code,
3373+
error_code: onion_error_code,
33443374
#[cfg(test)]
3345-
error_data: onion_error_data
3346-
}
3347-
);
3375+
error_data: onion_error_data
3376+
});
3377+
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
33483378
},
33493379
&HTLCFailReason::Reason {
33503380
#[cfg(test)]
@@ -3359,22 +3389,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33593389
// ChannelDetails.
33603390
// TODO: For non-temporary failures, we really should be closing the
33613391
// channel here as we apparently can't relay through them anyway.
3362-
self.pending_events.lock().unwrap().push(
3363-
events::Event::PaymentPathFailed {
3364-
payment_id: Some(payment_id),
3365-
payment_hash: payment_hash.clone(),
3366-
rejected_by_dest: path.len() == 1,
3367-
network_update: None,
3368-
all_paths_failed,
3369-
path: path.clone(),
3370-
short_channel_id: Some(path.first().unwrap().short_channel_id),
3371-
retry,
3392+
let mut pending_events = self.pending_events.lock().unwrap();
3393+
pending_events.push(events::Event::PaymentPathFailed {
3394+
payment_id: Some(payment_id),
3395+
payment_hash: payment_hash.clone(),
3396+
rejected_by_dest: path.len() == 1,
3397+
network_update: None,
3398+
all_paths_failed,
3399+
path: path.clone(),
3400+
short_channel_id: Some(path.first().unwrap().short_channel_id),
3401+
retry,
33723402
#[cfg(test)]
3373-
error_code: Some(*failure_code),
3403+
error_code: Some(*failure_code),
33743404
#[cfg(test)]
3375-
error_data: Some(data.clone()),
3376-
}
3377-
);
3405+
error_data: Some(data.clone()),
3406+
});
3407+
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
33783408
}
33793409
}
33803410
},
@@ -4905,14 +4935,19 @@ where
49054935
inbound_payment.expiry_time > header.time as u64
49064936
});
49074937

4938+
let mut pending_events = self.pending_events.lock().unwrap();
49084939
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
4909-
outbounds.retain(|_, payment| {
4910-
const PAYMENT_EXPIRY_BLOCKS: u32 = 3;
4940+
outbounds.retain(|payment_id, payment| {
49114941
if payment.remaining_parts() != 0 { return true }
4912-
if let PendingOutboundPayment::Retryable { starting_block_height, .. } = payment {
4913-
return *starting_block_height + PAYMENT_EXPIRY_BLOCKS > height
4914-
}
4915-
true
4942+
if let PendingOutboundPayment::Retryable { starting_block_height, payment_hash, .. } = payment {
4943+
if *starting_block_height + PAYMENT_EXPIRY_BLOCKS <= height {
4944+
log_info!(self.logger, "Timing out payment with id {} and hash {}", log_bytes!(payment_id.0), log_bytes!(payment_hash.0));
4945+
pending_events.push(events::Event::PaymentFailed {
4946+
payment_id: *payment_id, payment_hash: *payment_hash,
4947+
});
4948+
false
4949+
} else { true }
4950+
} else { true }
49164951
});
49174952
}
49184953

lightning/src/ln/functional_test_utils.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,8 +1212,9 @@ macro_rules! expect_payment_failed_with_update {
12121212
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $scid: expr, $chan_closed: expr) => {
12131213
let events = $node.node.get_and_clear_pending_events();
12141214
assert_eq!(events.len(), 1);
1215+
let our_payment_id;
12151216
match events[0] {
1216-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, .. } => {
1217+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, ref payment_id, .. } => {
12171218
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
12181219
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
12191220
assert!(retry.is_some(), "expected retry.is_some()");
@@ -1233,19 +1234,34 @@ macro_rules! expect_payment_failed_with_update {
12331234
Some(_) => panic!("Unexpected update type"),
12341235
None => panic!("Expected update"),
12351236
}
1237+
our_payment_id = payment_id.unwrap();
12361238
},
12371239
_ => panic!("Unexpected event"),
12381240
}
1241+
$node.node.no_further_payment_retries(our_payment_id);
1242+
let events = $node.node.get_and_clear_pending_events();
1243+
assert_eq!(events.len(), 1);
1244+
match events[0] {
1245+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1246+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected second payment_hash");
1247+
assert_eq!(*payment_id, our_payment_id);
1248+
}
1249+
_ => panic!("Unexpected second event"),
1250+
}
12391251
}
12401252
}
12411253

12421254
#[cfg(test)]
12431255
macro_rules! expect_payment_failed {
12441256
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
1257+
expect_payment_failed!($node, $expected_payment_hash, $rejected_by_dest, false $(, $expected_error_code, $expected_error_data)*);
1258+
};
1259+
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $mpp_parts_remain: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
12451260
let events = $node.node.get_and_clear_pending_events();
12461261
assert_eq!(events.len(), 1);
1262+
let our_payment_id;
12471263
match events[0] {
1248-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, ref path, ref retry, .. } => {
1264+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data, ref path, ref retry, ref payment_id, .. } => {
12491265
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
12501266
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
12511267
assert!(retry.is_some(), "expected retry.is_some()");
@@ -1257,9 +1273,22 @@ macro_rules! expect_payment_failed {
12571273
assert_eq!(error_code.unwrap(), $expected_error_code, "unexpected error code");
12581274
assert_eq!(&error_data.as_ref().unwrap()[..], $expected_error_data, "unexpected error data");
12591275
)*
1276+
our_payment_id = payment_id.unwrap();
12601277
},
12611278
_ => panic!("Unexpected event"),
12621279
}
1280+
if !$mpp_parts_remain {
1281+
$node.node.no_further_payment_retries(our_payment_id);
1282+
let events = $node.node.get_and_clear_pending_events();
1283+
assert_eq!(events.len(), 1);
1284+
match events[0] {
1285+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1286+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected second payment_hash");
1287+
assert_eq!(*payment_id, our_payment_id);
1288+
}
1289+
_ => panic!("Unexpected second event"),
1290+
}
1291+
}
12631292
}
12641293
}
12651294

@@ -1563,17 +1592,31 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
15631592
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
15641593
let events = origin_node.node.get_and_clear_pending_events();
15651594
assert_eq!(events.len(), 1);
1595+
let our_payment_id;
15661596
match events[0] {
1567-
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, .. } => {
1597+
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, ref payment_id, .. } => {
15681598
assert_eq!(payment_hash, our_payment_hash);
15691599
assert!(rejected_by_dest);
15701600
assert_eq!(all_paths_failed, i == expected_paths.len() - 1);
15711601
for (idx, hop) in expected_route.iter().enumerate() {
15721602
assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey);
15731603
}
1604+
our_payment_id = payment_id.unwrap();
15741605
},
15751606
_ => panic!("Unexpected event"),
15761607
}
1608+
if i == expected_paths.len() - 1 {
1609+
origin_node.node.no_further_payment_retries(our_payment_id);
1610+
let events = origin_node.node.get_and_clear_pending_events();
1611+
assert_eq!(events.len(), 1);
1612+
match events[0] {
1613+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1614+
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
1615+
assert_eq!(*payment_id, our_payment_id);
1616+
}
1617+
_ => panic!("Unexpected second event"),
1618+
}
1619+
}
15771620
}
15781621
}
15791622

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};
@@ -3149,9 +3149,10 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
31493149
mine_transaction(&nodes[1], &revoked_local_txn[0]);
31503150
check_added_monitors!(nodes[1], 1);
31513151
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
3152+
assert!(ANTI_REORG_DELAY > PAYMENT_EXPIRY_BLOCKS); // We assume payments will also expire
31523153

31533154
let events = nodes[1].node.get_and_clear_pending_events();
3154-
assert_eq!(events.len(), if deliver_bs_raa { 2 } else { 3 });
3155+
assert_eq!(events.len(), if deliver_bs_raa { 2 } else { 4 });
31553156
match events[0] {
31563157
Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => { },
31573158
_ => panic!("Unexepected event"),
@@ -3164,6 +3165,12 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
31643165
}
31653166
if !deliver_bs_raa {
31663167
match events[2] {
3168+
Event::PaymentFailed { ref payment_hash, .. } => {
3169+
assert_eq!(*payment_hash, fourth_payment_hash);
3170+
},
3171+
_ => panic!("Unexpected event"),
3172+
}
3173+
match events[3] {
31673174
Event::PendingHTLCsForwardable { .. } => { },
31683175
_ => panic!("Unexpected event"),
31693176
};
@@ -4181,7 +4188,14 @@ fn do_test_holding_cell_htlc_add_timeouts(forwarded_htlc: bool) {
41814188
}
41824189
expect_payment_failed_with_update!(nodes[0], second_payment_hash, false, chan_2.0.contents.short_channel_id, false);
41834190
} else {
4184-
expect_payment_failed!(nodes[1], second_payment_hash, true);
4191+
let events = nodes[1].node.get_and_clear_pending_events();
4192+
assert_eq!(events.len(), 2);
4193+
if let Event::PaymentPathFailed { ref payment_hash, .. } = events[0] {
4194+
assert_eq!(*payment_hash, second_payment_hash);
4195+
} else { panic!("Unexpected event"); }
4196+
if let Event::PaymentFailed { ref payment_hash, .. } = events[1] {
4197+
assert_eq!(*payment_hash, second_payment_hash);
4198+
} else { panic!("Unexpected event"); }
41854199
}
41864200
}
41874201

@@ -5837,7 +5851,14 @@ fn do_htlc_claim_previous_remote_commitment_only(use_dust: bool, check_revoke_no
58375851
check_added_monitors!(nodes[0], 1);
58385852
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
58395853
} else {
5840-
expect_payment_failed!(nodes[0], our_payment_hash, true);
5854+
let events = nodes[0].node.get_and_clear_pending_events();
5855+
assert_eq!(events.len(), 2);
5856+
if let Event::PaymentPathFailed { ref payment_hash, .. } = events[0] {
5857+
assert_eq!(*payment_hash, our_payment_hash);
5858+
} else { panic!("Unexpected event"); }
5859+
if let Event::PaymentFailed { ref payment_hash, .. } = events[1] {
5860+
assert_eq!(*payment_hash, our_payment_hash);
5861+
} else { panic!("Unexpected event"); }
58415862
}
58425863
}
58435864

0 commit comments

Comments
 (0)