Skip to content

Commit 500f8e9

Browse files
Use MPPFragmentFailed event if other MPP fragments are pending upon one path's failure
1 parent 778141f commit 500f8e9

File tree

4 files changed

+128
-56
lines changed

4 files changed

+128
-56
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 75 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
485485
/// The session_priv bytes of outbound payments which are pending resolution.
486486
/// The authoritative state of these HTLCs resides either within Channels or ChannelMonitors
487487
/// (if the channel has been force-closed), however we track them here to prevent duplicative
488-
/// PaymentSent/PaymentFailed events. Specifically, in the case of a duplicative
488+
/// PaymentSent/MPPFragmentFailed/PaymentFailed events. Specifically, in the case of a duplicative
489489
/// update_fulfill_htlc message after a reconnect, we may "claim" a payment twice.
490490
/// Additionally, because ChannelMonitors are often not re-serialized after connecting block(s)
491491
/// which may generate a claim event, we may receive similar duplicate claim/fail MonitorEvents
@@ -2837,24 +2837,34 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28372837
self.fail_htlc_backwards_internal(channel_state,
28382838
htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data});
28392839
},
2840-
HTLCSource::OutboundRoute { session_priv, mpp_id, .. } => {
2840+
HTLCSource::OutboundRoute { session_priv, mpp_id, path, .. } => {
28412841
let mut session_priv_bytes = [0; 32];
28422842
session_priv_bytes.copy_from_slice(&session_priv[..]);
28432843
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
28442844
if let Some(sessions) = outbounds.get_mut(&mpp_id) {
28452845
if sessions.remove(&session_priv_bytes) {
2846-
self.pending_events.lock().unwrap().push(
2847-
events::Event::PaymentFailed {
2848-
payment_hash,
2849-
rejected_by_dest: false,
2850-
#[cfg(test)]
2851-
error_code: None,
2852-
#[cfg(test)]
2853-
error_data: None,
2846+
match sessions.len() {
2847+
0 => {
2848+
self.pending_events.lock().unwrap().push(
2849+
events::Event::PaymentFailed {
2850+
payment_hash,
2851+
rejected_by_dest: false,
2852+
#[cfg(test)]
2853+
error_code: None,
2854+
#[cfg(test)]
2855+
error_data: None,
2856+
}
2857+
);
2858+
outbounds.remove(&mpp_id);
2859+
},
2860+
_ => {
2861+
self.pending_events.lock().unwrap().push(
2862+
events::Event::MPPFragmentFailed {
2863+
payment_hash,
2864+
payment_path: path.clone()
2865+
}
2866+
);
28542867
}
2855-
);
2856-
if sessions.len() == 0 {
2857-
outbounds.remove(&mpp_id);
28582868
}
28592869
continue
28602870
}
@@ -2885,12 +2895,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28852895
let mut session_priv_bytes = [0; 32];
28862896
session_priv_bytes.copy_from_slice(&session_priv[..]);
28872897
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
2898+
let mut partial_failure = true;
28882899
if let Some(sessions) = outbounds.get_mut(&mpp_id) {
28892900
if !sessions.remove(&session_priv_bytes) {
28902901
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
28912902
return;
28922903
}
28932904
if sessions.len() == 0 {
2905+
partial_failure = false;
28942906
outbounds.remove(&mpp_id);
28952907
}
28962908
} else {
@@ -2915,40 +2927,58 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
29152927
}
29162928
);
29172929
}
2918-
self.pending_events.lock().unwrap().push(
2919-
events::Event::PaymentFailed {
2920-
payment_hash: payment_hash.clone(),
2921-
rejected_by_dest: !payment_retryable,
2922-
#[cfg(test)]
2923-
error_code: onion_error_code,
2924-
#[cfg(test)]
2925-
error_data: onion_error_data
2926-
}
2927-
);
2930+
if partial_failure {
2931+
self.pending_events.lock().unwrap().push(
2932+
events::Event::MPPFragmentFailed {
2933+
payment_hash: payment_hash.clone(),
2934+
payment_path: path.clone()
2935+
}
2936+
);
2937+
} else {
2938+
self.pending_events.lock().unwrap().push(
2939+
events::Event::PaymentFailed {
2940+
payment_hash: payment_hash.clone(),
2941+
rejected_by_dest: !payment_retryable,
2942+
#[cfg(test)]
2943+
error_code: onion_error_code,
2944+
#[cfg(test)]
2945+
error_data: onion_error_data
2946+
}
2947+
);
2948+
}
29282949
},
29292950
&HTLCFailReason::Reason {
2930-
#[cfg(test)]
2931-
ref failure_code,
2932-
#[cfg(test)]
2933-
ref data,
2934-
.. } => {
2935-
// we get a fail_malformed_htlc from the first hop
2936-
// TODO: We'd like to generate a PaymentFailureNetworkUpdate for temporary
2937-
// failures here, but that would be insufficient as get_route
2938-
// generally ignores its view of our own channels as we provide them via
2939-
// ChannelDetails.
2940-
// TODO: For non-temporary failures, we really should be closing the
2941-
// channel here as we apparently can't relay through them anyway.
2942-
self.pending_events.lock().unwrap().push(
2943-
events::Event::PaymentFailed {
2944-
payment_hash: payment_hash.clone(),
2945-
rejected_by_dest: path.len() == 1,
2946-
#[cfg(test)]
2947-
error_code: Some(*failure_code),
2948-
#[cfg(test)]
2949-
error_data: Some(data.clone()),
2950-
}
2951-
);
2951+
#[cfg(test)]
2952+
ref failure_code,
2953+
#[cfg(test)]
2954+
ref data,
2955+
.. } => {
2956+
if partial_failure {
2957+
self.pending_events.lock().unwrap().push(
2958+
events::Event::MPPFragmentFailed {
2959+
payment_hash: payment_hash.clone(),
2960+
payment_path: path.clone()
2961+
}
2962+
);
2963+
} else {
2964+
// we get a fail_malformed_htlc from the first hop
2965+
// TODO: We'd like to generate a PaymentFailureNetworkUpdate for temporary
2966+
// failures here, but that would be insufficient as get_route
2967+
// generally ignores its view of our own channels as we provide them via
2968+
// ChannelDetails.
2969+
// TODO: For non-temporary failures, we really should be closing the
2970+
// channel here as we apparently can't relay through them anyway.
2971+
self.pending_events.lock().unwrap().push(
2972+
events::Event::PaymentFailed {
2973+
payment_hash: payment_hash.clone(),
2974+
rejected_by_dest: path.len() == 1,
2975+
#[cfg(test)]
2976+
error_code: Some(*failure_code),
2977+
#[cfg(test)]
2978+
error_data: Some(data.clone()),
2979+
}
2980+
);
2981+
}
29522982
}
29532983
}
29542984
},

lightning/src/ln/functional_test_utils.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,8 @@ pub fn send_payment<'a, 'b, 'c>(origin: &Node<'a, 'b, 'c>, expected_route: &[&No
12841284
claim_payment(&origin, expected_route, our_payment_preimage);
12851285
}
12861286

1287-
pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) {
1287+
pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) {
1288+
let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect();
12881289
for path in expected_paths.iter() {
12891290
assert_eq!(path.last().unwrap().node.get_our_node_id(), expected_paths[0].last().unwrap().node.get_our_node_id());
12901291
}
@@ -1314,8 +1315,10 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
13141315
for ev in events.iter() {
13151316
per_path_msgs.push(msgs_from_ev!(ev));
13161317
}
1318+
per_path_msgs.sort_unstable_by(|(_, node_id_a), (_, node_id_b)| node_id_a.cmp(node_id_b));
1319+
expected_paths.sort_unstable_by(|path_a, path_b| path_a[path_a.len() - 2].node.get_our_node_id().cmp(&path_b[path_b.len() - 2].node.get_our_node_id()));
13171320

1318-
for (expected_route, (path_msgs, next_hop)) in expected_paths.iter().zip(per_path_msgs.drain(..)) {
1321+
for (i, (expected_route, (path_msgs, next_hop))) in expected_paths.iter().zip(per_path_msgs.drain(..)).enumerate() {
13191322
let mut next_msgs = Some(path_msgs);
13201323
let mut expected_next_node = next_hop;
13211324
let mut prev_node = expected_route.last().unwrap();
@@ -1324,8 +1327,9 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
13241327
assert_eq!(expected_next_node, node.node.get_our_node_id());
13251328
if next_msgs.is_some() {
13261329
node.node.handle_update_fail_htlc(&prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0);
1327-
commitment_signed_dance!(node, prev_node, next_msgs.as_ref().unwrap().1, !(skip_last && idx == expected_route.len() - 1));
1328-
if skip_last && idx == expected_route.len() - 1 {
1330+
let last_node = !(skip_last && idx == expected_route.len() - 1);
1331+
commitment_signed_dance!(node, prev_node, next_msgs.as_ref().unwrap().1, last_node);
1332+
if !last_node {
13291333
expect_pending_htlcs_forwardable!(node);
13301334
}
13311335
}
@@ -1362,12 +1366,21 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
13621366
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
13631367
let events = origin_node.node.get_and_clear_pending_events();
13641368
assert_eq!(events.len(), 1);
1365-
match events[0] {
1366-
Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
1367-
assert_eq!(payment_hash, our_payment_hash);
1368-
assert!(rejected_by_dest);
1369-
},
1370-
_ => panic!("Unexpected event"),
1369+
if i != expected_paths.len() - 1 {
1370+
match events[0] {
1371+
Event::MPPFragmentFailed { payment_hash, .. } => {
1372+
assert_eq!(payment_hash, our_payment_hash);
1373+
},
1374+
_ => panic!("Unexpected event"),
1375+
}
1376+
} else {
1377+
match events[0] {
1378+
Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
1379+
assert_eq!(payment_hash, our_payment_hash);
1380+
assert!(rejected_by_dest);
1381+
},
1382+
_ => panic!("Unexpected event"),
1383+
}
13711384
}
13721385
}
13731386
}

lightning/src/ln/functional_tests.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4165,6 +4165,34 @@ fn test_no_txn_manager_serialize_deserialize() {
41654165
send_payment(&nodes[0], &[&nodes[1]], 1000000);
41664166
}
41674167

4168+
#[test]
4169+
fn mpp_failure() {
4170+
let chanmon_cfgs = create_chanmon_cfgs(4);
4171+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
4172+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
4173+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
4174+
4175+
let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4176+
let chan_2_id = create_announced_chan_between_nodes(&nodes, 0, 2, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4177+
let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4178+
let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4179+
let logger = test_utils::TestLogger::new();
4180+
4181+
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[3]);
4182+
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
4183+
let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
4184+
let path = route.paths[0].clone();
4185+
route.paths.push(path);
4186+
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
4187+
route.paths[0][0].short_channel_id = chan_1_id;
4188+
route.paths[0][1].short_channel_id = chan_3_id;
4189+
route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
4190+
route.paths[1][0].short_channel_id = chan_2_id;
4191+
route.paths[1][1].short_channel_id = chan_4_id;
4192+
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], 200_000, payment_hash, payment_secret);
4193+
fail_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash);
4194+
}
4195+
41684196
#[test]
41694197
fn test_dup_htlc_onchain_fails_on_reload() {
41704198
// When a Channel is closed, any outbound HTLCs which were relayed through it are simply

lightning/src/util/events.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ pub enum Event {
125125
payment_preimage: PaymentPreimage,
126126
},
127127
/// Indicates an outbound payment we made failed. Probably some intermediary node dropped
128-
/// something. You may wish to retry with a different route.
128+
/// something. You may wish to retry with a different route. In the case of MPP payments, this
129+
/// event indicates that all payment paths failed.
129130
PaymentFailed {
130131
/// The hash which was given to ChannelManager::send_payment.
131132
payment_hash: PaymentHash,

0 commit comments

Comments
 (0)