@@ -487,20 +487,23 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
487
487
node_info. features . supports_basic_mpp ( )
488
488
} else { false }
489
489
} else { false } ;
490
+ log_trace ! ( logger, "Searching for a route from payer {} to payee {} {} MPP" , our_node_id, payee,
491
+ if allow_mpp { "with" } else { "without" } ) ;
490
492
491
493
// Step (1).
492
494
// Prepare the data we'll use for payee-to-payer search by
493
495
// inserting first hops suggested by the caller as targets.
494
496
// Our search will then attempt to reach them while traversing from the payee node.
495
- let mut first_hop_targets: HashMap < _ , ( _ , ChannelFeatures , _ , NodeFeatures ) > =
497
+ let mut first_hop_targets: HashMap < _ , Vec < ( _ , ChannelFeatures , _ , NodeFeatures ) > > =
496
498
HashMap :: with_capacity ( if first_hops. is_some ( ) { first_hops. as_ref ( ) . unwrap ( ) . len ( ) } else { 0 } ) ;
497
499
if let Some ( hops) = first_hops {
498
500
for chan in hops {
499
501
let short_channel_id = chan. short_channel_id . expect ( "first_hops should be filled in with usable channels, not pending ones" ) ;
500
502
if chan. counterparty . node_id == * our_node_id {
501
503
return Err ( LightningError { err : "First hop cannot have our_node_id as a destination." . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
502
504
}
503
- first_hop_targets. insert ( chan. counterparty . node_id , ( short_channel_id, chan. counterparty . features . to_context ( ) , chan. outbound_capacity_msat , chan. counterparty . features . to_context ( ) ) ) ;
505
+ first_hop_targets. entry ( chan. counterparty . node_id ) . or_insert ( Vec :: new ( ) )
506
+ . push ( ( short_channel_id, chan. counterparty . features . to_context ( ) , chan. outbound_capacity_msat , chan. counterparty . features . to_context ( ) ) ) ;
504
507
}
505
508
if first_hop_targets. is_empty ( ) {
506
509
return Err ( LightningError { err : "Cannot route when there are no outbound routes away from us" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
@@ -824,8 +827,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
824
827
} ;
825
828
826
829
if !skip_node {
827
- if first_hops . is_some ( ) {
828
- if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets . get ( & $node_id ) {
830
+ if let Some ( first_channels ) = first_hop_targets . get ( & $node_id ) {
831
+ for ( ref first_hop, ref features, ref outbound_capacity_msat, _) in first_channels {
829
832
add_entry!( first_hop, * our_node_id, $node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, $fee_to_target_msat, $next_hops_value_contribution, $next_hops_path_htlc_minimum_msat) ;
830
833
}
831
834
}
@@ -878,9 +881,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
878
881
879
882
// If first hop is a private channel and the only way to reach the payee, this is the only
880
883
// place where it could be added.
881
- if first_hops. is_some ( ) {
882
- if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets. get ( & payee) {
883
- add_entry ! ( first_hop, * our_node_id, payee, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, 0 , path_value_msat, 0 ) ;
884
+ if let Some ( first_channels) = first_hop_targets. get ( & payee) {
885
+ for ( ref first_hop, ref features, ref outbound_capacity_msat, _) in first_channels {
886
+ let added = add_entry ! ( first_hop, * our_node_id, payee, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, 0 , path_value_msat, 0 ) ;
887
+ log_trace ! ( logger, "{} direct route to payee via SCID {}" , if added { "Added" } else { "Skipped" } , first_hop) ;
884
888
}
885
889
}
886
890
@@ -949,8 +953,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
949
953
}
950
954
951
955
// Searching for a direct channel between last checked hop and first_hop_targets
952
- if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets. get ( & prev_hop_id) {
953
- add_entry ! ( first_hop, * our_node_id , prev_hop_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat) ;
956
+ if let Some ( first_channels) = first_hop_targets. get ( & prev_hop_id) {
957
+ for ( ref first_hop, ref features, ref outbound_capacity_msat, _) in first_channels {
958
+ add_entry ! ( first_hop, * our_node_id , prev_hop_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat) ;
959
+ }
954
960
}
955
961
956
962
if !hop_used {
@@ -981,8 +987,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
981
987
// Note that we *must* check if the last hop was added as `add_entry`
982
988
// always assumes that the third argument is a node to which we have a
983
989
// path.
984
- if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat, _) ) = first_hop_targets. get ( & hop. src_node_id ) {
985
- add_entry ! ( first_hop, * our_node_id , hop. src_node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat) ;
990
+ if let Some ( first_channels) = first_hop_targets. get ( & hop. src_node_id ) {
991
+ for ( ref first_hop, ref features, ref outbound_capacity_msat, _) in first_channels {
992
+ add_entry ! ( first_hop, * our_node_id , hop. src_node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat) ;
993
+ }
986
994
}
987
995
}
988
996
}
@@ -1013,8 +1021,17 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
1013
1021
let mut ordered_hops = vec ! ( ( new_entry. clone( ) , NodeFeatures :: empty( ) ) ) ;
1014
1022
1015
1023
' path_walk: loop {
1016
- if let Some ( & ( _, _, _, ref features) ) = first_hop_targets. get ( & ordered_hops. last ( ) . unwrap ( ) . 0 . pubkey ) {
1017
- ordered_hops. last_mut ( ) . unwrap ( ) . 1 = features. clone ( ) ;
1024
+ let mut features_set = false ;
1025
+ if let Some ( first_channels) = first_hop_targets. get ( & ordered_hops. last ( ) . unwrap ( ) . 0 . pubkey ) {
1026
+ for ( scid, _, _, ref features) in first_channels {
1027
+ if * scid == ordered_hops. last ( ) . unwrap ( ) . 0 . short_channel_id {
1028
+ ordered_hops. last_mut ( ) . unwrap ( ) . 1 = features. clone ( ) ;
1029
+ features_set = true ;
1030
+ break ;
1031
+ }
1032
+ }
1033
+ }
1034
+ if features_set {
1018
1035
} else if let Some ( node) = network_nodes. get ( & ordered_hops. last ( ) . unwrap ( ) . 0 . pubkey ) {
1019
1036
if let Some ( node_info) = node. announcement_info . as_ref ( ) {
1020
1037
ordered_hops. last_mut ( ) . unwrap ( ) . 1 = node_info. features . clone ( ) ;
@@ -4220,6 +4237,51 @@ mod tests {
4220
4237
}
4221
4238
}
4222
4239
4240
+ #[ test]
4241
+ fn multiple_direct_first_hops ( ) {
4242
+ // Previously we'd only ever considered one first hop path per counterparty.
4243
+ // However, as we don't restrict users to one channel per peer, we really need to support
4244
+ // looking at all first hop paths.
4245
+ // Here we test that we do not ignore all-but-the-last first hop paths per counterparty (as
4246
+ // we used to do by overwriting the `first_hop_targets` hashmap entry) and that we can MPP
4247
+ // route over multiple channels with the same first hop.
4248
+ let secp_ctx = Secp256k1 :: new ( ) ;
4249
+ let ( _, our_id, _, nodes) = get_nodes ( & secp_ctx) ;
4250
+ let logger = Arc :: new ( test_utils:: TestLogger :: new ( ) ) ;
4251
+ let network_graph = NetworkGraph :: new ( genesis_block ( Network :: Testnet ) . header . block_hash ( ) ) ;
4252
+
4253
+ {
4254
+ let route = get_route ( & our_id, & network_graph, & nodes[ 0 ] , Some ( InvoiceFeatures :: known ( ) ) , Some ( & [
4255
+ & get_channel_details ( Some ( 3 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 200_000 ) ,
4256
+ & get_channel_details ( Some ( 2 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 10_000 ) ,
4257
+ ] ) , & [ ] , 100_000 , 42 , Arc :: clone ( & logger) ) . unwrap ( ) ;
4258
+ assert_eq ! ( route. paths. len( ) , 1 ) ;
4259
+ assert_eq ! ( route. paths[ 0 ] . len( ) , 1 ) ;
4260
+
4261
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
4262
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 3 ) ;
4263
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 100_000 ) ;
4264
+ }
4265
+ {
4266
+ let route = get_route ( & our_id, & network_graph, & nodes[ 0 ] , Some ( InvoiceFeatures :: known ( ) ) , Some ( & [
4267
+ & get_channel_details ( Some ( 3 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 50_000 ) ,
4268
+ & get_channel_details ( Some ( 2 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 50_000 ) ,
4269
+ ] ) , & [ ] , 100_000 , 42 , Arc :: clone ( & logger) ) . unwrap ( ) ;
4270
+ assert_eq ! ( route. paths. len( ) , 2 ) ;
4271
+ assert_eq ! ( route. paths[ 0 ] . len( ) , 1 ) ;
4272
+ assert_eq ! ( route. paths[ 1 ] . len( ) , 1 ) ;
4273
+
4274
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
4275
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 3 ) ;
4276
+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 50_000 ) ;
4277
+
4278
+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
4279
+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . short_channel_id, 2 ) ;
4280
+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . fee_msat, 50_000 ) ;
4281
+ }
4282
+
4283
+ }
4284
+
4223
4285
#[ test]
4224
4286
fn total_fees_single_path ( ) {
4225
4287
let route = Route {
0 commit comments