Skip to content

Commit 7782d0a

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 0b3240e commit 7782d0a

File tree

5 files changed

+225
-68
lines changed

5 files changed

+225
-68
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 93 additions & 54 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 {
@@ -2411,15 +2415,31 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
24112415
/// Signals that no further retries for the given payment will occur.
24122416
///
24132417
/// After this method returns, any future calls to [`retry_payment`] for the given `payment_id`
2414-
/// will fail with [`PaymentSendFailure::ParameterError`].
2418+
/// will fail with [`PaymentSendFailure::ParameterError`]. If no such event has been generated,
2419+
/// an [`Event::PaymentFailed`] event will be generated as soon as there are no remaining
2420+
/// pending HTLCs for this payment.
2421+
///
2422+
/// Note that calling this method does *not* prevent a payment from succeeding. You must still
2423+
/// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
2424+
/// determine the ultimate status of a payment.
24152425
///
24162426
/// [`retry_payment`]: Self::retry_payment
2427+
/// [`Event::PaymentFailed`]: events::Event::PaymentFailed
2428+
/// [`Event::PaymentSent`]: events::Event::PaymentSent
24172429
pub fn abandon_payment(&self, payment_id: PaymentId) {
24182430
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
24192431

24202432
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
24212433
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
2422-
let _ = payment.get_mut().mark_abandoned();
2434+
if let Ok(()) = payment.get_mut().mark_abandoned() {
2435+
if payment.get().remaining_parts() == 0 {
2436+
self.pending_events.lock().unwrap().push(events::Event::PaymentFailed {
2437+
payment_id,
2438+
payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
2439+
});
2440+
payment.remove();
2441+
}
2442+
}
24232443
}
24242444
}
24252445

@@ -3250,22 +3270,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32503270
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
32513271
})
32523272
} else { None };
3253-
self.pending_events.lock().unwrap().push(
3254-
events::Event::PaymentPathFailed {
3255-
payment_id: Some(payment_id),
3256-
payment_hash,
3257-
rejected_by_dest: false,
3258-
network_update: None,
3259-
all_paths_failed: payment.get().remaining_parts() == 0,
3260-
path: path.clone(),
3261-
short_channel_id: None,
3262-
retry,
3263-
#[cfg(test)]
3264-
error_code: None,
3265-
#[cfg(test)]
3266-
error_data: None,
3267-
}
3268-
);
3273+
let mut pending_events = self.pending_events.lock().unwrap();
3274+
pending_events.push(events::Event::PaymentPathFailed {
3275+
payment_id: Some(payment_id),
3276+
payment_hash,
3277+
rejected_by_dest: false,
3278+
network_update: None,
3279+
all_paths_failed: payment.get().remaining_parts() == 0,
3280+
path: path.clone(),
3281+
short_channel_id: None,
3282+
retry,
3283+
#[cfg(test)]
3284+
error_code: None,
3285+
#[cfg(test)]
3286+
error_data: None,
3287+
});
3288+
if payment.get().abandoned() && payment.get().remaining_parts() == 0 {
3289+
pending_events.push(events::Event::PaymentFailed {
3290+
payment_id,
3291+
payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
3292+
});
3293+
payment.remove();
3294+
}
32693295
}
32703296
} else {
32713297
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3296,6 +3322,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32963322
session_priv_bytes.copy_from_slice(&session_priv[..]);
32973323
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
32983324
let mut all_paths_failed = false;
3325+
let mut full_failure_ev = None;
32993326
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
33003327
if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
33013328
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3307,6 +3334,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33073334
}
33083335
if payment.get().remaining_parts() == 0 {
33093336
all_paths_failed = true;
3337+
if payment.get().abandoned() {
3338+
full_failure_ev = Some(events::Event::PaymentFailed {
3339+
payment_id,
3340+
payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
3341+
});
3342+
payment.remove();
3343+
}
33103344
}
33113345
} else {
33123346
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
@@ -3322,7 +3356,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33223356
})
33233357
} else { None };
33243358
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
3325-
match &onion_error {
3359+
3360+
let path_failure = match &onion_error {
33263361
&HTLCFailReason::LightningError { ref err } => {
33273362
#[cfg(test)]
33283363
let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
@@ -3331,22 +3366,20 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33313366
// TODO: If we decided to blame ourselves (or one of our channels) in
33323367
// process_onion_failure we should close that channel as it implies our
33333368
// next-hop is needlessly blaming us!
3334-
self.pending_events.lock().unwrap().push(
3335-
events::Event::PaymentPathFailed {
3336-
payment_id: Some(payment_id),
3337-
payment_hash: payment_hash.clone(),
3338-
rejected_by_dest: !payment_retryable,
3339-
network_update,
3340-
all_paths_failed,
3341-
path: path.clone(),
3342-
short_channel_id,
3343-
retry,
3369+
events::Event::PaymentPathFailed {
3370+
payment_id: Some(payment_id),
3371+
payment_hash: payment_hash.clone(),
3372+
rejected_by_dest: !payment_retryable,
3373+
network_update,
3374+
all_paths_failed,
3375+
path: path.clone(),
3376+
short_channel_id,
3377+
retry,
33443378
#[cfg(test)]
3345-
error_code: onion_error_code,
3379+
error_code: onion_error_code,
33463380
#[cfg(test)]
3347-
error_data: onion_error_data
3348-
}
3349-
);
3381+
error_data: onion_error_data
3382+
}
33503383
},
33513384
&HTLCFailReason::Reason {
33523385
#[cfg(test)]
@@ -3361,24 +3394,25 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33613394
// ChannelDetails.
33623395
// TODO: For non-temporary failures, we really should be closing the
33633396
// channel here as we apparently can't relay through them anyway.
3364-
self.pending_events.lock().unwrap().push(
3365-
events::Event::PaymentPathFailed {
3366-
payment_id: Some(payment_id),
3367-
payment_hash: payment_hash.clone(),
3368-
rejected_by_dest: path.len() == 1,
3369-
network_update: None,
3370-
all_paths_failed,
3371-
path: path.clone(),
3372-
short_channel_id: Some(path.first().unwrap().short_channel_id),
3373-
retry,
3397+
events::Event::PaymentPathFailed {
3398+
payment_id: Some(payment_id),
3399+
payment_hash: payment_hash.clone(),
3400+
rejected_by_dest: path.len() == 1,
3401+
network_update: None,
3402+
all_paths_failed,
3403+
path: path.clone(),
3404+
short_channel_id: Some(path.first().unwrap().short_channel_id),
3405+
retry,
33743406
#[cfg(test)]
3375-
error_code: Some(*failure_code),
3407+
error_code: Some(*failure_code),
33763408
#[cfg(test)]
3377-
error_data: Some(data.clone()),
3378-
}
3379-
);
3409+
error_data: Some(data.clone()),
3410+
}
33803411
}
3381-
}
3412+
};
3413+
let mut pending_events = self.pending_events.lock().unwrap();
3414+
pending_events.push(path_failure);
3415+
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
33823416
},
33833417
HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id, htlc_id, incoming_packet_shared_secret, .. }) => {
33843418
let err_packet = match onion_error {
@@ -4907,14 +4941,19 @@ where
49074941
inbound_payment.expiry_time > header.time as u64
49084942
});
49094943

4944+
let mut pending_events = self.pending_events.lock().unwrap();
49104945
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
4911-
outbounds.retain(|_, payment| {
4912-
const PAYMENT_EXPIRY_BLOCKS: u32 = 3;
4946+
outbounds.retain(|payment_id, payment| {
49134947
if payment.remaining_parts() != 0 { return true }
4914-
if let PendingOutboundPayment::Retryable { starting_block_height, .. } = payment {
4915-
return *starting_block_height + PAYMENT_EXPIRY_BLOCKS > height
4916-
}
4917-
true
4948+
if let PendingOutboundPayment::Retryable { starting_block_height, payment_hash, .. } = payment {
4949+
if *starting_block_height + PAYMENT_EXPIRY_BLOCKS <= height {
4950+
log_info!(self.logger, "Timing out payment with id {} and hash {}", log_bytes!(payment_id.0), log_bytes!(payment_hash.0));
4951+
pending_events.push(events::Event::PaymentFailed {
4952+
payment_id: *payment_id, payment_hash: *payment_hash,
4953+
});
4954+
false
4955+
} else { true }
4956+
} else { true }
49184957
});
49194958
}
49204959

lightning/src/ln/functional_test_utils.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,7 @@ pub struct PaymentFailedConditions<'a> {
12111211
pub(crate) expected_htlc_error_data: Option<(u16, &'a [u8])>,
12121212
pub(crate) expected_blamed_scid: Option<u64>,
12131213
pub(crate) expected_blamed_chan_closed: Option<bool>,
1214+
pub(crate) expected_mpp_parts_remain: bool,
12141215
}
12151216

12161217
impl<'a> PaymentFailedConditions<'a> {
@@ -1219,8 +1220,13 @@ impl<'a> PaymentFailedConditions<'a> {
12191220
expected_htlc_error_data: None,
12201221
expected_blamed_scid: None,
12211222
expected_blamed_chan_closed: None,
1223+
expected_mpp_parts_remain: false,
12221224
}
12231225
}
1226+
pub fn mpp_parts_remain(mut self) -> Self {
1227+
self.expected_mpp_parts_remain = true;
1228+
self
1229+
}
12241230
pub fn blamed_scid(mut self, scid: u64) -> Self {
12251231
self.expected_blamed_scid = Some(scid);
12261232
self
@@ -1246,6 +1252,7 @@ macro_rules! expect_payment_failed_with_update {
12461252
#[cfg(test)]
12471253
macro_rules! expect_payment_failed {
12481254
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr $(, $expected_error_code: expr, $expected_error_data: expr)*) => {
1255+
#[allow(unused_mut)]
12491256
let mut conditions = $crate::ln::functional_test_utils::PaymentFailedConditions::new();
12501257
$(
12511258
conditions = conditions.expected_htlc_error_data($expected_error_code, &$expected_error_data);
@@ -1259,8 +1266,8 @@ macro_rules! expect_payment_failed_conditions {
12591266
($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $conditions: expr) => {
12601267
let events = $node.node.get_and_clear_pending_events();
12611268
assert_eq!(events.len(), 1);
1262-
match events[0] {
1263-
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data, ref path, ref retry, ref network_update, .. } => {
1269+
let expected_payment_id = match events[0] {
1270+
Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data, ref path, ref retry, ref payment_id, ref network_update, .. } => {
12641271
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
12651272
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
12661273
assert!(retry.is_some(), "expected retry.is_some()");
@@ -1292,10 +1299,24 @@ macro_rules! expect_payment_failed_conditions {
12921299
None => panic!("Expected update"),
12931300
}
12941301
}
1302+
1303+
payment_id.unwrap()
12951304
},
12961305
_ => panic!("Unexpected event"),
12971306
};
1298-
};
1307+
if !$conditions.expected_mpp_parts_remain {
1308+
$node.node.abandon_payment(expected_payment_id);
1309+
let events = $node.node.get_and_clear_pending_events();
1310+
assert_eq!(events.len(), 1);
1311+
match events[0] {
1312+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1313+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected second payment_hash");
1314+
assert_eq!(*payment_id, expected_payment_id);
1315+
}
1316+
_ => panic!("Unexpected second event"),
1317+
}
1318+
}
1319+
}
12991320
}
13001321

13011322
pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_paths: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) -> PaymentId {
@@ -1598,16 +1619,29 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
15981619
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
15991620
let events = origin_node.node.get_and_clear_pending_events();
16001621
assert_eq!(events.len(), 1);
1601-
match events[0] {
1602-
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, .. } => {
1622+
let expected_payment_id = match events[0] {
1623+
Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, ref payment_id, .. } => {
16031624
assert_eq!(payment_hash, our_payment_hash);
16041625
assert!(rejected_by_dest);
16051626
assert_eq!(all_paths_failed, i == expected_paths.len() - 1);
16061627
for (idx, hop) in expected_route.iter().enumerate() {
16071628
assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey);
16081629
}
1630+
payment_id.unwrap()
16091631
},
16101632
_ => panic!("Unexpected event"),
1633+
};
1634+
if i == expected_paths.len() - 1 {
1635+
origin_node.node.abandon_payment(expected_payment_id);
1636+
let events = origin_node.node.get_and_clear_pending_events();
1637+
assert_eq!(events.len(), 1);
1638+
match events[0] {
1639+
Event::PaymentFailed { ref payment_hash, ref payment_id } => {
1640+
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
1641+
assert_eq!(*payment_id, expected_payment_id);
1642+
}
1643+
_ => panic!("Unexpected second event"),
1644+
}
16111645
}
16121646
}
16131647
}

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)