Skip to content

Commit 8e3c58b

Browse files
Sort route hints by inbound and limit to three channels
1 parent 22d1bab commit 8e3c58b

File tree

1 file changed

+32
-9
lines changed

1 file changed

+32
-9
lines changed

lightning-invoice/src/utils.rs

+32-9
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ where
203203
for PhantomRouteHints { channels, phantom_scid, real_node_pubkey } in phantom_route_hints {
204204
log_trace!(logger, "Generating phantom route hints for node {}",
205205
log_pubkey!(real_node_pubkey));
206-
let mut route_hints = filter_channels(channels, amt_msat, &logger);
206+
let mut route_hints = sort_and_filter_channels(channels, amt_msat, &logger);
207207

208208
// If we have any public channel, the route hints from `filter_channels` will be empty.
209209
// In that case we create a RouteHint on which we will push a single hop with the phantom
@@ -485,7 +485,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
485485
invoice = invoice.amount_milli_satoshis(amt);
486486
}
487487

488-
let route_hints = filter_channels(channels, amt_msat, &logger);
488+
let route_hints = sort_and_filter_channels(channels, amt_msat, &logger);
489489
for hint in route_hints {
490490
invoice = invoice.private_route(hint);
491491
}
@@ -504,8 +504,8 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
504504
}
505505
}
506506

507-
/// Filters the `channels` for an invoice, and returns the corresponding `RouteHint`s to include
508-
/// in the invoice.
507+
/// Filters the `channels` for an invoice, and returns the three corresponding `RouteHint`s
508+
/// with the most inbound capacity to include in the invoice.
509509
///
510510
/// The filtering is based on the following criteria:
511511
/// * Only one channel per counterparty node
@@ -514,7 +514,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
514514
/// `is_usable` (i.e. the peer is connected).
515515
/// * If any public channel exists, the returned `RouteHint`s will be empty, and the sender will
516516
/// need to find the path by looking at the public channels instead
517-
fn filter_channels<L: Deref>(
517+
fn sort_and_filter_channels<L: Deref>(
518518
channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>, logger: &L
519519
) -> Vec<RouteHint> where L::Target: Logger {
520520
let mut filtered_channels: HashMap<PublicKey, ChannelDetails> = HashMap::new();
@@ -597,7 +597,7 @@ fn filter_channels<L: Deref>(
597597
// the payment value and where we're currently connected to the channel counterparty.
598598
// Even if we cannot satisfy both goals, always ensure we include *some* hints, preferring
599599
// those which meet at least one criteria.
600-
filtered_channels
600+
let mut eligible_channels = filtered_channels
601601
.into_iter()
602602
.map(|(_, channel)| channel)
603603
.filter(|channel| {
@@ -610,7 +610,7 @@ fn filter_channels<L: Deref>(
610610
// online-ness.
611611
has_enough_capacity
612612
} else if min_capacity_channel_exists {
613-
has_enough_capacity
613+
has_enough_capacity
614614
} else if online_channel_exists {
615615
channel.is_usable
616616
} else { true };
@@ -629,8 +629,11 @@ fn filter_channels<L: Deref>(
629629

630630
include_channel
631631
})
632-
.map(route_hint_from_channel)
633-
.collect::<Vec<RouteHint>>()
632+
.collect::<Vec<ChannelDetails>>();
633+
634+
// Sort eligible channels by inbound capacity and return the top 3.
635+
eligible_channels.sort_unstable_by(|a, b| b.inbound_capacity_msat.cmp(&a.inbound_capacity_msat));
636+
eligible_channels.into_iter().take(3).map(route_hint_from_channel).collect::<Vec<RouteHint>>()
634637
}
635638

636639
#[cfg(test)]
@@ -852,6 +855,26 @@ mod test {
852855
match_invoice_routes(Some(1_000_000_000), &nodes[0], scid_aliases);
853856
}
854857

858+
#[test]
859+
fn test_hints_limited_to_3() {
860+
let chanmon_cfgs = create_chanmon_cfgs(5);
861+
let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
862+
let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None,None,None, None, None]);
863+
let nodes = create_network(5, &node_cfgs, &node_chanmgrs);
864+
865+
let chan_1_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 100_004, 0);
866+
let chan_2_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 2, 0, 100_003, 0);
867+
let chan_3_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 3, 0, 100_002, 0);
868+
let _chan_4_0 = create_unannounced_chan_between_nodes_with_value(&nodes, 4, 0, 100_001, 0);
869+
870+
let mut scid_aliases = HashSet::new();
871+
scid_aliases.insert(chan_1_0.0.short_channel_id_alias.unwrap());
872+
scid_aliases.insert(chan_2_0.0.short_channel_id_alias.unwrap());
873+
scid_aliases.insert(chan_3_0.0.short_channel_id_alias.unwrap());
874+
875+
match_invoice_routes(Some(500_000), &nodes[0], scid_aliases);
876+
}
877+
855878
#[test]
856879
fn test_forwarding_info_not_assigned_channel_excluded_from_hints() {
857880
let chanmon_cfgs = create_chanmon_cfgs(3);

0 commit comments

Comments
 (0)