Skip to content

Commit c828ff4

Browse files
Add all_paths_failed field to PaymentFailed
see field docs for details
1 parent 8f17631 commit c828ff4

File tree

6 files changed

+55
-7
lines changed

6 files changed

+55
-7
lines changed

lightning/src/ln/channelmanager.rs

+5
Original file line numberDiff line numberDiff line change
@@ -2848,6 +2848,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28482848
payment_hash,
28492849
rejected_by_dest: false,
28502850
network_update: None,
2851+
all_paths_failed: sessions.get().len() == 0,
28512852
#[cfg(test)]
28522853
error_code: None,
28532854
#[cfg(test)]
@@ -2886,12 +2887,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28862887
let mut session_priv_bytes = [0; 32];
28872888
session_priv_bytes.copy_from_slice(&session_priv[..]);
28882889
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
2890+
let mut all_paths_failed = false;
28892891
if let hash_map::Entry::Occupied(mut sessions) = outbounds.entry(mpp_id) {
28902892
if !sessions.get_mut().remove(&session_priv_bytes) {
28912893
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
28922894
return;
28932895
}
28942896
if sessions.get().len() == 0 {
2897+
all_paths_failed = true;
28952898
sessions.remove();
28962899
}
28972900
} else {
@@ -2914,6 +2917,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
29142917
payment_hash: payment_hash.clone(),
29152918
rejected_by_dest: !payment_retryable,
29162919
network_update,
2920+
all_paths_failed,
29172921
#[cfg(test)]
29182922
error_code: onion_error_code,
29192923
#[cfg(test)]
@@ -2939,6 +2943,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
29392943
payment_hash: payment_hash.clone(),
29402944
rejected_by_dest: path.len() == 1,
29412945
network_update: None,
2946+
all_paths_failed,
29422947
#[cfg(test)]
29432948
error_code: Some(*failure_code),
29442949
#[cfg(test)]

lightning/src/ln/functional_test_utils.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ macro_rules! expect_payment_failed_with_update {
10411041
let events = $node.node.get_and_clear_pending_events();
10421042
assert_eq!(events.len(), 1);
10431043
match events[0] {
1044-
Event::PaymentFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data } => {
1044+
Event::PaymentFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, .. } => {
10451045
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
10461046
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
10471047
assert!(error_code.is_some(), "expected error_code.is_some() = true");
@@ -1070,7 +1070,7 @@ macro_rules! expect_payment_failed {
10701070
let events = $node.node.get_and_clear_pending_events();
10711071
assert_eq!(events.len(), 1);
10721072
match events[0] {
1073-
Event::PaymentFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data } => {
1073+
Event::PaymentFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, .. } => {
10741074
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
10751075
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
10761076
assert!(error_code.is_some(), "expected error_code.is_some() = true");
@@ -1367,9 +1367,10 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
13671367
let events = origin_node.node.get_and_clear_pending_events();
13681368
assert_eq!(events.len(), 1);
13691369
match events[0] {
1370-
Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
1370+
Event::PaymentFailed { payment_hash, rejected_by_dest, all_paths_failed, .. } => {
13711371
assert_eq!(payment_hash, our_payment_hash);
13721372
assert!(rejected_by_dest);
1373+
assert_eq!(all_paths_failed, i == expected_paths.len() - 1);
13731374
},
13741375
_ => panic!("Unexpected event"),
13751376
}

lightning/src/ln/functional_tests.rs

+32-2
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
@@ -5914,9 +5942,10 @@ fn test_fail_holding_cell_htlc_upon_free() {
59145942
let events = nodes[0].node.get_and_clear_pending_events();
59155943
assert_eq!(events.len(), 1);
59165944
match &events[0] {
5917-
&Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data } => {
5945+
&Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed } => {
59185946
assert_eq!(our_payment_hash.clone(), *payment_hash);
59195947
assert_eq!(*rejected_by_dest, false);
5948+
assert_eq!(*all_paths_failed, true);
59205949
assert_eq!(*network_update, None);
59215950
assert_eq!(*error_code, None);
59225951
assert_eq!(*error_data, None);
@@ -6000,9 +6029,10 @@ fn test_free_and_fail_holding_cell_htlcs() {
60006029
let events = nodes[0].node.get_and_clear_pending_events();
60016030
assert_eq!(events.len(), 1);
60026031
match &events[0] {
6003-
&Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data } => {
6032+
&Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed } => {
60046033
assert_eq!(payment_hash_2.clone(), *payment_hash);
60056034
assert_eq!(*rejected_by_dest, false);
6035+
assert_eq!(*all_paths_failed, true);
60066036
assert_eq!(*network_update, None);
60076037
assert_eq!(*error_code, None);
60086038
assert_eq!(*error_data, None);

lightning/src/ln/onion_route_tests.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case:
163163

164164
let events = nodes[0].node.get_and_clear_pending_events();
165165
assert_eq!(events.len(), 1);
166-
if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref network_update, ref error_code, error_data: _ } = &events[0] {
166+
if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref network_update, ref error_code, error_data: _, ref all_paths_failed } = &events[0] {
167167
assert_eq!(*rejected_by_dest, !expected_retryable);
168+
assert_eq!(*all_paths_failed, true);
168169
assert_eq!(*error_code, expected_error_code);
169170
if expected_channel_update.is_some() {
170171
match network_update {

lightning/src/routing/network_graph.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,7 @@ mod tests {
17281728
net_graph_msg_handler.handle_event(&Event::PaymentFailed {
17291729
payment_hash: PaymentHash([0; 32]),
17301730
rejected_by_dest: false,
1731+
all_paths_failed: true,
17311732
network_update: Some(NetworkUpdate::ChannelUpdateMessage {
17321733
msg: valid_channel_update,
17331734
}),
@@ -1750,6 +1751,7 @@ mod tests {
17501751
net_graph_msg_handler.handle_event(&Event::PaymentFailed {
17511752
payment_hash: PaymentHash([0; 32]),
17521753
rejected_by_dest: false,
1754+
all_paths_failed: true,
17531755
network_update: Some(NetworkUpdate::ChannelClosed {
17541756
short_channel_id,
17551757
is_permanent: false,
@@ -1771,6 +1773,7 @@ mod tests {
17711773
net_graph_msg_handler.handle_event(&Event::PaymentFailed {
17721774
payment_hash: PaymentHash([0; 32]),
17731775
rejected_by_dest: false,
1776+
all_paths_failed: true,
17741777
network_update: Some(NetworkUpdate::ChannelClosed {
17751778
short_channel_id,
17761779
is_permanent: true,

lightning/src/util/events.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ pub enum Event {
141141
/// [`NetworkGraph`]: crate::routing::network_graph::NetworkGraph
142142
/// [`NetGraphMsgHandler`]: crate::routing::network_graph::NetGraphMsgHandler
143143
network_update: Option<NetworkUpdate>,
144+
/// For both single-path and multi-path payments, this is set if all paths of the payment have
145+
/// failed. This will be set to false if (1) this is an MPP payment and (2) other parts of the
146+
/// larger MPP payment were still in flight when this event was generated.
147+
all_paths_failed: bool,
144148
#[cfg(test)]
145149
error_code: Option<u16>,
146150
#[cfg(test)]
@@ -224,7 +228,7 @@ impl Writeable for Event {
224228
(0, payment_preimage, required),
225229
});
226230
},
227-
&Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update,
231+
&Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed,
228232
#[cfg(test)]
229233
ref error_code,
230234
#[cfg(test)]
@@ -239,6 +243,7 @@ impl Writeable for Event {
239243
(0, payment_hash, required),
240244
(1, network_update, option),
241245
(2, rejected_by_dest, required),
246+
(3, all_paths_failed, required),
242247
});
243248
},
244249
&Event::PendingHTLCsForwardable { time_forwardable: _ } => {
@@ -322,15 +327,18 @@ impl MaybeReadable for Event {
322327
let mut payment_hash = PaymentHash([0; 32]);
323328
let mut rejected_by_dest = false;
324329
let mut network_update = None;
330+
let mut all_paths_failed = Some(true);
325331
read_tlv_fields!(reader, {
326332
(0, payment_hash, required),
327333
(1, network_update, ignorable),
328334
(2, rejected_by_dest, required),
335+
(3, all_paths_failed, option),
329336
});
330337
Ok(Some(Event::PaymentFailed {
331338
payment_hash,
332339
rejected_by_dest,
333340
network_update,
341+
all_paths_failed: all_paths_failed.unwrap(),
334342
#[cfg(test)]
335343
error_code,
336344
#[cfg(test)]

0 commit comments

Comments
 (0)