@@ -1772,6 +1772,14 @@ struct PathBuildingHop<'a> {
1772
1772
/// decrease as well. Thus, we have to explicitly track which nodes have been processed and
1773
1773
/// avoid processing them again.
1774
1774
was_processed : bool ,
1775
+ /// When processing a node as the next best-score candidate, we want to quickly check if it is
1776
+ /// a direct counterparty of ours, using our local channel information immediately if we can.
1777
+ ///
1778
+ /// In order to do so efficiently, we cache whether a node is a direct counterparty here at the
1779
+ /// start of a route-finding pass. Unlike all other fields in this struct, this field is never
1780
+ /// updated after being initialized - it is set at the start of a route-finding pass and only
1781
+ /// read thereafter.
1782
+ is_first_hop_target : bool ,
1775
1783
/// Used to compare channels when choosing the for routing.
1776
1784
/// Includes paying for the use of a hop and the following hops, as well as
1777
1785
/// an estimated cost of reaching this hop.
@@ -1810,6 +1818,7 @@ impl<'a> core::fmt::Debug for PathBuildingHop<'a> {
1810
1818
. field ( "source_node_id" , & self . candidate . source ( ) )
1811
1819
. field ( "target_node_id" , & self . candidate . target ( ) )
1812
1820
. field ( "short_channel_id" , & self . candidate . short_channel_id ( ) )
1821
+ . field ( "is_first_hop_target" , & self . is_first_hop_target )
1813
1822
. field ( "total_fee_msat" , & self . total_fee_msat )
1814
1823
. field ( "next_hops_fee_msat" , & self . next_hops_fee_msat )
1815
1824
. field ( "hop_use_fee_msat" , & self . hop_use_fee_msat )
@@ -2516,6 +2525,7 @@ where L::Target: Logger {
2516
2525
path_htlc_minimum_msat,
2517
2526
path_penalty_msat: u64 :: max_value( ) ,
2518
2527
was_processed: false ,
2528
+ is_first_hop_target: false ,
2519
2529
#[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2520
2530
value_contribution_msat,
2521
2531
} ) ;
@@ -2679,12 +2689,14 @@ where L::Target: Logger {
2679
2689
let fee_to_target_msat;
2680
2690
let next_hops_path_htlc_minimum_msat;
2681
2691
let next_hops_path_penalty_msat;
2692
+ let is_first_hop_target;
2682
2693
let skip_node = if let Some ( elem) = & mut dist[ $node. node_counter as usize ] {
2683
2694
let was_processed = elem. was_processed;
2684
2695
elem. was_processed = true ;
2685
2696
fee_to_target_msat = elem. total_fee_msat;
2686
2697
next_hops_path_htlc_minimum_msat = elem. path_htlc_minimum_msat;
2687
2698
next_hops_path_penalty_msat = elem. path_penalty_msat;
2699
+ is_first_hop_target = elem. is_first_hop_target;
2688
2700
was_processed
2689
2701
} else {
2690
2702
// Entries are added to dist in add_entry!() when there is a channel from a node.
@@ -2695,21 +2707,24 @@ where L::Target: Logger {
2695
2707
fee_to_target_msat = 0 ;
2696
2708
next_hops_path_htlc_minimum_msat = 0 ;
2697
2709
next_hops_path_penalty_msat = 0 ;
2710
+ is_first_hop_target = false ;
2698
2711
false
2699
2712
} ;
2700
2713
2701
2714
if !skip_node {
2702
- if let Some ( ( first_channels, peer_node_counter) ) = first_hop_targets. get( & $node_id) {
2703
- for details in first_channels {
2704
- debug_assert_eq!( * peer_node_counter, $node. node_counter) ;
2705
- let candidate = CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2706
- details, payer_node_id: & our_node_id, payer_node_counter,
2707
- target_node_counter: $node. node_counter,
2708
- } ) ;
2709
- add_entry!( & candidate, fee_to_target_msat,
2710
- $next_hops_value_contribution,
2711
- next_hops_path_htlc_minimum_msat, next_hops_path_penalty_msat,
2712
- $next_hops_cltv_delta, $next_hops_path_length) ;
2715
+ if is_first_hop_target {
2716
+ if let Some ( ( first_channels, peer_node_counter) ) = first_hop_targets. get( & $node_id) {
2717
+ for details in first_channels {
2718
+ debug_assert_eq!( * peer_node_counter, $node. node_counter) ;
2719
+ let candidate = CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2720
+ details, payer_node_id: & our_node_id, payer_node_counter,
2721
+ target_node_counter: $node. node_counter,
2722
+ } ) ;
2723
+ add_entry!( & candidate, fee_to_target_msat,
2724
+ $next_hops_value_contribution,
2725
+ next_hops_path_htlc_minimum_msat, next_hops_path_penalty_msat,
2726
+ $next_hops_cltv_delta, $next_hops_path_length) ;
2727
+ }
2713
2728
}
2714
2729
}
2715
2730
@@ -2756,6 +2771,32 @@ where L::Target: Logger {
2756
2771
for e in dist. iter_mut ( ) {
2757
2772
* e = None ;
2758
2773
}
2774
+ for ( _, ( chans, peer_node_counter) ) in first_hop_targets. iter ( ) {
2775
+ // In order to avoid looking up whether each node is a first-hop target, we store a
2776
+ // dummy entry in dist for each first-hop target, allowing us to do this lookup for
2777
+ // free since we're already looking at the `was_processed` flag.
2778
+ //
2779
+ // Note that all the fields (except `is_first_hop_target`) will be overwritten whenever
2780
+ // we find a path to the target, so are left as dummies here.
2781
+ dist[ * peer_node_counter as usize ] = Some ( PathBuildingHop {
2782
+ candidate : CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2783
+ details : & chans[ 0 ] ,
2784
+ payer_node_id : & our_node_id,
2785
+ target_node_counter : u32:: max_value ( ) ,
2786
+ payer_node_counter : u32:: max_value ( ) ,
2787
+ } ) ,
2788
+ fee_msat : 0 ,
2789
+ next_hops_fee_msat : u64:: max_value ( ) ,
2790
+ hop_use_fee_msat : u64:: max_value ( ) ,
2791
+ total_fee_msat : u64:: max_value ( ) ,
2792
+ path_htlc_minimum_msat : u64:: max_value ( ) ,
2793
+ path_penalty_msat : u64:: max_value ( ) ,
2794
+ was_processed : false ,
2795
+ is_first_hop_target : true ,
2796
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2797
+ value_contribution_msat : 0 ,
2798
+ } ) ;
2799
+ }
2759
2800
hit_minimum_limit = false ;
2760
2801
2761
2802
// If first hop is a private channel and the only way to reach the payee, this is the only
0 commit comments