Skip to content

Commit f480b33

Browse files
committed
Add a test for timeout'ing HTLCs which claim to be a part of an MPP
This is a key test for our automatic HTLC time-out logic, as it ensures we don't allow an HTLC which indicates we should wait for additional HTLCs before responding to cause us to force-close a channel due to HTLC near-timeout.
1 parent 6c67fb4 commit f480b33

File tree

3 files changed

+143
-124
lines changed

3 files changed

+143
-124
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 76 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,80 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
12271227
})
12281228
}
12291229

1230+
// Only public for testing, this should otherwise never be called direcly
1231+
pub(crate) fn send_payment_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32) -> Result<(), APIError> {
1232+
log_trace!(self, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
1233+
let (session_priv, prng_seed) = self.keys_manager.get_onion_rand();
1234+
1235+
let onion_keys = onion_utils::construct_onion_keys(&self.secp_ctx, &path, &session_priv)
1236+
.map_err(|_| APIError::RouteError{err: "Pubkey along hop was maliciously selected"})?;
1237+
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(path, total_value, payment_secret, cur_height)?;
1238+
if onion_utils::route_size_insane(&onion_payloads) {
1239+
return Err(APIError::RouteError{err: "Route size too large considering onion data"});
1240+
}
1241+
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
1242+
1243+
let _ = self.total_consistency_lock.read().unwrap();
1244+
1245+
let err: Result<(), _> = loop {
1246+
let mut channel_lock = self.channel_state.lock().unwrap();
1247+
let id = match channel_lock.short_to_id.get(&path.first().unwrap().short_channel_id) {
1248+
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"}),
1249+
Some(id) => id.clone(),
1250+
};
1251+
1252+
let channel_state = &mut *channel_lock;
1253+
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
1254+
match {
1255+
if chan.get().get_their_node_id() != path.first().unwrap().pubkey {
1256+
return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
1257+
}
1258+
if !chan.get().is_live() {
1259+
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!"});
1260+
}
1261+
break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
1262+
path: path.clone(),
1263+
session_priv: session_priv.clone(),
1264+
first_hop_htlc_msat: htlc_msat,
1265+
}, onion_packet), channel_state, chan)
1266+
} {
1267+
Some((update_add, commitment_signed, monitor_update)) => {
1268+
if let Err(e) = self.monitor.update_monitor(chan.get().get_funding_txo().unwrap(), monitor_update) {
1269+
maybe_break_monitor_err!(self, e, channel_state, chan, RAACommitmentOrder::CommitmentFirst, false, true);
1270+
// Note that MonitorUpdateFailed here indicates (per function docs)
1271+
// that we will resend the commitment update once monitor updating
1272+
// is restored. Therefore, we must return an error indicating that
1273+
// it is unsafe to retry the payment wholesale, which we do in the
1274+
// send_payment check for MonitorUpdateFailed, below.
1275+
return Err(APIError::MonitorUpdateFailed);
1276+
}
1277+
1278+
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
1279+
node_id: path.first().unwrap().pubkey,
1280+
updates: msgs::CommitmentUpdate {
1281+
update_add_htlcs: vec![update_add],
1282+
update_fulfill_htlcs: Vec::new(),
1283+
update_fail_htlcs: Vec::new(),
1284+
update_fail_malformed_htlcs: Vec::new(),
1285+
update_fee: None,
1286+
commitment_signed,
1287+
},
1288+
});
1289+
},
1290+
None => {},
1291+
}
1292+
} else { unreachable!(); }
1293+
return Ok(());
1294+
};
1295+
1296+
match handle_error!(self, err, path.first().unwrap().pubkey) {
1297+
Ok(_) => unreachable!(),
1298+
Err(e) => {
1299+
Err(APIError::ChannelUnavailable { err: e.err })
1300+
},
1301+
}
1302+
}
1303+
12301304
/// Sends a payment along a given route.
12311305
///
12321306
/// Value parameters are provided via the last hop in route, see documentation for RouteHop
@@ -1299,89 +1373,8 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
12991373

13001374
let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;
13011375
let mut results = Vec::new();
1302-
'path_loop: for path in route.paths.iter() {
1303-
macro_rules! check_res_push {
1304-
($res: expr) => { match $res {
1305-
Ok(r) => r,
1306-
Err(e) => {
1307-
results.push(Err(e));
1308-
continue 'path_loop;
1309-
},
1310-
}
1311-
}
1312-
}
1313-
1314-
log_trace!(self, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
1315-
let (session_priv, prng_seed) = self.keys_manager.get_onion_rand();
1316-
1317-
let onion_keys = check_res_push!(onion_utils::construct_onion_keys(&self.secp_ctx, &path, &session_priv)
1318-
.map_err(|_| APIError::RouteError{err: "Pubkey along hop was maliciously selected"}));
1319-
let (onion_payloads, htlc_msat, htlc_cltv) = check_res_push!(onion_utils::build_onion_payloads(&path, total_value, payment_secret, cur_height));
1320-
if onion_utils::route_size_insane(&onion_payloads) {
1321-
check_res_push!(Err(APIError::RouteError{err: "Route size too large considering onion data"}));
1322-
}
1323-
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, &payment_hash);
1324-
1325-
let _ = self.total_consistency_lock.read().unwrap();
1326-
1327-
let err: Result<(), _> = loop {
1328-
let mut channel_lock = self.channel_state.lock().unwrap();
1329-
let id = match channel_lock.short_to_id.get(&path.first().unwrap().short_channel_id) {
1330-
None => check_res_push!(Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"})),
1331-
Some(id) => id.clone(),
1332-
};
1333-
1334-
let channel_state = &mut *channel_lock;
1335-
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
1336-
match {
1337-
if chan.get().get_their_node_id() != path.first().unwrap().pubkey {
1338-
check_res_push!(Err(APIError::RouteError{err: "Node ID mismatch on first hop!"}));
1339-
}
1340-
if !chan.get().is_live() {
1341-
check_res_push!(Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!"}));
1342-
}
1343-
break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
1344-
path: path.clone(),
1345-
session_priv: session_priv.clone(),
1346-
first_hop_htlc_msat: htlc_msat,
1347-
}, onion_packet), channel_state, chan)
1348-
} {
1349-
Some((update_add, commitment_signed, monitor_update)) => {
1350-
if let Err(e) = self.monitor.update_monitor(chan.get().get_funding_txo().unwrap(), monitor_update) {
1351-
maybe_break_monitor_err!(self, e, channel_state, chan, RAACommitmentOrder::CommitmentFirst, false, true);
1352-
// Note that MonitorUpdateFailed here indicates (per function docs)
1353-
// that we will resend the commitment update once monitor updating
1354-
// is restored. Therefore, we must return an error indicating that
1355-
// it is unsafe to retry the payment wholesale, which we do in the
1356-
// next check for MonitorUpdateFailed, below.
1357-
check_res_push!(Err(APIError::MonitorUpdateFailed));
1358-
}
1359-
1360-
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
1361-
node_id: path.first().unwrap().pubkey,
1362-
updates: msgs::CommitmentUpdate {
1363-
update_add_htlcs: vec![update_add],
1364-
update_fulfill_htlcs: Vec::new(),
1365-
update_fail_htlcs: Vec::new(),
1366-
update_fail_malformed_htlcs: Vec::new(),
1367-
update_fee: None,
1368-
commitment_signed,
1369-
},
1370-
});
1371-
},
1372-
None => {},
1373-
}
1374-
} else { unreachable!(); }
1375-
results.push(Ok(()));
1376-
continue 'path_loop;
1377-
};
1378-
1379-
match handle_error!(self, err, path.first().unwrap().pubkey) {
1380-
Ok(_) => unreachable!(),
1381-
Err(e) => {
1382-
check_res_push!(Err(APIError::ChannelUnavailable { err: e.err }));
1383-
},
1384-
}
1376+
for path in route.paths.iter() {
1377+
results.push(self.send_payment_path(&path, &payment_hash, payment_secret, total_value, cur_height));
13851378
}
13861379
let mut has_ok = false;
13871380
let mut has_err = false;

lightning/src/ln/functional_test_utils.rs

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -791,49 +791,57 @@ macro_rules! expect_payment_failed {
791791
pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_paths: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>) {
792792
origin_node.node.send_payment(&route, our_payment_hash, &our_payment_secret).unwrap();
793793
check_added_monitors!(origin_node, expected_paths.len());
794+
pass_along_route(origin_node, expected_paths, recv_value, our_payment_hash, our_payment_secret);
795+
}
794796

795-
let mut events = origin_node.node.get_and_clear_pending_msg_events();
796-
assert_eq!(events.len(), expected_paths.len());
797-
for (path_idx, (ev, expected_route)) in events.drain(..).zip(expected_paths.iter()).enumerate() {
798-
let mut payment_event = SendEvent::from_event(ev);
799-
let mut prev_node = origin_node;
800-
801-
for (idx, &node) in expected_route.iter().enumerate() {
802-
assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
803-
804-
node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]);
805-
check_added_monitors!(node, 0);
806-
commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
807-
808-
expect_pending_htlcs_forwardable!(node);
809-
810-
if idx == expected_route.len() - 1 {
811-
let events_2 = node.node.get_and_clear_pending_events();
812-
// Once we've gotten through all the HTLCs, the last one should result in a
813-
// PaymentReceived (but each previous one should not!).
814-
if path_idx == expected_paths.len() - 1 {
815-
assert_eq!(events_2.len(), 1);
816-
match events_2[0] {
817-
Event::PaymentReceived { ref payment_hash, ref payment_secret, amt } => {
818-
assert_eq!(our_payment_hash, *payment_hash);
819-
assert_eq!(our_payment_secret, *payment_secret);
820-
assert_eq!(amt, recv_value);
821-
},
822-
_ => panic!("Unexpected event"),
823-
}
824-
} else {
825-
assert!(events_2.is_empty());
797+
pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_received_expected: bool) {
798+
let mut payment_event = SendEvent::from_event(ev);
799+
let mut prev_node = origin_node;
800+
801+
for (idx, &node) in expected_path.iter().enumerate() {
802+
assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
803+
804+
node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]);
805+
check_added_monitors!(node, 0);
806+
commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
807+
808+
expect_pending_htlcs_forwardable!(node);
809+
810+
if idx == expected_path.len() - 1 {
811+
let events_2 = node.node.get_and_clear_pending_events();
812+
if payment_received_expected {
813+
assert_eq!(events_2.len(), 1);
814+
match events_2[0] {
815+
Event::PaymentReceived { ref payment_hash, ref payment_secret, amt } => {
816+
assert_eq!(our_payment_hash, *payment_hash);
817+
assert_eq!(our_payment_secret, *payment_secret);
818+
assert_eq!(amt, recv_value);
819+
},
820+
_ => panic!("Unexpected event"),
826821
}
827822
} else {
828-
let mut events_2 = node.node.get_and_clear_pending_msg_events();
829-
assert_eq!(events_2.len(), 1);
830-
check_added_monitors!(node, 1);
831-
payment_event = SendEvent::from_event(events_2.remove(0));
832-
assert_eq!(payment_event.msgs.len(), 1);
823+
assert!(events_2.is_empty());
833824
}
834-
835-
prev_node = node;
825+
} else {
826+
let mut events_2 = node.node.get_and_clear_pending_msg_events();
827+
assert_eq!(events_2.len(), 1);
828+
check_added_monitors!(node, 1);
829+
payment_event = SendEvent::from_event(events_2.remove(0));
830+
assert_eq!(payment_event.msgs.len(), 1);
836831
}
832+
833+
prev_node = node;
834+
}
835+
}
836+
837+
pub fn pass_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>) {
838+
let mut events = origin_node.node.get_and_clear_pending_msg_events();
839+
assert_eq!(events.len(), expected_route.len());
840+
for (path_idx, (ev, expected_path)) in events.drain(..).zip(expected_route.iter()).enumerate() {
841+
// Once we've gotten through all the HTLCs, the last one should result in a
842+
// PaymentReceived (but each previous one should not!), .
843+
let expect_payment = path_idx == expected_route.len() - 1;
844+
pass_along_path(origin_node, expected_path, recv_value, our_payment_hash.clone(), our_payment_secret, ev, expect_payment);
837845
}
838846
}
839847

lightning/src/ln/functional_tests.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3623,8 +3623,7 @@ fn test_drop_messages_peer_disconnect_dual_htlc() {
36233623
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2, 1_000_000);
36243624
}
36253625

3626-
#[test]
3627-
fn test_htlc_timeout() {
3626+
fn do_test_htlc_timeout(subpath: bool) {
36283627
// If the user fails to claim/fail an HTLC within the HTLC CLTV timeout we fail it for them
36293628
// to avoid our counterparty failing the channel.
36303629
let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -3633,7 +3632,20 @@ fn test_htlc_timeout() {
36333632
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
36343633

36353634
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::supported(), InitFeatures::supported());
3636-
let (_, our_payment_hash) = route_payment(&nodes[0], &[&nodes[1]], 100000);
3635+
3636+
let our_payment_hash = if subpath {
3637+
let route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &Vec::new(), 100000, TEST_FINAL_CLTV).unwrap();
3638+
let (_, our_payment_hash) = get_payment_preimage_hash!(&nodes[0]);
3639+
let payment_secret = PaymentSecret([0xdb; 32]);
3640+
nodes[0].node.send_payment_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, CHAN_CONFIRM_DEPTH).unwrap();
3641+
check_added_monitors!(nodes[0], 1);
3642+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3643+
assert_eq!(events.len(), 1);
3644+
pass_along_path(&nodes[0], &[&nodes[1]], 100000, our_payment_hash, Some(payment_secret), events.drain(..).next().unwrap(), false);
3645+
our_payment_hash
3646+
} else {
3647+
route_payment(&nodes[0], &[&nodes[1]], 100000).1
3648+
};
36373649

36383650
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
36393651
nodes[0].block_notifier.block_connected_checked(&header, 101, &[], &[]);
@@ -3668,6 +3680,12 @@ fn test_htlc_timeout() {
36683680
0, 0, 0, 123]);
36693681
}
36703682

3683+
#[test]
3684+
fn test_htlc_timeout() {
3685+
do_test_htlc_timeout(true);
3686+
do_test_htlc_timeout(false);
3687+
}
3688+
36713689
#[test]
36723690
fn test_invalid_channel_announcement() {
36733691
//Test BOLT 7 channel_announcement msg requirement for final node, gather data to build customed channel_announcement msgs

0 commit comments

Comments
 (0)