@@ -487,20 +487,23 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
487487 node_info. features . supports_basic_mpp ( )
488488 } else { false }
489489 } 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" } ) ;
490492
491493 // Step (1).
492494 // Prepare the data we'll use for payee-to-payer search by
493495 // inserting first hops suggested by the caller as targets.
494496 // 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 ) > > =
496498 HashMap :: with_capacity ( if first_hops. is_some ( ) { first_hops. as_ref ( ) . unwrap ( ) . len ( ) } else { 0 } ) ;
497499 if let Some ( hops) = first_hops {
498500 for chan in hops {
499501 let short_channel_id = chan. short_channel_id . expect ( "first_hops should be filled in with usable channels, not pending ones" ) ;
500502 if chan. counterparty . node_id == * our_node_id {
501503 return Err ( LightningError { err : "First hop cannot have our_node_id as a destination." . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
502504 }
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 ( ) ) ) ;
504507 }
505508 if first_hop_targets. is_empty ( ) {
506509 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
824827 } ;
825828
826829 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 {
829832 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) ;
830833 }
831834 }
@@ -878,9 +881,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
878881
879882 // If first hop is a private channel and the only way to reach the payee, this is the only
880883 // 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) ;
884888 }
885889 }
886890
@@ -897,7 +901,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
897901 } ,
898902 }
899903
900- // Step (1 ).
904+ // Step (2 ).
901905 // If a caller provided us with last hops, add them to routing targets. Since this happens
902906 // earlier than general path finding, they will be somewhat prioritized, although currently
903907 // it matters only if the fees are exactly the same.
@@ -949,8 +953,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
949953 }
950954
951955 // 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+ }
954960 }
955961
956962 if !hop_used {
@@ -981,8 +987,10 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
981987 // Note that we *must* check if the last hop was added as `add_entry`
982988 // always assumes that the third argument is a node to which we have a
983989 // 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+ }
986994 }
987995 }
988996 }
@@ -995,7 +1003,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
9951003 // last hops communicated by the caller, and the payment receiver.
9961004 let mut found_new_path = false ;
9971005
998- // Step (2 ).
1006+ // Step (3 ).
9991007 // If this loop terminates due the exhaustion of targets, two situations are possible:
10001008 // - not enough outgoing liquidity:
10011009 // 0 < already_collected_value_msat < final_value_msat
@@ -1013,20 +1021,30 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
10131021 let mut ordered_hops = vec ! ( ( new_entry. clone( ) , NodeFeatures :: empty( ) ) ) ;
10141022
10151023 ' 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 ( ) ;
1018- } else if let Some ( node) = network_nodes. get ( & ordered_hops. last ( ) . unwrap ( ) . 0 . pubkey ) {
1019- if let Some ( node_info) = node. announcement_info . as_ref ( ) {
1020- ordered_hops. last_mut ( ) . unwrap ( ) . 1 = node_info. 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 {
1035+ if let Some ( node) = network_nodes. get ( & ordered_hops. last ( ) . unwrap ( ) . 0 . pubkey ) {
1036+ if let Some ( node_info) = node. announcement_info . as_ref ( ) {
1037+ ordered_hops. last_mut ( ) . unwrap ( ) . 1 = node_info. features . clone ( ) ;
1038+ } else {
1039+ ordered_hops. last_mut ( ) . unwrap ( ) . 1 = NodeFeatures :: empty ( ) ;
1040+ }
10211041 } else {
1022- ordered_hops. last_mut ( ) . unwrap ( ) . 1 = NodeFeatures :: empty ( ) ;
1042+ // We should be able to fill in features for everything except the last
1043+ // hop, if the last hop was provided via a BOLT 11 invoice (though we
1044+ // should be able to extend it further as BOLT 11 does have feature
1045+ // flags for the last hop node itself).
1046+ assert ! ( ordered_hops. last( ) . unwrap( ) . 0 . pubkey == * payee) ;
10231047 }
1024- } else {
1025- // We should be able to fill in features for everything except the last
1026- // hop, if the last hop was provided via a BOLT 11 invoice (though we
1027- // should be able to extend it further as BOLT 11 does have feature
1028- // flags for the last hop node itself).
1029- assert ! ( ordered_hops. last( ) . unwrap( ) . 0 . pubkey == * payee) ;
10301048 }
10311049
10321050 // Means we succesfully traversed from the payer to the payee, now
@@ -1130,7 +1148,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
11301148 break ' paths_collection;
11311149 }
11321150
1133- // Step (3 ).
1151+ // Step (4 ).
11341152 // Stop either when the recommended value is reached or if no new path was found in this
11351153 // iteration.
11361154 // In the latter case, making another path finding attempt won't help,
@@ -1154,7 +1172,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
11541172 }
11551173 }
11561174
1157- // Step (4 ).
1175+ // Step (5 ).
11581176 if payment_paths. len ( ) == 0 {
11591177 return Err ( LightningError { err : "Failed to find a path to the given destination" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
11601178 }
@@ -1175,12 +1193,12 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
11751193 let mut cur_route = Vec :: < PaymentPath > :: new ( ) ;
11761194 let mut aggregate_route_value_msat = 0 ;
11771195
1178- // Step (5 ).
1196+ // Step (6 ).
11791197 // TODO: real random shuffle
11801198 // Currently just starts with i_th and goes up to i-1_th in a looped way.
11811199 let cur_payment_paths = [ & payment_paths[ i..] , & payment_paths[ ..i] ] . concat ( ) ;
11821200
1183- // Step (6 ).
1201+ // Step (7 ).
11841202 for payment_path in cur_payment_paths {
11851203 cur_route. push ( payment_path. clone ( ) ) ;
11861204 aggregate_route_value_msat += payment_path. get_value_msat ( ) ;
@@ -1219,7 +1237,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
12191237
12201238 assert ! ( cur_route. len( ) > 0 ) ;
12211239
1222- // Step (7 ).
1240+ // Step (8 ).
12231241 // Now, substract the overpaid value from the most-expensive path.
12241242 // TODO: this could also be optimized by also sorting by feerate_per_sat_routed,
12251243 // so that the sender pays less fees overall. And also htlc_minimum_msat.
@@ -1236,7 +1254,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
12361254 drawn_routes. push ( cur_route) ;
12371255 }
12381256
1239- // Step (8 ).
1257+ // Step (9 ).
12401258 // Select the best route by lowest total fee.
12411259 drawn_routes. sort_by_key ( |paths| paths. iter ( ) . map ( |path| path. get_total_fee_paid_msat ( ) ) . sum :: < u64 > ( ) ) ;
12421260 let mut selected_paths = Vec :: < Vec < RouteHop > > :: new ( ) ;
@@ -4220,6 +4238,50 @@ mod tests {
42204238 }
42214239 }
42224240
4241+ #[ test]
4242+ fn multiple_direct_first_hops ( ) {
4243+ // Previously we'd only ever considered one first hop path per counterparty.
4244+ // However, as we don't restrict users to one channel per peer, we really need to support
4245+ // looking at all first hop paths.
4246+ // Here we test that we do not ignore all-but-the-last first hop paths per counterparty (as
4247+ // we used to do by overwriting the `first_hop_targets` hashmap entry) and that we can MPP
4248+ // route over multiple channels with the same first hop.
4249+ let secp_ctx = Secp256k1 :: new ( ) ;
4250+ let ( _, our_id, _, nodes) = get_nodes ( & secp_ctx) ;
4251+ let logger = Arc :: new ( test_utils:: TestLogger :: new ( ) ) ;
4252+ let network_graph = NetworkGraph :: new ( genesis_block ( Network :: Testnet ) . header . block_hash ( ) ) ;
4253+
4254+ {
4255+ let route = get_route ( & our_id, & network_graph, & nodes[ 0 ] , Some ( InvoiceFeatures :: known ( ) ) , Some ( & [
4256+ & get_channel_details ( Some ( 3 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 200_000 ) ,
4257+ & get_channel_details ( Some ( 2 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 10_000 ) ,
4258+ ] ) , & [ ] , 100_000 , 42 , Arc :: clone ( & logger) ) . unwrap ( ) ;
4259+ assert_eq ! ( route. paths. len( ) , 1 ) ;
4260+ assert_eq ! ( route. paths[ 0 ] . len( ) , 1 ) ;
4261+
4262+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
4263+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 3 ) ;
4264+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 100_000 ) ;
4265+ }
4266+ {
4267+ let route = get_route ( & our_id, & network_graph, & nodes[ 0 ] , Some ( InvoiceFeatures :: known ( ) ) , Some ( & [
4268+ & get_channel_details ( Some ( 3 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 50_000 ) ,
4269+ & get_channel_details ( Some ( 2 ) , nodes[ 0 ] , InitFeatures :: known ( ) , 50_000 ) ,
4270+ ] ) , & [ ] , 100_000 , 42 , Arc :: clone ( & logger) ) . unwrap ( ) ;
4271+ assert_eq ! ( route. paths. len( ) , 2 ) ;
4272+ assert_eq ! ( route. paths[ 0 ] . len( ) , 1 ) ;
4273+ assert_eq ! ( route. paths[ 1 ] . len( ) , 1 ) ;
4274+
4275+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
4276+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 3 ) ;
4277+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 50_000 ) ;
4278+
4279+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
4280+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . short_channel_id, 2 ) ;
4281+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . fee_msat, 50_000 ) ;
4282+ }
4283+ }
4284+
42234285 #[ test]
42244286 fn total_fees_single_path ( ) {
42254287 let route = Route {
0 commit comments