Skip to content

Commit 9ea5649

Browse files
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 e29c401 commit 9ea5649

File tree

1 file changed

+84
-25
lines changed

1 file changed

+84
-25
lines changed

lightning-invoice/src/utils.rs

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use lightning::chain;
1010
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
1111
use lightning::chain::keysinterface::{Recipient, KeysInterface, Sign};
1212
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
13-
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY, MIN_CLTV_EXPIRY_DELTA};
13+
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, PhantomRouteHints, CounterpartyForwardingInfo, MIN_FINAL_CLTV_EXPIRY, MIN_CLTV_EXPIRY_DELTA};
1414
use lightning::ln::msgs::LightningError;
1515
use lightning::routing::scoring::Score;
1616
use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
@@ -158,30 +158,10 @@ where
158158
F::Target: FeeEstimator,
159159
L::Target: Logger,
160160
{
161-
// Marshall route hints.
162-
let our_channels = channelmanager.list_usable_channels();
163-
let mut route_hints = vec![];
164-
for channel in our_channels {
165-
let short_channel_id = match channel.short_channel_id {
166-
Some(id) => id,
167-
None => continue,
168-
};
169-
let forwarding_info = match channel.counterparty.forwarding_info {
170-
Some(info) => info,
171-
None => continue,
172-
};
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-
}]));
184-
}
161+
let (route_hints, _has_private_channels) = filter_channels(
162+
channelmanager.list_usable_channels(),
163+
amt_msat,
164+
);
185165

186166
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
187167
// supply.
@@ -218,6 +198,85 @@ where
218198
}
219199
}
220200

201+
/// Utility to filter channels for an invoice, and return the corresponding route hints to include
202+
/// in the invoice.
203+
///
204+
/// The filtering ensures that the RouteHints returned only include the highest inbound capacity
205+
/// channel per counterparty node for which channels are present. Channels with a lower inbound
206+
/// capacity than the invoice amount aren't included in the hints. If any of the channels are
207+
/// private, the function returns no RouteHints, and the sender will need to look at the public
208+
/// channels to find the path instead.
209+
///
210+
/// Input:
211+
/// `channels`: The channels to filter.
212+
/// `min_inbound_capacity_required`: Defines the lowest inbound capacity a channel must have to not
213+
/// be filtered out.
214+
///
215+
/// Result:
216+
/// `Vec<RouteHint>`: The filtered RouteHints
217+
/// `bool`: True if any of the inputted channels are private, else false
218+
fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_required: Option<u64>) -> (Vec<RouteHint>, bool){
219+
let mut filtered_channels: HashMap<PublicKey, (u64, RouteHint)> = HashMap::new();
220+
let min_capacity_required = match min_inbound_capacity_required {
221+
Some(amt) => amt,
222+
None => 0,
223+
};
224+
let route_hint_from_channel = |channel: ChannelDetails, short_channel_id: u64, forwarding_info: CounterpartyForwardingInfo| {
225+
RouteHint(vec![RouteHintHop {
226+
src_node_id: channel.counterparty.node_id,
227+
short_channel_id,
228+
fees: RoutingFees {
229+
base_msat: forwarding_info.fee_base_msat,
230+
proportional_millionths: forwarding_info.fee_proportional_millionths,
231+
},
232+
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
233+
htlc_minimum_msat: None,
234+
htlc_maximum_msat: None,}])
235+
};
236+
for channel in channels {
237+
if !channel.is_public {
238+
// If any of the channels are private, return no hints and let the sender look at the
239+
// public channels instead.
240+
let has_private_channels = true;
241+
return (vec![], has_private_channels);
242+
}
243+
let short_channel_id = match channel.clone().short_channel_id {
244+
Some(id) => id,
245+
None => continue,
246+
};
247+
let forwarding_info = match channel.clone().counterparty.forwarding_info {
248+
Some(info) => info,
249+
None => continue,
250+
};
251+
if channel.inbound_capacity_msat < min_capacity_required {
252+
continue;
253+
};
254+
match filtered_channels.entry(channel.counterparty.node_id) {
255+
hash_map::Entry::Occupied(mut entry) => {
256+
let current_max_capacity = entry.get().0;
257+
if channel.inbound_capacity_msat < current_max_capacity {
258+
continue;
259+
}
260+
entry.insert((
261+
channel.inbound_capacity_msat,
262+
route_hint_from_channel(channel, short_channel_id, forwarding_info),
263+
));
264+
}
265+
hash_map::Entry::Vacant(entry) => {
266+
entry.insert((
267+
channel.inbound_capacity_msat,
268+
route_hint_from_channel(channel, short_channel_id, forwarding_info),
269+
));
270+
}
271+
}
272+
}
273+
let route_hints = filtered_channels.iter()
274+
.map(|(_channel, (_max_capacity, route_hint))| route_hint.clone())
275+
.collect::<Vec<RouteHint>>();
276+
let has_private_channels = false;
277+
(route_hints, has_private_channels)
278+
}
279+
221280
/// A [`Router`] implemented using [`find_route`].
222281
pub struct DefaultRouter<G: Deref<Target = NetworkGraph>, L: Deref> where L::Target: Logger {
223282
network_graph: G,

0 commit comments

Comments
 (0)