Skip to content

Commit 74b9c1a

Browse files
Merge pull request #1353 from dunxen/2022-03-mpp-receive-timeout
Add MPP receive timeout handling
2 parents a584901 + 4086ce8 commit 74b9c1a

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ struct ClaimableHTLC {
441441
cltv_expiry: u32,
442442
value: u64,
443443
onion_payload: OnionPayload,
444+
timer_ticks: u8,
444445
}
445446

446447
/// A payment identifier used to uniquely identify a payment to LDK.
@@ -1148,6 +1149,9 @@ const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_G
11481149
/// pending HTLCs in flight.
11491150
pub(crate) const PAYMENT_EXPIRY_BLOCKS: u32 = 3;
11501151

1152+
/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until expiry of incomplete MPPs
1153+
pub(crate) const MPP_TIMEOUT_TICKS: u8 = 3;
1154+
11511155
/// Information needed for constructing an invoice route hint for this channel.
11521156
#[derive(Clone, Debug, PartialEq)]
11531157
pub struct CounterpartyForwardingInfo {
@@ -3326,6 +3330,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33263330
phantom_shared_secret,
33273331
},
33283332
value: amt_to_forward,
3333+
timer_ticks: 0,
33293334
cltv_expiry,
33303335
onion_payload,
33313336
};
@@ -3620,6 +3625,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
36203625
let new_feerate = self.fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
36213626

36223627
let mut handle_errors = Vec::new();
3628+
let mut timed_out_mpp_htlcs = Vec::new();
36233629
{
36243630
let mut channel_state_lock = self.channel_state.lock().unwrap();
36253631
let channel_state = &mut *channel_state_lock;
@@ -3668,6 +3674,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
36683674

36693675
true
36703676
});
3677+
3678+
channel_state.claimable_htlcs.retain(|payment_hash, htlcs| {
3679+
if htlcs.is_empty() {
3680+
// This should be unreachable
3681+
debug_assert!(false);
3682+
return false;
3683+
}
3684+
if let OnionPayload::Invoice(ref final_hop_data) = htlcs[0].onion_payload {
3685+
// Check if we've received all the parts we need for an MPP (the value of the parts adds to total_msat).
3686+
// In this case we're not going to handle any timeouts of the parts here.
3687+
if final_hop_data.total_msat == htlcs.iter().fold(0, |total, htlc| total + htlc.value) {
3688+
return true;
3689+
} else if htlcs.into_iter().any(|htlc| {
3690+
htlc.timer_ticks += 1;
3691+
return htlc.timer_ticks >= MPP_TIMEOUT_TICKS
3692+
}) {
3693+
timed_out_mpp_htlcs.extend(htlcs.into_iter().map(|htlc| (htlc.prev_hop.clone(), payment_hash.clone())));
3694+
return false;
3695+
}
3696+
}
3697+
true
3698+
});
3699+
}
3700+
3701+
for htlc_source in timed_out_mpp_htlcs.drain(..) {
3702+
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), HTLCSource::PreviousHopData(htlc_source.0), &htlc_source.1, HTLCFailReason::Reason { failure_code: 23, data: Vec::new() });
36713703
}
36723704

36733705
for (err, counterparty_node_id) in handle_errors.drain(..) {
@@ -6240,6 +6272,7 @@ impl Readable for ClaimableHTLC {
62406272
};
62416273
Ok(Self {
62426274
prev_hop: prev_hop.0.unwrap(),
6275+
timer_ticks: 0,
62436276
value,
62446277
onion_payload,
62456278
cltv_expiry,

lightning/src/ln/payment_tests.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use chain::{ChannelMonitorUpdateErr, Confirm, Listen, Watch};
1515
use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
1616
use chain::transaction::OutPoint;
1717
use chain::keysinterface::KeysInterface;
18-
use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, PaymentId, PaymentSendFailure};
18+
use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, MPP_TIMEOUT_TICKS, PaymentId, PaymentSendFailure};
1919
use ln::features::{InitFeatures, InvoiceFeatures};
2020
use ln::msgs;
2121
use ln::msgs::ChannelMessageHandler;
@@ -199,6 +199,78 @@ fn mpp_retry() {
199199
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
200200
}
201201

202+
fn do_mpp_receive_timeout(send_partial_mpp: bool) {
203+
let chanmon_cfgs = create_chanmon_cfgs(4);
204+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
205+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
206+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
207+
208+
let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
209+
let chan_2_id = create_announced_chan_between_nodes(&nodes, 0, 2, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
210+
let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
211+
let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
212+
213+
let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[3], 100_000);
214+
let path = route.paths[0].clone();
215+
route.paths.push(path);
216+
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
217+
route.paths[0][0].short_channel_id = chan_1_id;
218+
route.paths[0][1].short_channel_id = chan_3_id;
219+
route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
220+
route.paths[1][0].short_channel_id = chan_2_id;
221+
route.paths[1][1].short_channel_id = chan_4_id;
222+
223+
// Initiate the MPP payment.
224+
let _ = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
225+
check_added_monitors!(nodes[0], 2); // one monitor per path
226+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
227+
assert_eq!(events.len(), 2);
228+
229+
// Pass half of the payment along the first path.
230+
pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 200_000, payment_hash, Some(payment_secret), events.remove(0), false, None);
231+
232+
if send_partial_mpp {
233+
// Time out the partial MPP
234+
for _ in 0..MPP_TIMEOUT_TICKS {
235+
nodes[3].node.timer_tick_occurred();
236+
}
237+
238+
// Failed HTLC from node 3 -> 1
239+
expect_pending_htlcs_forwardable!(nodes[3]);
240+
let htlc_fail_updates_3_1 = get_htlc_update_msgs!(nodes[3], nodes[1].node.get_our_node_id());
241+
assert_eq!(htlc_fail_updates_3_1.update_fail_htlcs.len(), 1);
242+
nodes[1].node.handle_update_fail_htlc(&nodes[3].node.get_our_node_id(), &htlc_fail_updates_3_1.update_fail_htlcs[0]);
243+
check_added_monitors!(nodes[3], 1);
244+
commitment_signed_dance!(nodes[1], nodes[3], htlc_fail_updates_3_1.commitment_signed, false);
245+
246+
// Failed HTLC from node 1 -> 0
247+
expect_pending_htlcs_forwardable!(nodes[1]);
248+
let htlc_fail_updates_1_0 = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
249+
assert_eq!(htlc_fail_updates_1_0.update_fail_htlcs.len(), 1);
250+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates_1_0.update_fail_htlcs[0]);
251+
check_added_monitors!(nodes[1], 1);
252+
commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates_1_0.commitment_signed, false);
253+
254+
expect_payment_failed_conditions!(nodes[0], payment_hash, false, PaymentFailedConditions::new().mpp_parts_remain().expected_htlc_error_data(23, &[][..]));
255+
} else {
256+
// Pass half of the payment along the second path.
257+
pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 200_000, payment_hash, Some(payment_secret), events.remove(0), true, None);
258+
259+
// Even after MPP_TIMEOUT_TICKS we should not timeout the MPP if we have all the parts
260+
for _ in 0..MPP_TIMEOUT_TICKS {
261+
nodes[3].node.timer_tick_occurred();
262+
}
263+
264+
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
265+
}
266+
}
267+
268+
#[test]
269+
fn mpp_receive_timeout() {
270+
do_mpp_receive_timeout(true);
271+
do_mpp_receive_timeout(false);
272+
}
273+
202274
#[test]
203275
fn retry_expired_payment() {
204276
let chanmon_cfgs = create_chanmon_cfgs(3);

0 commit comments

Comments
 (0)