@@ -1542,6 +1542,14 @@ struct PathBuildingHop<'a> {
1542
1542
/// decrease as well. Thus, we have to explicitly track which nodes have been processed and
1543
1543
/// avoid processing them again.
1544
1544
was_processed : bool ,
1545
+ /// When processing a node as the next best-score candidate, we want to quickly check if it is
1546
+ /// a direct counterparty of ours, using our local channel information immediately if we can.
1547
+ ///
1548
+ /// In order to do so efficiently, we cache whether a node is a direct counterparty here at the
1549
+ /// start of a route-finding pass. Unlike all other fields in this struct, this field is never
1550
+ /// updated after being initialized - it is set at the start of a route-finding pass and only
1551
+ /// read thereafter.
1552
+ is_first_hop_target : bool ,
1545
1553
/// Used to compare channels when choosing the for routing.
1546
1554
/// Includes paying for the use of a hop and the following hops, as well as
1547
1555
/// an estimated cost of reaching this hop.
@@ -2318,7 +2326,7 @@ where L::Target: Logger {
2318
2326
. saturating_add( curr_min) ;
2319
2327
2320
2328
let dist_entry = & mut dist[ $candidate. src_node_counter( ) as usize ] ;
2321
- let mut old_entry = if let Some ( hop) = dist_entry {
2329
+ let old_entry = if let Some ( hop) = dist_entry {
2322
2330
hop
2323
2331
} else {
2324
2332
// If there was previously no known way to access the source node
@@ -2336,6 +2344,7 @@ where L::Target: Logger {
2336
2344
path_htlc_minimum_msat,
2337
2345
path_penalty_msat: u64 :: max_value( ) ,
2338
2346
was_processed: false ,
2347
+ is_first_hop_target: false ,
2339
2348
#[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2340
2349
value_contribution_msat,
2341
2350
} ) ;
@@ -2500,12 +2509,14 @@ where L::Target: Logger {
2500
2509
let fee_to_target_msat;
2501
2510
let next_hops_path_htlc_minimum_msat;
2502
2511
let next_hops_path_penalty_msat;
2512
+ let is_first_hop_target;
2503
2513
let skip_node = if let Some ( elem) = & mut dist[ $node. node_counter as usize ] {
2504
2514
let was_processed = elem. was_processed;
2505
2515
elem. was_processed = true ;
2506
2516
fee_to_target_msat = elem. total_fee_msat;
2507
2517
next_hops_path_htlc_minimum_msat = elem. path_htlc_minimum_msat;
2508
2518
next_hops_path_penalty_msat = elem. path_penalty_msat;
2519
+ is_first_hop_target = elem. is_first_hop_target;
2509
2520
was_processed
2510
2521
} else {
2511
2522
// Entries are added to dist in add_entry!() when there is a channel from a node.
@@ -2516,21 +2527,24 @@ where L::Target: Logger {
2516
2527
fee_to_target_msat = 0 ;
2517
2528
next_hops_path_htlc_minimum_msat = 0 ;
2518
2529
next_hops_path_penalty_msat = 0 ;
2530
+ is_first_hop_target = false ;
2519
2531
false
2520
2532
} ;
2521
2533
2522
2534
if !skip_node {
2523
- if let Some ( ( first_channels, peer_node_counter) ) = first_hop_targets. get( & $node_id) {
2524
- for details in first_channels {
2525
- debug_assert_eq!( * peer_node_counter, $node. node_counter) ;
2526
- let candidate = CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2527
- details, payer_node_id: & our_node_id, payer_node_counter,
2528
- target_node_counter: $node. node_counter,
2529
- } ) ;
2530
- add_entry!( & candidate, fee_to_target_msat,
2531
- $next_hops_value_contribution,
2532
- next_hops_path_htlc_minimum_msat, next_hops_path_penalty_msat,
2533
- $next_hops_cltv_delta, $next_hops_path_length) ;
2535
+ if is_first_hop_target {
2536
+ if let Some ( ( first_channels, peer_node_counter) ) = first_hop_targets. get( & $node_id) {
2537
+ for details in first_channels {
2538
+ debug_assert_eq!( * peer_node_counter, $node. node_counter) ;
2539
+ let candidate = CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2540
+ details, payer_node_id: & our_node_id, payer_node_counter,
2541
+ target_node_counter: $node. node_counter,
2542
+ } ) ;
2543
+ add_entry!( & candidate, fee_to_target_msat,
2544
+ $next_hops_value_contribution,
2545
+ next_hops_path_htlc_minimum_msat, next_hops_path_penalty_msat,
2546
+ $next_hops_cltv_delta, $next_hops_path_length) ;
2547
+ }
2534
2548
}
2535
2549
}
2536
2550
@@ -2577,6 +2591,33 @@ where L::Target: Logger {
2577
2591
for e in dist. iter_mut ( ) {
2578
2592
* e = None ;
2579
2593
}
2594
+ for ( _, ( chans, peer_node_counter) ) in first_hop_targets. iter ( ) {
2595
+ // In order to avoid looking up whether each node is a first-hop target, we store a
2596
+ // dummy entry in dist for each first-hop target, allowing us to do this lookup for
2597
+ // free since we're already looking at the `was_processed` flag.
2598
+ //
2599
+ // Note that all the fields (except `is_first_hop_target`) will be overwritten whenever
2600
+ // we find a path to the target, so are left as dummies here.
2601
+ dist[ * peer_node_counter as usize ] = Some ( PathBuildingHop {
2602
+ candidate : CandidateRouteHop :: FirstHop ( FirstHopCandidate {
2603
+ details : & chans[ 0 ] ,
2604
+ payer_node_id : & our_node_id,
2605
+ target_node_counter : u32:: max_value ( ) ,
2606
+ payer_node_counter : u32:: max_value ( ) ,
2607
+ } ) ,
2608
+ target_node_counter : None ,
2609
+ fee_msat : 0 ,
2610
+ next_hops_fee_msat : u64:: max_value ( ) ,
2611
+ hop_use_fee_msat : u64:: max_value ( ) ,
2612
+ total_fee_msat : u64:: max_value ( ) ,
2613
+ path_htlc_minimum_msat : u64:: max_value ( ) ,
2614
+ path_penalty_msat : u64:: max_value ( ) ,
2615
+ was_processed : false ,
2616
+ is_first_hop_target : true ,
2617
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2618
+ value_contribution_msat : 0 ,
2619
+ } ) ;
2620
+ }
2580
2621
hit_minimum_limit = false ;
2581
2622
2582
2623
// If first hop is a private channel and the only way to reach the payee, this is the only
0 commit comments