Skip to content

Commit 568a20b

Browse files
authored
Merge pull request #2148 from TheBlueMatt/2023-04-claim-from-closed
Allow claiming a payment if a channel with an HTLC has closed
2 parents 1016e1f + 4a8d01d commit 568a20b

File tree

6 files changed

+191
-57
lines changed

6 files changed

+191
-57
lines changed

lightning/src/events/mod.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,9 @@ pub enum Event {
321321
///
322322
/// # Note
323323
/// LDK will not stop an inbound payment from being paid multiple times, so multiple
324-
/// `PaymentClaimable` events may be generated for the same payment.
324+
/// `PaymentClaimable` events may be generated for the same payment. In such a case it is
325+
/// polite (and required in the lightning specification) to fail the payment the second time
326+
/// and give the sender their money back rather than accepting double payment.
325327
///
326328
/// # Note
327329
/// This event used to be called `PaymentReceived` in LDK versions 0.0.112 and earlier.
@@ -349,6 +351,14 @@ pub enum Event {
349351
via_channel_id: Option<[u8; 32]>,
350352
/// The `user_channel_id` indicating over which channel we received the payment.
351353
via_user_channel_id: Option<u128>,
354+
/// The block height at which this payment will be failed back and will no longer be
355+
/// eligible for claiming.
356+
///
357+
/// Prior to this height, a call to [`ChannelManager::claim_funds`] is guaranteed to
358+
/// succeed, however you should wait for [`Event::PaymentClaimed`] to be sure.
359+
///
360+
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
361+
claim_deadline: Option<u32>,
352362
},
353363
/// Indicates a payment has been claimed and we've received money!
354364
///
@@ -770,7 +780,7 @@ impl Writeable for Event {
770780
// We never write out FundingGenerationReady events as, upon disconnection, peers
771781
// drop any channels which have not yet exchanged funding_signed.
772782
},
773-
&Event::PaymentClaimable { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref via_channel_id, ref via_user_channel_id } => {
783+
&Event::PaymentClaimable { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref via_channel_id, ref via_user_channel_id, ref claim_deadline } => {
774784
1u8.write(writer)?;
775785
let mut payment_secret = None;
776786
let payment_preimage;
@@ -791,6 +801,7 @@ impl Writeable for Event {
791801
(4, amount_msat, required),
792802
(5, via_user_channel_id, option),
793803
(6, 0u64, required), // user_payment_id required for compatibility with 0.0.103 and earlier
804+
(7, claim_deadline, option),
794805
(8, payment_preimage, option),
795806
});
796807
},
@@ -989,6 +1000,7 @@ impl MaybeReadable for Event {
9891000
let mut receiver_node_id = None;
9901001
let mut _user_payment_id = None::<u64>; // For compatibility with 0.0.103 and earlier
9911002
let mut via_channel_id = None;
1003+
let mut claim_deadline = None;
9921004
let mut via_user_channel_id = None;
9931005
read_tlv_fields!(reader, {
9941006
(0, payment_hash, required),
@@ -998,6 +1010,7 @@ impl MaybeReadable for Event {
9981010
(4, amount_msat, required),
9991011
(5, via_user_channel_id, option),
10001012
(6, _user_payment_id, option),
1013+
(7, claim_deadline, option),
10011014
(8, payment_preimage, option),
10021015
});
10031016
let purpose = match payment_secret {
@@ -1015,6 +1028,7 @@ impl MaybeReadable for Event {
10151028
purpose,
10161029
via_channel_id,
10171030
via_user_channel_id,
1031+
claim_deadline,
10181032
}))
10191033
};
10201034
f()

lightning/src/ln/chanmon_update_fail_tests.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) {
208208
let events_3 = nodes[1].node.get_and_clear_pending_events();
209209
assert_eq!(events_3.len(), 1);
210210
match events_3[0] {
211-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
211+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, .. } => {
212212
assert_eq!(payment_hash_1, *payment_hash);
213213
assert_eq!(amount_msat, 1_000_000);
214214
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
@@ -581,7 +581,7 @@ fn do_test_monitor_temporary_update_fail(disconnect_count: usize) {
581581
let events_5 = nodes[1].node.get_and_clear_pending_events();
582582
assert_eq!(events_5.len(), 1);
583583
match events_5[0] {
584-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
584+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, .. } => {
585585
assert_eq!(payment_hash_2, *payment_hash);
586586
assert_eq!(amount_msat, 1_000_000);
587587
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
@@ -699,7 +699,7 @@ fn test_monitor_update_fail_cs() {
699699
let events = nodes[1].node.get_and_clear_pending_events();
700700
assert_eq!(events.len(), 1);
701701
match events[0] {
702-
Event::PaymentClaimable { payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
702+
Event::PaymentClaimable { payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, .. } => {
703703
assert_eq!(payment_hash, our_payment_hash);
704704
assert_eq!(amount_msat, 1_000_000);
705705
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
@@ -1692,7 +1692,7 @@ fn test_monitor_update_fail_claim() {
16921692
let events = nodes[0].node.get_and_clear_pending_events();
16931693
assert_eq!(events.len(), 2);
16941694
match events[0] {
1695-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id } => {
1695+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id, .. } => {
16961696
assert_eq!(payment_hash_2, *payment_hash);
16971697
assert_eq!(1_000_000, amount_msat);
16981698
assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id());
@@ -1709,7 +1709,7 @@ fn test_monitor_update_fail_claim() {
17091709
_ => panic!("Unexpected event"),
17101710
}
17111711
match events[1] {
1712-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
1712+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, .. } => {
17131713
assert_eq!(payment_hash_3, *payment_hash);
17141714
assert_eq!(1_000_000, amount_msat);
17151715
assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id());

lightning/src/ln/channelmanager.rs

+14-40
Original file line numberDiff line numberDiff line change
@@ -3359,8 +3359,10 @@ where
33593359
}
33603360
}
33613361
let mut total_value = claimable_htlc.sender_intended_value;
3362+
let mut earliest_expiry = claimable_htlc.cltv_expiry;
33623363
for htlc in htlcs.iter() {
33633364
total_value += htlc.sender_intended_value;
3365+
earliest_expiry = cmp::min(earliest_expiry, htlc.cltv_expiry);
33643366
match &htlc.onion_payload {
33653367
OnionPayload::Invoice { .. } => {
33663368
if htlc.total_msat != $payment_data.total_msat {
@@ -3393,6 +3395,7 @@ where
33933395
amount_msat,
33943396
via_channel_id: Some(prev_channel_id),
33953397
via_user_channel_id: Some(prev_user_channel_id),
3398+
claim_deadline: Some(earliest_expiry - HTLC_FAIL_BACK_BUFFER),
33963399
});
33973400
payment_claimable_generated = true;
33983401
} else {
@@ -3446,6 +3449,7 @@ where
34463449
hash_map::Entry::Vacant(e) => {
34473450
let amount_msat = claimable_htlc.value;
34483451
claimable_htlc.total_value_received = Some(amount_msat);
3452+
let claim_deadline = Some(claimable_htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER);
34493453
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
34503454
e.insert((purpose.clone(), vec![claimable_htlc]));
34513455
let prev_channel_id = prev_funding_outpoint.to_channel_id();
@@ -3456,6 +3460,7 @@ where
34563460
purpose,
34573461
via_channel_id: Some(prev_channel_id),
34583462
via_user_channel_id: Some(prev_user_channel_id),
3463+
claim_deadline,
34593464
});
34603465
},
34613466
hash_map::Entry::Occupied(_) => {
@@ -3931,16 +3936,18 @@ where
39313936
/// Provides a payment preimage in response to [`Event::PaymentClaimable`], generating any
39323937
/// [`MessageSendEvent`]s needed to claim the payment.
39333938
///
3934-
/// Note that calling this method does *not* guarantee that the payment has been claimed. You
3935-
/// *must* wait for an [`Event::PaymentClaimed`] event which upon a successful claim will be
3936-
/// provided to your [`EventHandler`] when [`process_pending_events`] is next called.
3939+
/// This method is guaranteed to ensure the payment has been claimed but only if the current
3940+
/// height is strictly below [`Event::PaymentClaimable::claim_deadline`]. To avoid race
3941+
/// conditions, you should wait for an [`Event::PaymentClaimed`] before considering the payment
3942+
/// successful. It will generally be available in the next [`process_pending_events`] call.
39373943
///
39383944
/// Note that if you did not set an `amount_msat` when calling [`create_inbound_payment`] or
39393945
/// [`create_inbound_payment_for_hash`] you must check that the amount in the `PaymentClaimable`
39403946
/// event matches your expectation. If you fail to do so and call this method, you may provide
39413947
/// the sender "proof-of-payment" when they did not fulfill the full expected payment.
39423948
///
39433949
/// [`Event::PaymentClaimable`]: crate::events::Event::PaymentClaimable
3950+
/// [`Event::PaymentClaimable::claim_deadline`]: crate::events::Event::PaymentClaimable::claim_deadline
39443951
/// [`Event::PaymentClaimed`]: crate::events::Event::PaymentClaimed
39453952
/// [`process_pending_events`]: EventsProvider::process_pending_events
39463953
/// [`create_inbound_payment`]: Self::create_inbound_payment
@@ -3977,50 +3984,17 @@ where
39773984
};
39783985
debug_assert!(!sources.is_empty());
39793986

3980-
// If we are claiming an MPP payment, we check that all channels which contain a claimable
3981-
// HTLC still exist. While this isn't guaranteed to remain true if a channel closes while
3982-
// we're claiming (or even after we claim, before the commitment update dance completes),
3983-
// it should be a relatively rare race, and we'd rather not claim HTLCs that require us to
3984-
// go on-chain (and lose the on-chain fee to do so) than just reject the payment.
3985-
//
3986-
// Note that we'll still always get our funds - as long as the generated
3987-
// `ChannelMonitorUpdate` makes it out to the relevant monitor we can claim on-chain.
3988-
//
3989-
// If we find an HTLC which we would need to claim but for which we do not have a
3990-
// channel, we will fail all parts of the MPP payment. While we could wait and see if
3991-
// the sender retries the already-failed path(s), it should be a pretty rare case where
3992-
// we got all the HTLCs and then a channel closed while we were waiting for the user to
3993-
// provide the preimage, so worrying too much about the optimal handling isn't worth
3994-
// it.
3987+
// Just in case one HTLC has been failed between when we generated the `PaymentClaimable`
3988+
// and when we got here we need to check that the amount we're about to claim matches the
3989+
// amount we told the user in the last `PaymentClaimable`. We also do a sanity-check that
3990+
// the MPP parts all have the same `total_msat`.
39953991
let mut claimable_amt_msat = 0;
39963992
let mut prev_total_msat = None;
39973993
let mut expected_amt_msat = None;
39983994
let mut valid_mpp = true;
39993995
let mut errs = Vec::new();
40003996
let per_peer_state = self.per_peer_state.read().unwrap();
40013997
for htlc in sources.iter() {
4002-
let (counterparty_node_id, chan_id) = match self.short_to_chan_info.read().unwrap().get(&htlc.prev_hop.short_channel_id) {
4003-
Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
4004-
None => {
4005-
valid_mpp = false;
4006-
break;
4007-
}
4008-
};
4009-
4010-
let peer_state_mutex_opt = per_peer_state.get(&counterparty_node_id);
4011-
if peer_state_mutex_opt.is_none() {
4012-
valid_mpp = false;
4013-
break;
4014-
}
4015-
4016-
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
4017-
let peer_state = &mut *peer_state_lock;
4018-
4019-
if peer_state.channel_by_id.get(&chan_id).is_none() {
4020-
valid_mpp = false;
4021-
break;
4022-
}
4023-
40243998
if prev_total_msat.is_some() && prev_total_msat != Some(htlc.total_msat) {
40253999
log_error!(self.logger, "Somehow ended up with an MPP payment with different expected total amounts - this should not be reachable!");
40264000
debug_assert!(false);

lightning/src/ln/functional_test_utils.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1680,7 +1680,7 @@ macro_rules! expect_payment_claimable {
16801680
let events = $node.node.get_and_clear_pending_events();
16811681
assert_eq!(events.len(), 1);
16821682
match events[0] {
1683-
$crate::events::Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id: _, via_user_channel_id: _ } => {
1683+
$crate::events::Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, .. } => {
16841684
assert_eq!($expected_payment_hash, *payment_hash);
16851685
assert_eq!($expected_recv_value, amount_msat);
16861686
assert_eq!($expected_receiver_node_id, receiver_node_id.unwrap());
@@ -1963,9 +1963,10 @@ pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>,
19631963
payment_id
19641964
}
19651965

1966-
pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, clear_recipient_events: bool, expected_preimage: Option<PaymentPreimage>) {
1966+
pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, clear_recipient_events: bool, expected_preimage: Option<PaymentPreimage>) -> Option<Event> {
19671967
let mut payment_event = SendEvent::from_event(ev);
19681968
let mut prev_node = origin_node;
1969+
let mut event = None;
19691970

19701971
for (idx, &node) in expected_path.iter().enumerate() {
19711972
assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
@@ -1981,7 +1982,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
19811982
if payment_claimable_expected {
19821983
assert_eq!(events_2.len(), 1);
19831984
match events_2[0] {
1984-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, ref via_channel_id, ref via_user_channel_id } => {
1985+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, ref via_channel_id, ref via_user_channel_id, claim_deadline } => {
19851986
assert_eq!(our_payment_hash, *payment_hash);
19861987
assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap());
19871988
match &purpose {
@@ -1997,9 +1998,11 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
19971998
assert_eq!(amount_msat, recv_value);
19981999
assert!(node.node.list_channels().iter().any(|details| details.channel_id == via_channel_id.unwrap()));
19992000
assert!(node.node.list_channels().iter().any(|details| details.user_channel_id == via_user_channel_id.unwrap()));
2001+
assert!(claim_deadline.unwrap() > node.best_block_info().1);
20002002
},
20012003
_ => panic!("Unexpected event"),
20022004
}
2005+
event = Some(events_2[0].clone());
20032006
} else {
20042007
assert!(events_2.is_empty());
20052008
}
@@ -2013,10 +2016,11 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
20132016

20142017
prev_node = node;
20152018
}
2019+
event
20162020
}
20172021

2018-
pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, expected_preimage: Option<PaymentPreimage>) {
2019-
do_pass_along_path(origin_node, expected_path, recv_value, our_payment_hash, our_payment_secret, ev, payment_claimable_expected, true, expected_preimage);
2022+
pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, expected_preimage: Option<PaymentPreimage>) -> Option<Event> {
2023+
do_pass_along_path(origin_node, expected_path, recv_value, our_payment_hash, our_payment_secret, ev, payment_claimable_expected, true, expected_preimage)
20202024
}
20212025

20222026
pub fn pass_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) {

lightning/src/ln/functional_tests.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1997,7 +1997,7 @@ fn test_channel_reserve_holding_cell_htlcs() {
19971997
let events = nodes[2].node.get_and_clear_pending_events();
19981998
assert_eq!(events.len(), 2);
19991999
match events[0] {
2000-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
2000+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, .. } => {
20012001
assert_eq!(our_payment_hash_21, *payment_hash);
20022002
assert_eq!(recv_value_21, amount_msat);
20032003
assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
@@ -2013,7 +2013,7 @@ fn test_channel_reserve_holding_cell_htlcs() {
20132013
_ => panic!("Unexpected event"),
20142014
}
20152015
match events[1] {
2016-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
2016+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, .. } => {
20172017
assert_eq!(our_payment_hash_22, *payment_hash);
20182018
assert_eq!(recv_value_22, amount_msat);
20192019
assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
@@ -3803,7 +3803,7 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken
38033803
let events_2 = nodes[1].node.get_and_clear_pending_events();
38043804
assert_eq!(events_2.len(), 1);
38053805
match events_2[0] {
3806-
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
3806+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, .. } => {
38073807
assert_eq!(payment_hash_1, *payment_hash);
38083808
assert_eq!(amount_msat, 1_000_000);
38093809
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());

0 commit comments

Comments
 (0)