Skip to content

Commit 6a6b17b

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 708d511 commit 6a6b17b

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
@@ -779,49 +779,57 @@ macro_rules! expect_payment_failed {
779779
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>) {
780780
origin_node.node.send_payment(&route, our_payment_hash, &our_payment_secret).unwrap();
781781
check_added_monitors!(origin_node, expected_paths.len());
782+
pass_along_route(origin_node, expected_paths, recv_value, our_payment_hash, our_payment_secret);
783+
}
782784

783-
let mut events = origin_node.node.get_and_clear_pending_msg_events();
784-
assert_eq!(events.len(), expected_paths.len());
785-
for (path_idx, (ev, expected_route)) in events.drain(..).zip(expected_paths.iter()).enumerate() {
786-
let mut payment_event = SendEvent::from_event(ev);
787-
let mut prev_node = origin_node;
788-
789-
for (idx, &node) in expected_route.iter().enumerate() {
790-
assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
791-
792-
node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]);
793-
check_added_monitors!(node, 0);
794-
commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
795-
796-
expect_pending_htlcs_forwardable!(node);
797-
798-
if idx == expected_route.len() - 1 {
799-
let events_2 = node.node.get_and_clear_pending_events();
800-
// Once we've gotten through all the HTLCs, the last one should result in a
801-
// PaymentReceived (but each previous one should not!).
802-
if path_idx == expected_paths.len() - 1 {
803-
assert_eq!(events_2.len(), 1);
804-
match events_2[0] {
805-
Event::PaymentReceived { ref payment_hash, ref payment_secret, amt } => {
806-
assert_eq!(our_payment_hash, *payment_hash);
807-
assert_eq!(our_payment_secret, *payment_secret);
808-
assert_eq!(amt, recv_value);
809-
},
810-
_ => panic!("Unexpected event"),
811-
}
812-
} else {
813-
assert!(events_2.is_empty());
785+
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) {
786+
let mut payment_event = SendEvent::from_event(ev);
787+
let mut prev_node = origin_node;
788+
789+
for (idx, &node) in expected_path.iter().enumerate() {
790+
assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
791+
792+
node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]);
793+
check_added_monitors!(node, 0);
794+
commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
795+
796+
expect_pending_htlcs_forwardable!(node);
797+
798+
if idx == expected_path.len() - 1 {
799+
let events_2 = node.node.get_and_clear_pending_events();
800+
if payment_received_expected {
801+
assert_eq!(events_2.len(), 1);
802+
match events_2[0] {
803+
Event::PaymentReceived { ref payment_hash, ref payment_secret, amt } => {
804+
assert_eq!(our_payment_hash, *payment_hash);
805+
assert_eq!(our_payment_secret, *payment_secret);
806+
assert_eq!(amt, recv_value);
807+
},
808+
_ => panic!("Unexpected event"),
814809
}
815810
} else {
816-
let mut events_2 = node.node.get_and_clear_pending_msg_events();
817-
assert_eq!(events_2.len(), 1);
818-
check_added_monitors!(node, 1);
819-
payment_event = SendEvent::from_event(events_2.remove(0));
820-
assert_eq!(payment_event.msgs.len(), 1);
811+
assert!(events_2.is_empty());
821812
}
822-
823-
prev_node = node;
813+
} else {
814+
let mut events_2 = node.node.get_and_clear_pending_msg_events();
815+
assert_eq!(events_2.len(), 1);
816+
check_added_monitors!(node, 1);
817+
payment_event = SendEvent::from_event(events_2.remove(0));
818+
assert_eq!(payment_event.msgs.len(), 1);
824819
}
820+
821+
prev_node = node;
822+
}
823+
}
824+
825+
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>) {
826+
let mut events = origin_node.node.get_and_clear_pending_msg_events();
827+
assert_eq!(events.len(), expected_route.len());
828+
for (path_idx, (ev, expected_path)) in events.drain(..).zip(expected_route.iter()).enumerate() {
829+
// Once we've gotten through all the HTLCs, the last one should result in a
830+
// PaymentReceived (but each previous one should not!), .
831+
let expect_payment = path_idx == expected_route.len() - 1;
832+
pass_along_path(origin_node, expected_path, recv_value, our_payment_hash.clone(), our_payment_secret, ev, expect_payment);
825833
}
826834
}
827835

lightning/src/ln/functional_tests.rs

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

3692-
#[test]
3693-
fn test_htlc_timeout() {
3692+
fn do_test_htlc_timeout(subpath: bool) {
36943693
// If the user fails to claim/fail an HTLC within the HTLC CLTV timeout we fail it for them
36953694
// to avoid our counterparty failing the channel.
36963695
let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -3699,7 +3698,20 @@ fn test_htlc_timeout() {
36993698
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
37003699

37013700
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::supported(), InitFeatures::supported());
3702-
let (_, our_payment_hash) = route_payment(&nodes[0], &[&nodes[1]], 100000);
3701+
3702+
let our_payment_hash = if subpath {
3703+
let route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &Vec::new(), 100000, TEST_FINAL_CLTV).unwrap();
3704+
let (_, our_payment_hash) = get_payment_preimage_hash!(&nodes[0]);
3705+
let payment_secret = PaymentSecret([0xdb; 32]);
3706+
nodes[0].node.send_payment_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, CHAN_CONFIRM_DEPTH).unwrap();
3707+
check_added_monitors!(nodes[0], 1);
3708+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3709+
assert_eq!(events.len(), 1);
3710+
pass_along_path(&nodes[0], &[&nodes[1]], 100000, our_payment_hash, Some(payment_secret), events.drain(..).next().unwrap(), false);
3711+
our_payment_hash
3712+
} else {
3713+
route_payment(&nodes[0], &[&nodes[1]], 100000).1
3714+
};
37033715

37043716
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
37053717
nodes[0].block_notifier.block_connected_checked(&header, 101, &[], &[]);
@@ -3732,6 +3744,12 @@ fn test_htlc_timeout() {
37323744
}
37333745
}
37343746

3747+
#[test]
3748+
fn test_htlc_timeout() {
3749+
do_test_htlc_timeout(true);
3750+
do_test_htlc_timeout(false);
3751+
}
3752+
37353753
#[test]
37363754
fn test_invalid_channel_announcement() {
37373755
//Test BOLT 7 channel_announcement msg requirement for final node, gather data to build customed channel_announcement msgs

0 commit comments

Comments
 (0)