@@ -1119,7 +1119,7 @@ struct RouteGraphNode {
1119
1119
// considering their capacity and fees
1120
1120
value_contribution_msat : u64 ,
1121
1121
total_cltv_delta : u32 ,
1122
- /// The number of hops walked up to this node.
1122
+ /// The number of intermediate hops walked up to this node.
1123
1123
path_length_to_node : u8 ,
1124
1124
is_intro_node : bool ,
1125
1125
}
@@ -1466,6 +1466,17 @@ impl<'a> CandidateRouteHop<'a> {
1466
1466
CandidateRouteHop :: OneHopBlinded ( _) => None ,
1467
1467
}
1468
1468
}
1469
+ /// Whether this candidate is an unblinded forwarding hop along the path, i.e. not the final
1470
+ /// hop or part of a blinded path.
1471
+ pub fn is_intermediate_hop ( & self , payee_node_id_opt : & Option < NodeId > ) -> bool {
1472
+ match self {
1473
+ CandidateRouteHop :: FirstHop ( h) => !h. targets_blinded_path && & self . target ( ) != payee_node_id_opt,
1474
+ CandidateRouteHop :: PublicHop ( h) => !h. targets_blinded_path && & self . target ( ) != payee_node_id_opt,
1475
+ CandidateRouteHop :: PrivateHop ( _) => & self . target ( ) != payee_node_id_opt,
1476
+ CandidateRouteHop :: Blinded ( _) => false ,
1477
+ CandidateRouteHop :: OneHopBlinded ( _) => false ,
1478
+ }
1479
+ }
1469
1480
}
1470
1481
1471
1482
/// A unique(ish) identifier for a specific [`CandidateRouteHop`].
@@ -1873,6 +1884,7 @@ pub(crate) fn get_route<L: Deref, S: ScoreLookUp>(
1873
1884
where L :: Target : Logger {
1874
1885
1875
1886
let payment_params = & route_params. payment_params ;
1887
+ let max_intermediate_hops = core:: cmp:: min ( payment_params. max_intermediate_hops , MAX_PATH_LENGTH_ESTIMATE ) ;
1876
1888
let final_value_msat = route_params. final_value_msat ;
1877
1889
// If we're routing to a blinded recipient, we won't have their node id. Therefore, keep the
1878
1890
// unblinded payee id as an option. We also need a non-optional "payee id" for path construction,
@@ -2168,8 +2180,9 @@ where L::Target: Logger {
2168
2180
// Verify the liquidity offered by this channel complies to the minimal contribution.
2169
2181
let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat;
2170
2182
// Do not consider candidate hops that would exceed the maximum path length.
2171
- let path_length_to_node = $next_hops_path_length + 1 ;
2172
- let exceeds_max_path_length = path_length_to_node > MAX_PATH_LENGTH_ESTIMATE ;
2183
+ let intermediate_path_length_to_node = $next_hops_path_length
2184
+ + if $candidate. is_intermediate_hop( & payee_node_id_opt) { 1 } else { 0 } ;
2185
+ let exceeds_max_path_length = intermediate_path_length_to_node > max_intermediate_hops;
2173
2186
2174
2187
// Do not consider candidates that exceed the maximum total cltv expiry limit.
2175
2188
// In order to already account for some of the privacy enhancing random CLTV
@@ -2380,7 +2393,7 @@ where L::Target: Logger {
2380
2393
score: cmp:: max( total_fee_msat, path_htlc_minimum_msat) . saturating_add( path_penalty_msat) ,
2381
2394
total_cltv_delta: hop_total_cltv_delta,
2382
2395
value_contribution_msat,
2383
- path_length_to_node,
2396
+ path_length_to_node: intermediate_path_length_to_node ,
2384
2397
is_intro_node: $candidate. blinded_hint_idx( ) . is_some( ) ,
2385
2398
} ;
2386
2399
targets. push( new_graph_node) ;
@@ -2625,9 +2638,8 @@ where L::Target: Logger {
2625
2638
} ;
2626
2639
let path_min = candidate. htlc_minimum_msat ( ) . saturating_add (
2627
2640
compute_fees_saturating ( candidate. htlc_minimum_msat ( ) , candidate. fees ( ) ) ) ;
2628
- add_entry ! ( & first_hop_candidate, blinded_path_fee,
2629
- path_contribution_msat, path_min, 0_u64 , candidate. cltv_expiry_delta( ) ,
2630
- candidate. blinded_path( ) . map_or( 1 , |bp| bp. blinded_hops. len( ) as u8 ) ) ;
2641
+ add_entry ! ( & first_hop_candidate, blinded_path_fee, path_contribution_msat, path_min,
2642
+ 0_u64 , candidate. cltv_expiry_delta( ) , 0 ) ;
2631
2643
}
2632
2644
}
2633
2645
}
@@ -2653,7 +2665,7 @@ where L::Target: Logger {
2653
2665
let mut aggregate_next_hops_path_htlc_minimum_msat: u64 = 0 ;
2654
2666
let mut aggregate_next_hops_path_penalty_msat: u64 = 0 ;
2655
2667
let mut aggregate_next_hops_cltv_delta: u32 = 0 ;
2656
- let mut aggregate_next_hops_path_length : u8 = 0 ;
2668
+ let mut aggregate_next_hops_intermediate_path_len : u8 = 0 ;
2657
2669
let mut aggregate_path_contribution_msat = path_value_msat;
2658
2670
2659
2671
for ( idx, ( hop, prev_hop_id) ) in hop_iter. zip ( prev_hop_iter) . enumerate ( ) {
@@ -2680,7 +2692,7 @@ where L::Target: Logger {
2680
2692
if let Some ( hop_used_msat) = add_entry ! ( & candidate,
2681
2693
aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
2682
2694
aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
2683
- aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length )
2695
+ aggregate_next_hops_cltv_delta, aggregate_next_hops_intermediate_path_len )
2684
2696
{
2685
2697
aggregate_path_contribution_msat = hop_used_msat;
2686
2698
} else {
@@ -2707,8 +2719,12 @@ where L::Target: Logger {
2707
2719
aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
2708
2720
. saturating_add ( hop. cltv_expiry_delta as u32 ) ;
2709
2721
2710
- aggregate_next_hops_path_length = aggregate_next_hops_path_length
2711
- . saturating_add ( 1 ) ;
2722
+ if idx > 0 {
2723
+ // The final hop doesn't count towards the number of intermediate hops, so only start
2724
+ // incrementing here after it's been processed.
2725
+ aggregate_next_hops_intermediate_path_len = aggregate_next_hops_intermediate_path_len
2726
+ . saturating_add ( 1 ) ;
2727
+ }
2712
2728
2713
2729
// Searching for a direct channel between last checked hop and first_hop_targets
2714
2730
if let Some ( first_channels) = first_hop_targets. get ( target) {
@@ -2723,7 +2739,7 @@ where L::Target: Logger {
2723
2739
add_entry ! ( & first_hop_candidate,
2724
2740
aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
2725
2741
aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
2726
- aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length ) ;
2742
+ aggregate_next_hops_cltv_delta, aggregate_next_hops_intermediate_path_len ) ;
2727
2743
}
2728
2744
}
2729
2745
@@ -2775,7 +2791,7 @@ where L::Target: Logger {
2775
2791
aggregate_next_hops_path_htlc_minimum_msat,
2776
2792
aggregate_next_hops_path_penalty_msat,
2777
2793
aggregate_next_hops_cltv_delta,
2778
- aggregate_next_hops_path_length ) ;
2794
+ aggregate_next_hops_intermediate_path_len ) ;
2779
2795
}
2780
2796
}
2781
2797
}
@@ -3390,7 +3406,8 @@ mod tests {
3390
3406
fn simple_route_test ( ) {
3391
3407
let ( secp_ctx, network_graph, _, _, logger) = build_graph ( ) ;
3392
3408
let ( _, our_id, _, nodes) = get_nodes ( & secp_ctx) ;
3393
- let payment_params = PaymentParameters :: from_node_id ( nodes[ 2 ] , 42 ) ;
3409
+ let mut payment_params = PaymentParameters :: from_node_id ( nodes[ 2 ] , 42 ) ;
3410
+ payment_params. max_intermediate_hops = 1 ;
3394
3411
let scorer = ln_test_utils:: TestScorer :: new ( ) ;
3395
3412
let keys_manager = ln_test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
3396
3413
let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
@@ -3405,7 +3422,7 @@ mod tests {
3405
3422
assert_eq ! ( err, "Cannot send a payment of 0 msat" ) ;
3406
3423
} else { panic ! ( ) ; }
3407
3424
3408
- let route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 100 ) ;
3425
+ let mut route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 100 ) ;
3409
3426
let route = get_route ( & our_id, & route_params, & network_graph. read_only ( ) , None ,
3410
3427
Arc :: clone ( & logger) , & scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap ( ) ;
3411
3428
assert_eq ! ( route. paths[ 0 ] . hops. len( ) , 2 ) ;
@@ -3423,6 +3440,10 @@ mod tests {
3423
3440
assert_eq ! ( route. paths[ 0 ] . hops[ 1 ] . cltv_expiry_delta, 42 ) ;
3424
3441
assert_eq ! ( route. paths[ 0 ] . hops[ 1 ] . node_features. le_flags( ) , & id_to_feature_flags( 3 ) ) ;
3425
3442
assert_eq ! ( route. paths[ 0 ] . hops[ 1 ] . channel_features. le_flags( ) , & id_to_feature_flags( 4 ) ) ;
3443
+
3444
+ route_params. payment_params . max_intermediate_hops = 0 ;
3445
+ get_route ( & our_id, & route_params, & network_graph. read_only ( ) , None ,
3446
+ Arc :: clone ( & logger) , & scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap_err ( ) ;
3426
3447
}
3427
3448
3428
3449
#[ test]
@@ -3827,7 +3848,8 @@ mod tests {
3827
3848
} ) ;
3828
3849
3829
3850
// If all the channels require some features we don't understand, route should fail
3830
- let route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 100 ) ;
3851
+ let mut route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 100 ) ;
3852
+ route_params. payment_params . max_intermediate_hops = 1 ;
3831
3853
if let Err ( LightningError { err, action : ErrorAction :: IgnoreError } ) = get_route ( & our_id,
3832
3854
& route_params, & network_graph. read_only ( ) , None , Arc :: clone ( & logger) , & scorer,
3833
3855
& Default :: default ( ) , & random_seed_bytes) {
@@ -4083,8 +4105,9 @@ mod tests {
4083
4105
} else { panic ! ( ) ; }
4084
4106
}
4085
4107
4086
- let payment_params = PaymentParameters :: from_node_id ( nodes[ 6 ] , 42 )
4108
+ let mut payment_params = PaymentParameters :: from_node_id ( nodes[ 6 ] , 42 )
4087
4109
. with_route_hints ( last_hops_multi_private_channels ( & nodes) ) . unwrap ( ) ;
4110
+ payment_params. max_intermediate_hops = 4 ;
4088
4111
let route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 100 ) ;
4089
4112
let route = get_route ( & our_id, & route_params, & network_graph. read_only ( ) , None ,
4090
4113
Arc :: clone ( & logger) , & scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap ( ) ;
@@ -4242,7 +4265,8 @@ mod tests {
4242
4265
let keys_manager = ln_test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
4243
4266
let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
4244
4267
// Test through channels 2, 3, 0xff00, 0xff01.
4245
- // Test shows that multiple hop hints are considered.
4268
+ // Test shows that multi-hop route hints are considered and factored correctly into the
4269
+ // max path length.
4246
4270
4247
4271
// Disabling channels 6 & 7 by flags=2
4248
4272
update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 2 ] , UnsignedChannelUpdate {
@@ -4270,7 +4294,8 @@ mod tests {
4270
4294
excess_data : Vec :: new ( )
4271
4295
} ) ;
4272
4296
4273
- let route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 100 ) ;
4297
+ let mut route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 100 ) ;
4298
+ route_params. payment_params . max_intermediate_hops = 3 ;
4274
4299
let route = get_route ( & our_id, & route_params, & network_graph. read_only ( ) , None ,
4275
4300
Arc :: clone ( & logger) , & scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap ( ) ;
4276
4301
assert_eq ! ( route. paths[ 0 ] . hops. len( ) , 4 ) ;
@@ -4302,6 +4327,9 @@ mod tests {
4302
4327
assert_eq ! ( route. paths[ 0 ] . hops[ 3 ] . cltv_expiry_delta, 42 ) ;
4303
4328
assert_eq ! ( route. paths[ 0 ] . hops[ 3 ] . node_features. le_flags( ) , default_node_features( ) . le_flags( ) ) ; // We dont pass flags in from invoices yet
4304
4329
assert_eq ! ( route. paths[ 0 ] . hops[ 3 ] . channel_features. le_flags( ) , & Vec :: <u8 >:: new( ) ) ; // We can't learn any flags from invoices, sadly
4330
+ route_params. payment_params . max_intermediate_hops = 2 ;
4331
+ get_route ( & our_id, & route_params, & network_graph. read_only ( ) , None ,
4332
+ Arc :: clone ( & logger) , & scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap_err ( ) ;
4305
4333
}
4306
4334
4307
4335
#[ test]
@@ -6843,16 +6871,20 @@ mod tests {
6843
6871
let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
6844
6872
6845
6873
// First check we can actually create a long route on this graph.
6846
- let feasible_payment_params = PaymentParameters :: from_node_id ( nodes[ 18 ] , 0 ) ;
6874
+ let mut feasible_payment_params = PaymentParameters :: from_node_id ( nodes[ 18 ] , 0 ) ;
6875
+ // Check that we default to MAX_PATH_LENGTH_ESTIMATE for the path length limit.
6876
+ assert_eq ! ( feasible_payment_params. max_intermediate_hops, MAX_PATH_LENGTH_ESTIMATE ) ;
6877
+ feasible_payment_params. max_intermediate_hops = 18 ;
6847
6878
let route_params = RouteParameters :: from_payment_params_and_value (
6848
6879
feasible_payment_params, 100 ) ;
6849
6880
let route = get_route ( & our_id, & route_params, & network_graph, None , Arc :: clone ( & logger) ,
6850
6881
& scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap ( ) ;
6851
6882
let path = route. paths [ 0 ] . hops . iter ( ) . map ( |hop| hop. short_channel_id ) . collect :: < Vec < _ > > ( ) ;
6852
- assert ! ( path. len( ) == MAX_PATH_LENGTH_ESTIMATE . into ( ) ) ;
6883
+ assert ! ( path. len( ) == route_params . payment_params . max_intermediate_hops as usize + 1 ) ;
6853
6884
6854
- // But we can't create a path surpassing the MAX_PATH_LENGTH_ESTIMATE limit.
6855
- let fail_payment_params = PaymentParameters :: from_node_id ( nodes[ 19 ] , 0 ) ;
6885
+ // But we can't create a path surpassing the parameterized path length limit.
6886
+ let mut fail_payment_params = PaymentParameters :: from_node_id ( nodes[ 19 ] , 0 ) ;
6887
+ fail_payment_params. max_intermediate_hops = 18 ;
6856
6888
let route_params = RouteParameters :: from_payment_params_and_value (
6857
6889
fail_payment_params, 100 ) ;
6858
6890
match get_route ( & our_id, & route_params, & network_graph, None , Arc :: clone ( & logger) , & scorer,
0 commit comments