@@ -1659,6 +1659,14 @@ struct PathBuildingHop<'a> {
1659
1659
/// decrease as well. Thus, we have to explicitly track which nodes have been processed and
1660
1660
/// avoid processing them again.
1661
1661
was_processed : bool ,
1662
+ /// When processing a node as the next best-score candidate, we want to quickly check if it is
1663
+ /// a direct counterparty of ours, using our local channel information immediately if we can.
1664
+ ///
1665
+ /// In order to do so efficiently, we cache whether a node is a direct counterparty here at the
1666
+ /// start of a route-finding pass. Unlike all other fields in this struct, this field is never
1667
+ /// updated after being initialized - it is set at the start of a route-finding pass and only
1668
+ /// read thereafter.
1669
+ is_first_hop_target : bool ,
1662
1670
/// Used to compare channels when choosing the for routing.
1663
1671
/// Includes paying for the use of a hop and the following hops, as well as
1664
1672
/// an estimated cost of reaching this hop.
@@ -2452,7 +2460,7 @@ where L::Target: Logger {
2452
2460
. saturating_add( curr_min) ;
2453
2461
2454
2462
let dist_entry = & mut dist[ $candidate. src_node_counter( ) as usize ] ;
2455
- let mut old_entry = if let Some ( hop) = dist_entry {
2463
+ let old_entry = if let Some ( hop) = dist_entry {
2456
2464
hop
2457
2465
} else {
2458
2466
// If there was previously no known way to access the source node
@@ -2470,6 +2478,7 @@ where L::Target: Logger {
2470
2478
path_htlc_minimum_msat,
2471
2479
path_penalty_msat: u64 :: max_value( ) ,
2472
2480
was_processed: false ,
2481
+ is_first_hop_target: false ,
2473
2482
#[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2474
2483
value_contribution_msat,
2475
2484
} ) ;
@@ -2634,12 +2643,14 @@ where L::Target: Logger {
2634
2643
let fee_to_target_msat;
2635
2644
let next_hops_path_htlc_minimum_msat;
2636
2645
let next_hops_path_penalty_msat;
2646
+ let is_first_hop_target;
2637
2647
let skip_node = if let Some ( elem) = & mut dist[ $node. node_counter as usize ] {
2638
2648
let was_processed = elem. was_processed;
2639
2649
elem. was_processed = true ;
2640
2650
fee_to_target_msat = elem. total_fee_msat;
2641
2651
next_hops_path_htlc_minimum_msat = elem. path_htlc_minimum_msat;
2642
2652
next_hops_path_penalty_msat = elem. path_penalty_msat;
2653
+ is_first_hop_target = elem. is_first_hop_target;
2643
2654
was_processed
2644
2655
} else {
2645
2656
// Entries are added to dist in add_entry!() when there is a channel from a node.
@@ -2650,21 +2661,24 @@ where L::Target: Logger {
2650
2661
fee_to_target_msat = 0 ;
2651
2662
next_hops_path_htlc_minimum_msat = 0 ;
2652
2663
next_hops_path_penalty_msat = 0 ;
2664
+ is_first_hop_target = false ;
2653
2665
false
2654
2666
} ;
2655
2667
2656
2668
if !skip_node {
2657
- if let Some ( ( first_channels, peer_node_counter) ) = first_hop_targets. get( & $node_id) {
2658
- for details in first_channels {
2659
- debug_assert_eq!( * peer_node_counter, $node. node_counter) ;
2660
- let candidate = CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2661
- details, payer_node_id: & our_node_id, payer_node_counter,
2662
- target_node_counter: $node. node_counter,
2663
- } ) ;
2664
- add_entry!( & candidate, fee_to_target_msat,
2665
- $next_hops_value_contribution,
2666
- next_hops_path_htlc_minimum_msat, next_hops_path_penalty_msat,
2667
- $next_hops_cltv_delta, $next_hops_path_length) ;
2669
+ if is_first_hop_target {
2670
+ if let Some ( ( first_channels, peer_node_counter) ) = first_hop_targets. get( & $node_id) {
2671
+ for details in first_channels {
2672
+ debug_assert_eq!( * peer_node_counter, $node. node_counter) ;
2673
+ let candidate = CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2674
+ details, payer_node_id: & our_node_id, payer_node_counter,
2675
+ target_node_counter: $node. node_counter,
2676
+ } ) ;
2677
+ add_entry!( & candidate, fee_to_target_msat,
2678
+ $next_hops_value_contribution,
2679
+ next_hops_path_htlc_minimum_msat, next_hops_path_penalty_msat,
2680
+ $next_hops_cltv_delta, $next_hops_path_length) ;
2681
+ }
2668
2682
}
2669
2683
}
2670
2684
@@ -2711,6 +2725,33 @@ where L::Target: Logger {
2711
2725
for e in dist. iter_mut ( ) {
2712
2726
* e = None ;
2713
2727
}
2728
+ for ( _, ( chans, peer_node_counter) ) in first_hop_targets. iter ( ) {
2729
+ // In order to avoid looking up whether each node is a first-hop target, we store a
2730
+ // dummy entry in dist for each first-hop target, allowing us to do this lookup for
2731
+ // free since we're already looking at the `was_processed` flag.
2732
+ //
2733
+ // Note that all the fields (except `is_first_hop_target`) will be overwritten whenever
2734
+ // we find a path to the target, so are left as dummies here.
2735
+ dist[ * peer_node_counter as usize ] = Some ( PathBuildingHop {
2736
+ candidate : CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2737
+ details : & chans[ 0 ] ,
2738
+ payer_node_id : & our_node_id,
2739
+ target_node_counter : u32:: max_value ( ) ,
2740
+ payer_node_counter : u32:: max_value ( ) ,
2741
+ } ) ,
2742
+ target_node_counter : None ,
2743
+ fee_msat : 0 ,
2744
+ next_hops_fee_msat : u64:: max_value ( ) ,
2745
+ hop_use_fee_msat : u64:: max_value ( ) ,
2746
+ total_fee_msat : u64:: max_value ( ) ,
2747
+ path_htlc_minimum_msat : u64:: max_value ( ) ,
2748
+ path_penalty_msat : u64:: max_value ( ) ,
2749
+ was_processed : false ,
2750
+ is_first_hop_target : true ,
2751
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2752
+ value_contribution_msat : 0 ,
2753
+ } ) ;
2754
+ }
2714
2755
hit_minimum_limit = false ;
2715
2756
2716
2757
// If first hop is a private channel and the only way to reach the payee, this is the only
0 commit comments