Skip to content

Commit ae070e6

Browse files
Use MPPFragmentFailed event if other MPP fragments are pending upon one path's failure
1 parent 011ce95 commit ae070e6

File tree

4 files changed

+131
-59
lines changed

4 files changed

+131
-59
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 78 additions & 48 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,25 +2837,35 @@ 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-
network_update: None,
2851-
#[cfg(test)]
2852-
error_code: None,
2853-
#[cfg(test)]
2854-
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+
network_update: None,
2853+
#[cfg(test)]
2854+
error_code: None,
2855+
#[cfg(test)]
2856+
error_data: None,
2857+
}
2858+
);
2859+
outbounds.remove(&mpp_id);
2860+
},
2861+
_ => {
2862+
self.pending_events.lock().unwrap().push(
2863+
events::Event::MPPFragmentFailed {
2864+
payment_hash,
2865+
payment_path: path.clone()
2866+
}
2867+
);
28552868
}
2856-
);
2857-
if sessions.len() == 0 {
2858-
outbounds.remove(&mpp_id);
28592869
}
28602870
}
28612871
} else {
@@ -2886,12 +2896,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28862896
let mut session_priv_bytes = [0; 32];
28872897
session_priv_bytes.copy_from_slice(&session_priv[..]);
28882898
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
2899+
let mut partial_failure = true;
28892900
if let Some(sessions) = outbounds.get_mut(&mpp_id) {
28902901
if !sessions.remove(&session_priv_bytes) {
28912902
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
28922903
return;
28932904
}
28942905
if sessions.len() == 0 {
2906+
partial_failure = false;
28952907
outbounds.remove(&mpp_id);
28962908
}
28972909
} else {
@@ -2909,42 +2921,60 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
29092921
// TODO: If we decided to blame ourselves (or one of our channels) in
29102922
// process_onion_failure we should close that channel as it implies our
29112923
// next-hop is needlessly blaming us!
2912-
self.pending_events.lock().unwrap().push(
2913-
events::Event::PaymentFailed {
2914-
payment_hash: payment_hash.clone(),
2915-
rejected_by_dest: !payment_retryable,
2916-
network_update,
2917-
#[cfg(test)]
2918-
error_code: onion_error_code,
2919-
#[cfg(test)]
2920-
error_data: onion_error_data
2921-
}
2922-
);
2924+
if partial_failure {
2925+
self.pending_events.lock().unwrap().push(
2926+
events::Event::MPPFragmentFailed {
2927+
payment_hash: payment_hash.clone(),
2928+
payment_path: path.clone()
2929+
}
2930+
);
2931+
} else {
2932+
self.pending_events.lock().unwrap().push(
2933+
events::Event::PaymentFailed {
2934+
payment_hash: payment_hash.clone(),
2935+
rejected_by_dest: !payment_retryable,
2936+
network_update,
2937+
#[cfg(test)]
2938+
error_code: onion_error_code,
2939+
#[cfg(test)]
2940+
error_data: onion_error_data
2941+
}
2942+
);
2943+
}
29232944
},
29242945
&HTLCFailReason::Reason {
2925-
#[cfg(test)]
2926-
ref failure_code,
2927-
#[cfg(test)]
2928-
ref data,
2929-
.. } => {
2930-
// we get a fail_malformed_htlc from the first hop
2931-
// TODO: We'd like to generate a NetworkUpdate for temporary
2932-
// failures here, but that would be insufficient as get_route
2933-
// generally ignores its view of our own channels as we provide them via
2934-
// ChannelDetails.
2935-
// TODO: For non-temporary failures, we really should be closing the
2936-
// channel here as we apparently can't relay through them anyway.
2937-
self.pending_events.lock().unwrap().push(
2938-
events::Event::PaymentFailed {
2939-
payment_hash: payment_hash.clone(),
2940-
rejected_by_dest: path.len() == 1,
2941-
network_update: None,
2942-
#[cfg(test)]
2943-
error_code: Some(*failure_code),
2944-
#[cfg(test)]
2945-
error_data: Some(data.clone()),
2946-
}
2947-
);
2946+
#[cfg(test)]
2947+
ref failure_code,
2948+
#[cfg(test)]
2949+
ref data,
2950+
.. } => {
2951+
if partial_failure {
2952+
self.pending_events.lock().unwrap().push(
2953+
events::Event::MPPFragmentFailed {
2954+
payment_hash: payment_hash.clone(),
2955+
payment_path: path.clone()
2956+
}
2957+
);
2958+
} else {
2959+
// we get a fail_malformed_htlc from the first hop
2960+
// TODO: We'd like to generate a PaymentFailureNetworkUpdate for temporary
2961+
// failures here, but that would be insufficient as get_route
2962+
// generally ignores its view of our own channels as we provide them via
2963+
// ChannelDetails.
2964+
// TODO: For non-temporary failures, we really should be closing the
2965+
// channel here as we apparently can't relay through them anyway.
2966+
self.pending_events.lock().unwrap().push(
2967+
events::Event::PaymentFailed {
2968+
payment_hash: payment_hash.clone(),
2969+
rejected_by_dest: path.len() == 1,
2970+
network_update: None,
2971+
#[cfg(test)]
2972+
error_code: Some(*failure_code),
2973+
#[cfg(test)]
2974+
error_data: Some(data.clone()),
2975+
}
2976+
);
2977+
}
29482978
}
29492979
}
29502980
},

lightning/src/ln/functional_test_utils.rs

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

1292-
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) {
1292+
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) {
1293+
let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect();
12931294
for path in expected_paths.iter() {
12941295
assert_eq!(path.last().unwrap().node.get_our_node_id(), expected_paths[0].last().unwrap().node.get_our_node_id());
12951296
}
@@ -1319,8 +1320,10 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
13191320
for ev in events.iter() {
13201321
per_path_msgs.push(msgs_from_ev!(ev));
13211322
}
1323+
per_path_msgs.sort_unstable_by(|(_, node_id_a), (_, node_id_b)| node_id_a.cmp(node_id_b));
1324+
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()));
13221325

1323-
for (expected_route, (path_msgs, next_hop)) in expected_paths.iter().zip(per_path_msgs.drain(..)) {
1326+
for (i, (expected_route, (path_msgs, next_hop))) in expected_paths.iter().zip(per_path_msgs.drain(..)).enumerate() {
13241327
let mut next_msgs = Some(path_msgs);
13251328
let mut expected_next_node = next_hop;
13261329
let mut prev_node = expected_route.last().unwrap();
@@ -1329,8 +1332,9 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
13291332
assert_eq!(expected_next_node, node.node.get_our_node_id());
13301333
if next_msgs.is_some() {
13311334
node.node.handle_update_fail_htlc(&prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0);
1332-
commitment_signed_dance!(node, prev_node, next_msgs.as_ref().unwrap().1, !(skip_last && idx == expected_route.len() - 1));
1333-
if skip_last && idx == expected_route.len() - 1 {
1335+
let last_node = !(skip_last && idx == expected_route.len() - 1);
1336+
commitment_signed_dance!(node, prev_node, next_msgs.as_ref().unwrap().1, last_node);
1337+
if !last_node {
13341338
expect_pending_htlcs_forwardable!(node);
13351339
}
13361340
}
@@ -1367,12 +1371,21 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
13671371
commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
13681372
let events = origin_node.node.get_and_clear_pending_events();
13691373
assert_eq!(events.len(), 1);
1370-
match events[0] {
1371-
Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
1372-
assert_eq!(payment_hash, our_payment_hash);
1373-
assert!(rejected_by_dest);
1374-
},
1375-
_ => panic!("Unexpected event"),
1374+
if i != expected_paths.len() - 1 {
1375+
match events[0] {
1376+
Event::MPPFragmentFailed { payment_hash, .. } => {
1377+
assert_eq!(payment_hash, our_payment_hash);
1378+
},
1379+
_ => panic!("Unexpected event"),
1380+
}
1381+
} else {
1382+
match events[0] {
1383+
Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
1384+
assert_eq!(payment_hash, our_payment_hash);
1385+
assert!(rejected_by_dest);
1386+
},
1387+
_ => panic!("Unexpected event"),
1388+
}
13761389
}
13771390
}
13781391
}

lightning/src/ln/functional_tests.rs

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

4087+
#[test]
4088+
fn mpp_failure() {
4089+
let chanmon_cfgs = create_chanmon_cfgs(4);
4090+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
4091+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
4092+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
4093+
4094+
let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4095+
let chan_2_id = create_announced_chan_between_nodes(&nodes, 0, 2, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4096+
let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4097+
let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
4098+
let logger = test_utils::TestLogger::new();
4099+
4100+
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[3]);
4101+
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
4102+
let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
4103+
let path = route.paths[0].clone();
4104+
route.paths.push(path);
4105+
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
4106+
route.paths[0][0].short_channel_id = chan_1_id;
4107+
route.paths[0][1].short_channel_id = chan_3_id;
4108+
route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
4109+
route.paths[1][0].short_channel_id = chan_2_id;
4110+
route.paths[1][1].short_channel_id = chan_4_id;
4111+
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], 200_000, payment_hash, payment_secret);
4112+
fail_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash);
4113+
}
4114+
40874115
#[test]
40884116
fn test_dup_htlc_onchain_fails_on_reload() {
40894117
// 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
@@ -126,7 +126,8 @@ pub enum Event {
126126
payment_preimage: PaymentPreimage,
127127
},
128128
/// Indicates an outbound payment we made failed. Probably some intermediary node dropped
129-
/// something. You may wish to retry with a different route.
129+
/// something. You may wish to retry with a different route. In the case of MPP payments, this
130+
/// event indicates that all payment paths failed.
130131
PaymentFailed {
131132
/// The hash which was given to ChannelManager::send_payment.
132133
payment_hash: PaymentHash,

0 commit comments

Comments
 (0)