Skip to content

Commit 779887d

Browse files
committed
Filter route hints for create invoice
Filter the route hints in `create_invoice_from_channelmanager` based on the following criteria: * Only one channel per counterparty node * Always select the channel with the highest inbound capacity * Filter out channels with a lower inbound capacity than the invoice amt * If any private channel exists, the invoice rout_hints should be empty
1 parent e43cfe1 commit 779887d

File tree

1 file changed

+55
-14
lines changed

1 file changed

+55
-14
lines changed

lightning-invoice/src/utils.rs

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,21 @@ where
160160
{
161161
// Marshall route hints.
162162
let our_channels = channelmanager.list_usable_channels();
163-
let mut route_hints = vec![];
163+
let mut route_hints: HashMap<PublicKey, (u64, RouteHint)> = HashMap::new();
164+
let min_capacity_required = match amt_msat {
165+
Some(amt) => amt,
166+
None => 0,
167+
};
164168
for channel in our_channels {
169+
// Filter the channels to ensure that the `route_hints` only include the highest inbound
170+
// capacity channel per counterparty node, and that channels with a lower inbound capacity
171+
// than the invoice amount aren't included in the hints.
172+
if !channel.is_public {
173+
// If any of our channels are private, the invoice shouldn't include any `route_hints`
174+
// and the sender will need to look at our public channels instead.
175+
route_hints.clear();
176+
break;
177+
}
165178
let short_channel_id = match channel.short_channel_id {
166179
Some(id) => id,
167180
None => continue,
@@ -170,17 +183,45 @@ where
170183
Some(info) => info,
171184
None => continue,
172185
};
173-
route_hints.push(RouteHint(vec![RouteHintHop {
174-
src_node_id: channel.counterparty.node_id,
175-
short_channel_id,
176-
fees: RoutingFees {
177-
base_msat: forwarding_info.fee_base_msat,
178-
proportional_millionths: forwarding_info.fee_proportional_millionths,
179-
},
180-
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
181-
htlc_minimum_msat: None,
182-
htlc_maximum_msat: None,
183-
}]));
186+
if channel.inbound_capacity_msat < min_capacity_required {
187+
continue;
188+
};
189+
match route_hints.entry(channel.counterparty.node_id) {
190+
hash_map::Entry::Occupied(mut entry) => {
191+
let current_max_capacity = entry.get().0;
192+
if channel.inbound_capacity_msat < current_max_capacity {
193+
continue;
194+
}
195+
entry.insert((
196+
channel.inbound_capacity_msat,
197+
RouteHint(vec![RouteHintHop {
198+
src_node_id: channel.counterparty.node_id,
199+
short_channel_id,
200+
fees: RoutingFees {
201+
base_msat: forwarding_info.fee_base_msat,
202+
proportional_millionths: forwarding_info.fee_proportional_millionths,
203+
},
204+
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
205+
htlc_minimum_msat: None,
206+
htlc_maximum_msat: None,
207+
}])));
208+
}
209+
hash_map::Entry::Vacant(entry) => {
210+
entry.insert((
211+
channel.inbound_capacity_msat,
212+
RouteHint(vec![RouteHintHop {
213+
src_node_id: channel.counterparty.node_id,
214+
short_channel_id,
215+
fees: RoutingFees {
216+
base_msat: forwarding_info.fee_base_msat,
217+
proportional_millionths: forwarding_info.fee_proportional_millionths,
218+
},
219+
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
220+
htlc_minimum_msat: None,
221+
htlc_maximum_msat: None,
222+
}])));
223+
}
224+
}
184225
}
185226

186227
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
@@ -200,8 +241,8 @@ where
200241
if let Some(amt) = amt_msat {
201242
invoice = invoice.amount_milli_satoshis(amt);
202243
}
203-
for hint in route_hints {
204-
invoice = invoice.private_route(hint);
244+
for (_, (_, hint)) in &route_hints {
245+
invoice = invoice.private_route(hint.clone());
205246
}
206247

207248
let raw_invoice = match invoice.build_raw() {

0 commit comments

Comments
 (0)