@@ -10,7 +10,7 @@ use lightning::chain;
1010use lightning:: chain:: chaininterface:: { BroadcasterInterface , FeeEstimator } ;
1111use lightning:: chain:: keysinterface:: { Recipient , KeysInterface , Sign } ;
1212use lightning:: ln:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
13- use lightning:: ln:: channelmanager:: { ChannelDetails , ChannelManager , PaymentId , PaymentSendFailure , MIN_FINAL_CLTV_EXPIRY } ;
13+ use lightning:: ln:: channelmanager:: { ChannelDetails , ChannelManager , PaymentId , PaymentSendFailure , CounterpartyForwardingInfo , MIN_FINAL_CLTV_EXPIRY } ;
1414#[ cfg( feature = "std" ) ]
1515use lightning:: ln:: channelmanager:: { PhantomRouteHints , MIN_CLTV_EXPIRY_DELTA } ;
1616use lightning:: ln:: msgs:: LightningError ;
@@ -161,30 +161,10 @@ where
161161 F :: Target : FeeEstimator ,
162162 L :: Target : Logger ,
163163{
164- // Marshall route hints.
165- let our_channels = channelmanager. list_usable_channels ( ) ;
166- let mut route_hints = vec ! [ ] ;
167- for channel in our_channels {
168- let short_channel_id = match channel. get_inbound_payment_scid ( ) {
169- Some ( id) => id,
170- None => continue ,
171- } ;
172- let forwarding_info = match channel. counterparty . forwarding_info {
173- Some ( info) => info,
174- None => continue ,
175- } ;
176- route_hints. push ( RouteHint ( vec ! [ RouteHintHop {
177- src_node_id: channel. counterparty. node_id,
178- short_channel_id,
179- fees: RoutingFees {
180- base_msat: forwarding_info. fee_base_msat,
181- proportional_millionths: forwarding_info. fee_proportional_millionths,
182- } ,
183- cltv_expiry_delta: forwarding_info. cltv_expiry_delta,
184- htlc_minimum_msat: None ,
185- htlc_maximum_msat: None ,
186- } ] ) ) ;
187- }
164+ let route_hints = filter_channels (
165+ channelmanager. list_usable_channels ( ) ,
166+ amt_msat,
167+ ) ;
188168
189169 // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
190170 // supply.
@@ -221,6 +201,91 @@ where
221201 }
222202}
223203
204+ /// Filters channels for an invoice, and returns the corresponding route hints to include
205+ /// in the invoice.
206+ ///
207+ /// The filtering ensures that the `RouteHints` returned only include the highest inbound capacity
208+ /// channel per counterparty node. If any channel with a higher inbound capacity than the given
209+ /// `min_inbound_capacity_msat` exists, channels to other counterparty nodes with a lower inbound
210+ /// capacity than `min_inbound_capacity_msat` will be filtered out, even if they are the highest
211+ /// inbound capacity channel for that specific counterparty node.
212+ /// If any public channel exists, the function returns no RouteHints, and the sender will need to
213+ /// look at the public channels to find a path instead.
214+ ///
215+ /// Input:
216+ /// `channels`: The channels to filter.
217+ /// `min_inbound_capacity_msat`: Defines the lowest inbound capacity channels must have to not
218+ /// be filtered out, if any other channel above that amount exists.
219+ ///
220+ /// Result:
221+ /// `Vec<RouteHint>`: The filtered `RouteHints`, which will be empty if any public channel exists.
222+ fn filter_channels ( channels : Vec < ChannelDetails > , min_inbound_capacity_msat : Option < u64 > ) -> Vec < RouteHint > {
223+ let mut filtered_channels: HashMap < PublicKey , ( u64 , RouteHint ) > = HashMap :: new ( ) ;
224+ let min_inbound_capacity = min_inbound_capacity_msat. unwrap_or ( 0 ) ;
225+ let mut min_capacity_channel_exists = false ;
226+
227+ let route_hint_from_channel = |channel : & ChannelDetails , short_channel_id : u64 , forwarding_info : & CounterpartyForwardingInfo | {
228+ RouteHint ( vec ! [ RouteHintHop {
229+ src_node_id: channel. counterparty. node_id,
230+ short_channel_id,
231+ fees: RoutingFees {
232+ base_msat: forwarding_info. fee_base_msat,
233+ proportional_millionths: forwarding_info. fee_proportional_millionths,
234+ } ,
235+ cltv_expiry_delta: forwarding_info. cltv_expiry_delta,
236+ htlc_minimum_msat: None ,
237+ htlc_maximum_msat: None , } ] )
238+ } ;
239+ for channel in channels {
240+ let short_channel_id = match channel. get_inbound_payment_scid ( ) {
241+ Some ( id) => id,
242+ None => continue ,
243+ } ;
244+ let forwarding_info = match & channel. counterparty . forwarding_info {
245+ Some ( info) => info,
246+ None => continue ,
247+ } ;
248+ if channel. is_public {
249+ // If any public channel exists, return no hints and let the sender
250+ // look at the public channels instead.
251+ return vec ! [ ]
252+ }
253+
254+ if channel. inbound_capacity_msat >= min_inbound_capacity {
255+ min_capacity_channel_exists = true ;
256+ } ;
257+ match filtered_channels. entry ( channel. counterparty . node_id ) {
258+ hash_map:: Entry :: Occupied ( mut entry) => {
259+ let current_max_capacity = entry. get ( ) . 0 ;
260+ if channel. inbound_capacity_msat < current_max_capacity {
261+ continue ;
262+ }
263+ entry. insert ( (
264+ channel. inbound_capacity_msat ,
265+ route_hint_from_channel ( & channel, short_channel_id, forwarding_info) ,
266+ ) ) ;
267+ }
268+ hash_map:: Entry :: Vacant ( entry) => {
269+ entry. insert ( (
270+ channel. inbound_capacity_msat ,
271+ route_hint_from_channel ( & channel, short_channel_id, forwarding_info) ,
272+ ) ) ;
273+ }
274+ }
275+ }
276+
277+ // If all channels are private, return the route hint for the highest inbound capacity channel
278+ // per counterparty node. If channels with an higher inbound capacity than the
279+ // min_inbound_capacity exists, filter out the channels with a lower capacity than that.
280+ filtered_channels. into_iter ( )
281+ . filter ( |( _channel, ( capacity, _route_hint) ) | {
282+ min_capacity_channel_exists && capacity >= & min_inbound_capacity ||
283+ !min_capacity_channel_exists
284+ } )
285+ . map ( |( _channel, ( _max_capacity, route_hint) ) | route_hint)
286+ . collect :: < Vec < RouteHint > > ( )
287+ }
288+
224289/// A [`Router`] implemented using [`find_route`].
225290pub struct DefaultRouter < G : Deref < Target = NetworkGraph > , L : Deref > where L :: Target : Logger {
226291 network_graph : G ,
@@ -308,6 +373,7 @@ mod test {
308373 use lightning:: util:: enforcing_trait_impls:: EnforcingSigner ;
309374 use lightning:: util:: events:: { MessageSendEvent , MessageSendEventsProvider , Event } ;
310375 use lightning:: util:: test_utils;
376+ use lightning:: util:: config:: UserConfig ;
311377 use lightning:: chain:: keysinterface:: KeysInterface ;
312378 use utils:: create_invoice_from_channelmanager_and_duration_since_epoch;
313379
@@ -317,7 +383,9 @@ mod test {
317383 let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
318384 let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
319385 let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
320- let _chan = create_announced_chan_between_nodes ( & nodes, 0 , 1 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
386+ let mut private_chan_cfg = UserConfig :: default ( ) ;
387+ private_chan_cfg. channel_options . announced_channel = false ;
388+ create_unannounced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
321389 let invoice = create_invoice_from_channelmanager_and_duration_since_epoch (
322390 & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet , Some ( 10_000 ) , "test" . to_string ( ) ,
323391 Duration :: from_secs ( 1234567 ) ) . unwrap ( ) ;
0 commit comments