Skip to content

Commit 80354ac

Browse files
committed
Include all channel route hints if no connected channels exist
Mobile clients often take a second or two before they reconnect to their peers as its not always clear immediately that connections have been killed (especially on iOS). This can cause us to spuriously fail to include route hints in our invoices if we're on mobile. The fix is simple, if we're selecting channels to include in route hints and we're not not connected to the peer for any of our channels, we should simply include the hints for all channels, even though we're disconencted. Fixes #1768.
1 parent fdfd4f0 commit 80354ac

File tree

1 file changed

+46
-8
lines changed

1 file changed

+46
-8
lines changed

lightning-invoice/src/utils.rs

+46-8
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ where
326326
F::Target: FeeEstimator,
327327
L::Target: Logger,
328328
{
329-
let route_hints = filter_channels(channelmanager.list_usable_channels(), amt_msat);
329+
let route_hints = filter_channels(channelmanager.list_channels(), amt_msat);
330330

331331
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
332332
// supply.
@@ -381,12 +381,13 @@ where
381381
/// channel with a higher or equal inbound capacity than `min_inbound_capacity_msat` exists
382382
/// * If any public channel exists, the returned `RouteHint`s will be empty, and the sender will
383383
/// need to find the path by looking at the public channels instead
384-
fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>) -> Vec<RouteHint>{
385-
let mut filtered_channels: HashMap<PublicKey, &ChannelDetails> = HashMap::new();
384+
fn filter_channels(mut channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>) -> Vec<RouteHint>{
385+
let mut filtered_channels: HashMap<PublicKey, ChannelDetails> = HashMap::new();
386386
let min_inbound_capacity = min_inbound_capacity_msat.unwrap_or(0);
387387
let mut min_capacity_channel_exists = false;
388+
let mut online_channel_exists = false;
388389

389-
for channel in channels.iter() {
390+
for channel in channels.drain(..).filter(|chan| chan.is_channel_ready) {
390391
if channel.get_inbound_payment_scid().is_none() || channel.counterparty.forwarding_info.is_none() {
391392
continue;
392393
}
@@ -400,6 +401,9 @@ fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Opt
400401
if channel.inbound_capacity_msat >= min_inbound_capacity {
401402
min_capacity_channel_exists = true;
402403
};
404+
if channel.is_usable {
405+
online_channel_exists = true;
406+
}
403407
match filtered_channels.entry(channel.counterparty.node_id) {
404408
hash_map::Entry::Occupied(mut entry) => {
405409
let current_max_capacity = entry.get().inbound_capacity_msat;
@@ -414,7 +418,7 @@ fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Opt
414418
}
415419
}
416420

417-
let route_hint_from_channel = |channel: &ChannelDetails| {
421+
let route_hint_from_channel = |channel: ChannelDetails| {
418422
let forwarding_info = channel.counterparty.forwarding_info.as_ref().unwrap();
419423
RouteHint(vec![RouteHintHop {
420424
src_node_id: channel.counterparty.node_id,
@@ -430,12 +434,19 @@ fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Opt
430434
// If all channels are private, return the route hint for the highest inbound capacity channel
431435
// per counterparty node. If channels with an higher inbound capacity than the
432436
// min_inbound_capacity exists, filter out the channels with a lower capacity than that.
437+
// Further, if we are connected to our peer for any channels, only return those.
433438
filtered_channels.into_iter()
434439
.filter(|(_counterparty_id, channel)| {
435-
min_capacity_channel_exists && channel.inbound_capacity_msat >= min_inbound_capacity ||
436-
!min_capacity_channel_exists
440+
if min_capacity_channel_exists {
441+
channel.inbound_capacity_msat >= min_inbound_capacity
442+
} else { true }
443+
})
444+
.filter(|(_counterparty_id, channel)| {
445+
if online_channel_exists {
446+
channel.is_usable
447+
} else { true }
437448
})
438-
.map(|(_counterparty_id, channel)| route_hint_from_channel(&channel))
449+
.map(|(_counterparty_id, channel)| route_hint_from_channel(channel))
439450
.collect::<Vec<RouteHint>>()
440451
}
441452

@@ -723,6 +734,33 @@ mod test {
723734
match_invoice_routes(Some(5000), &nodes[0], scid_aliases);
724735
}
725736

737+
#[test]
738+
fn test_hints_has_only_online_channels() {
739+
let chanmon_cfgs = create_chanmon_cfgs(3);
740+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
741+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
742+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
743+
let chan_a = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 10_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features());
744+
let chan_b = create_unannounced_chan_between_nodes_with_value(&nodes, 2, 0, 10_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features());
745+
746+
// With both peers connected we should get all hints
747+
let mut scid_aliases = HashSet::new();
748+
scid_aliases.insert(chan_a.0.short_channel_id_alias.unwrap());
749+
scid_aliases.insert(chan_b.0.short_channel_id_alias.unwrap());
750+
751+
match_invoice_routes(Some(5000), &nodes[0], scid_aliases.clone());
752+
753+
// With only one peer connected other hints should go away
754+
scid_aliases.remove(&chan_b.0.short_channel_id_alias.unwrap());
755+
nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id(), false);
756+
match_invoice_routes(Some(5000), &nodes[0], scid_aliases.clone());
757+
758+
// With both peers disconnected we should just get all the hints
759+
scid_aliases.insert(chan_b.0.short_channel_id_alias.unwrap());
760+
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
761+
match_invoice_routes(Some(5000), &nodes[0], scid_aliases);
762+
}
763+
726764
#[test]
727765
fn test_forwarding_info_not_assigned_channel_excluded_from_hints() {
728766
let chanmon_cfgs = create_chanmon_cfgs(3);

0 commit comments

Comments
 (0)