Skip to content

Commit fa1a0d8

Browse files
authored
Merge pull request #1704 from TheBlueMatt/2022-09-always-probe-failed
Dont use PaymentPathFailed a probe fails without making it out
2 parents 5e65c49 + 6601192 commit fa1a0d8

File tree

5 files changed

+106
-88
lines changed

5 files changed

+106
-88
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 31 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3812,62 +3812,17 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
38123812
counterparty_node_id: &PublicKey
38133813
) {
38143814
for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
3815-
match htlc_src {
3816-
HTLCSource::PreviousHopData(HTLCPreviousHopData { .. }) => {
3817-
let (failure_code, onion_failure_data) =
3818-
match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
3819-
hash_map::Entry::Occupied(chan_entry) => {
3820-
self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
3821-
},
3822-
hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
3823-
};
3824-
let channel_state = self.channel_state.lock().unwrap();
3815+
let mut channel_state = self.channel_state.lock().unwrap();
3816+
let (failure_code, onion_failure_data) =
3817+
match channel_state.by_id.entry(channel_id) {
3818+
hash_map::Entry::Occupied(chan_entry) => {
3819+
self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
3820+
},
3821+
hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
3822+
};
38253823

3826-
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id };
3827-
self.fail_htlc_backwards_internal(channel_state, htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data }, receiver)
3828-
},
3829-
HTLCSource::OutboundRoute { session_priv, payment_id, path, payment_params, .. } => {
3830-
let mut session_priv_bytes = [0; 32];
3831-
session_priv_bytes.copy_from_slice(&session_priv[..]);
3832-
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
3833-
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
3834-
if payment.get_mut().remove(&session_priv_bytes, Some(&path)) && !payment.get().is_fulfilled() {
3835-
let retry = if let Some(payment_params_data) = payment_params {
3836-
let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
3837-
Some(RouteParameters {
3838-
payment_params: payment_params_data,
3839-
final_value_msat: path_last_hop.fee_msat,
3840-
final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
3841-
})
3842-
} else { None };
3843-
let mut pending_events = self.pending_events.lock().unwrap();
3844-
pending_events.push(events::Event::PaymentPathFailed {
3845-
payment_id: Some(payment_id),
3846-
payment_hash,
3847-
payment_failed_permanently: false,
3848-
network_update: None,
3849-
all_paths_failed: payment.get().remaining_parts() == 0,
3850-
path: path.clone(),
3851-
short_channel_id: None,
3852-
retry,
3853-
#[cfg(test)]
3854-
error_code: None,
3855-
#[cfg(test)]
3856-
error_data: None,
3857-
});
3858-
if payment.get().abandoned() && payment.get().remaining_parts() == 0 {
3859-
pending_events.push(events::Event::PaymentFailed {
3860-
payment_id,
3861-
payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
3862-
});
3863-
payment.remove();
3864-
}
3865-
}
3866-
} else {
3867-
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
3868-
}
3869-
},
3870-
};
3824+
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id };
3825+
self.fail_htlc_backwards_internal(channel_state, htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data }, receiver);
38713826
}
38723827
}
38733828

@@ -3987,19 +3942,29 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
39873942
// channel here as we apparently can't relay through them anyway.
39883943
let scid = path.first().unwrap().short_channel_id;
39893944
retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
3990-
events::Event::PaymentPathFailed {
3991-
payment_id: Some(payment_id),
3992-
payment_hash: payment_hash.clone(),
3993-
payment_failed_permanently: false,
3994-
network_update: None,
3995-
all_paths_failed,
3996-
path: path.clone(),
3997-
short_channel_id: Some(scid),
3998-
retry,
3945+
3946+
if self.payment_is_probe(payment_hash, &payment_id) {
3947+
events::Event::ProbeFailed {
3948+
payment_id: payment_id,
3949+
payment_hash: payment_hash.clone(),
3950+
path: path.clone(),
3951+
short_channel_id: Some(scid),
3952+
}
3953+
} else {
3954+
events::Event::PaymentPathFailed {
3955+
payment_id: Some(payment_id),
3956+
payment_hash: payment_hash.clone(),
3957+
payment_failed_permanently: false,
3958+
network_update: None,
3959+
all_paths_failed,
3960+
path: path.clone(),
3961+
short_channel_id: Some(scid),
3962+
retry,
39993963
#[cfg(test)]
4000-
error_code: Some(*failure_code),
3964+
error_code: Some(*failure_code),
40013965
#[cfg(test)]
4002-
error_data: Some(data.clone()),
3966+
error_data: Some(data.clone()),
3967+
}
40033968
}
40043969
}
40053970
};

lightning/src/ln/functional_test_utils.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,18 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
304304
fn drop(&mut self) {
305305
if !panicking() {
306306
// Check that we processed all pending events
307-
assert!(self.node.get_and_clear_pending_msg_events().is_empty());
308-
assert!(self.node.get_and_clear_pending_events().is_empty());
309-
assert!(self.chain_monitor.added_monitors.lock().unwrap().is_empty());
307+
let msg_events = self.node.get_and_clear_pending_msg_events();
308+
if !msg_events.is_empty() {
309+
panic!("Had excess message events on node {}: {:?}", self.logger.id, msg_events);
310+
}
311+
let events = self.node.get_and_clear_pending_events();
312+
if !events.is_empty() {
313+
panic!("Had excess events on node {}: {:?}", self.logger.id, events);
314+
}
315+
let added_monitors = self.chain_monitor.added_monitors.lock().unwrap().split_off(0);
316+
if !added_monitors.is_empty() {
317+
panic!("Had {} excess added monitors on node {}", added_monitors.len(), self.logger.id);
318+
}
310319

311320
// Check that if we serialize the Router, we can deserialize it again.
312321
{

lightning/src/ln/functional_tests.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6264,15 +6264,13 @@ fn test_fail_holding_cell_htlc_upon_free() {
62646264
let events = nodes[0].node.get_and_clear_pending_events();
62656265
assert_eq!(events.len(), 1);
62666266
match &events[0] {
6267-
&Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref all_paths_failed, ref short_channel_id, ref error_code, ref error_data, .. } => {
6267+
&Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref all_paths_failed, ref short_channel_id, .. } => {
62686268
assert_eq!(our_payment_id, *payment_id.as_ref().unwrap());
62696269
assert_eq!(our_payment_hash.clone(), *payment_hash);
62706270
assert_eq!(*payment_failed_permanently, false);
62716271
assert_eq!(*all_paths_failed, true);
62726272
assert_eq!(*network_update, None);
6273-
assert_eq!(*short_channel_id, None);
6274-
assert_eq!(*error_code, None);
6275-
assert_eq!(*error_data, None);
6273+
assert_eq!(*short_channel_id, Some(route.paths[0][0].short_channel_id));
62766274
},
62776275
_ => panic!("Unexpected event"),
62786276
}
@@ -6350,15 +6348,13 @@ fn test_free_and_fail_holding_cell_htlcs() {
63506348
let events = nodes[0].node.get_and_clear_pending_events();
63516349
assert_eq!(events.len(), 1);
63526350
match &events[0] {
6353-
&Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref all_paths_failed, ref short_channel_id, ref error_code, ref error_data, .. } => {
6351+
&Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref all_paths_failed, ref short_channel_id, .. } => {
63546352
assert_eq!(payment_id_2, *payment_id.as_ref().unwrap());
63556353
assert_eq!(payment_hash_2.clone(), *payment_hash);
63566354
assert_eq!(*payment_failed_permanently, false);
63576355
assert_eq!(*all_paths_failed, true);
63586356
assert_eq!(*network_update, None);
6359-
assert_eq!(*short_channel_id, None);
6360-
assert_eq!(*error_code, None);
6361-
assert_eq!(*error_data, None);
6357+
assert_eq!(*short_channel_id, Some(route_2.paths[0][0].short_channel_id));
63626358
},
63636359
_ => panic!("Unexpected event"),
63646360
}

lightning/src/ln/payment_tests.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,3 +1200,57 @@ fn failed_probe_yields_event() {
12001200
_ => panic!(),
12011201
};
12021202
}
1203+
1204+
#[test]
1205+
fn onchain_failed_probe_yields_event() {
1206+
// Tests that an attempt to probe over a channel that is eventaully closed results in a failure
1207+
// event.
1208+
let chanmon_cfgs = create_chanmon_cfgs(3);
1209+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
1210+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
1211+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
1212+
1213+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
1214+
create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
1215+
1216+
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id());
1217+
1218+
// Send a dust HTLC, which will be treated as if it timed out once the channel hits the chain.
1219+
let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 1_000, 42);
1220+
let (payment_hash, payment_id) = nodes[0].node.send_probe(route.paths[0].clone()).unwrap();
1221+
1222+
// node[0] -- update_add_htlcs -> node[1]
1223+
check_added_monitors!(nodes[0], 1);
1224+
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
1225+
let probe_event = SendEvent::from_commitment_update(nodes[1].node.get_our_node_id(), updates);
1226+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &probe_event.msgs[0]);
1227+
check_added_monitors!(nodes[1], 0);
1228+
commitment_signed_dance!(nodes[1], nodes[0], probe_event.commitment_msg, false);
1229+
expect_pending_htlcs_forwardable!(nodes[1]);
1230+
1231+
check_added_monitors!(nodes[1], 1);
1232+
let _ = get_htlc_update_msgs!(nodes[1], nodes[2].node.get_our_node_id());
1233+
1234+
// Don't bother forwarding the HTLC onwards and just confirm the force-close transaction on
1235+
// Node A, which after 6 confirmations should result in a probe failure event.
1236+
let bs_txn = get_local_commitment_txn!(nodes[1], chan_id);
1237+
confirm_transaction(&nodes[0], &bs_txn[0]);
1238+
check_closed_broadcast!(&nodes[0], true);
1239+
check_added_monitors!(nodes[0], 1);
1240+
1241+
let mut events = nodes[0].node.get_and_clear_pending_events();
1242+
assert_eq!(events.len(), 2);
1243+
let mut found_probe_failed = false;
1244+
for event in events.drain(..) {
1245+
match event {
1246+
Event::ProbeFailed { payment_id: ev_pid, payment_hash: ev_ph, .. } => {
1247+
assert_eq!(payment_id, ev_pid);
1248+
assert_eq!(payment_hash, ev_ph);
1249+
found_probe_failed = true;
1250+
},
1251+
Event::ChannelClosed { .. } => {},
1252+
_ => panic!(),
1253+
}
1254+
}
1255+
assert!(found_probe_failed);
1256+
}

lightning/src/util/test_utils.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -517,10 +517,7 @@ impl events::MessageSendEventsProvider for TestRoutingMessageHandler {
517517

518518
pub struct TestLogger {
519519
level: Level,
520-
#[cfg(feature = "std")]
521-
id: String,
522-
#[cfg(not(feature = "std"))]
523-
_id: String,
520+
pub(crate) id: String,
524521
pub lines: Mutex<HashMap<(String, String), usize>>,
525522
}
526523

@@ -531,10 +528,7 @@ impl TestLogger {
531528
pub fn with_id(id: String) -> TestLogger {
532529
TestLogger {
533530
level: Level::Trace,
534-
#[cfg(feature = "std")]
535531
id,
536-
#[cfg(not(feature = "std"))]
537-
_id: id,
538532
lines: Mutex::new(HashMap::new())
539533
}
540534
}
@@ -558,10 +552,10 @@ impl TestLogger {
558552
assert_eq!(l, count)
559553
}
560554

561-
/// Search for the number of occurrences of logged lines which
562-
/// 1. belong to the specified module and
563-
/// 2. match the given regex pattern.
564-
/// Assert that the number of occurrences equals the given `count`
555+
/// Search for the number of occurrences of logged lines which
556+
/// 1. belong to the specified module and
557+
/// 2. match the given regex pattern.
558+
/// Assert that the number of occurrences equals the given `count`
565559
pub fn assert_log_regex(&self, module: String, pattern: regex::Regex, count: usize) {
566560
let log_entries = self.lines.lock().unwrap();
567561
let l: usize = log_entries.iter().filter(|&(&(ref m, ref l), _c)| {

0 commit comments

Comments
 (0)