@@ -1816,6 +1816,12 @@ struct PathBuildingHop<'a> {
1816
1816
/// decrease as well. Thus, we have to explicitly track which nodes have been processed and
1817
1817
/// avoid processing them again.
1818
1818
was_processed : bool ,
1819
+ /// If we've already processed a channel backwards from a target node, we shouldn't update our
1820
+ /// selected best path from that node to the destination. This should never happen, but with
1821
+ /// multiple codepaths processing channels we've had issues here in the past, so in debug-mode
1822
+ /// we track it and assert on it when processing a node.
1823
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1824
+ best_path_from_hop_selected : bool ,
1819
1825
/// When processing a node as the next best-score candidate, we want to quickly check if it is
1820
1826
/// a direct counterparty of ours, using our local channel information immediately if we can.
1821
1827
///
@@ -2459,6 +2465,19 @@ where L::Target: Logger {
2459
2465
// We "return" whether we updated the path at the end, and how much we can route via
2460
2466
// this channel, via this:
2461
2467
let mut hop_contribution_amt_msat = None ;
2468
+
2469
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2470
+ if let Some ( counter) = $candidate. target_node_counter( ) {
2471
+ // Once we are adding paths backwards from a given target, we've selected the best
2472
+ // path from that target to the destination and it should no longer change. We thus
2473
+ // set the best-path selected flag and check that it doesn't change below.
2474
+ if let Some ( node) = & mut dist[ counter as usize ] {
2475
+ node. best_path_from_hop_selected = true ;
2476
+ } else if counter != payee_node_counter {
2477
+ panic!( "No dist entry for target node counter {}" , counter) ;
2478
+ }
2479
+ }
2480
+
2462
2481
// Channels to self should not be used. This is more of belt-and-suspenders, because in
2463
2482
// practice these cases should be caught earlier:
2464
2483
// - for regular channels at channel announcement (TODO)
@@ -2623,6 +2642,8 @@ where L::Target: Logger {
2623
2642
was_processed: false ,
2624
2643
is_first_hop_target: false ,
2625
2644
is_last_hop_target: false ,
2645
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2646
+ best_path_from_hop_selected: false ,
2626
2647
value_contribution_msat,
2627
2648
} ) ;
2628
2649
dist_entry. as_mut( ) . unwrap( )
@@ -2703,6 +2724,11 @@ where L::Target: Logger {
2703
2724
|| ( new_cost == old_cost && old_entry. value_contribution_msat < value_contribution_msat) ;
2704
2725
2705
2726
if !old_entry. was_processed && should_replace {
2727
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2728
+ {
2729
+ assert!( !old_entry. best_path_from_hop_selected) ;
2730
+ }
2731
+
2706
2732
let new_graph_node = RouteGraphNode {
2707
2733
node_id: src_node_id,
2708
2734
node_counter: src_node_counter,
@@ -2916,6 +2942,8 @@ where L::Target: Logger {
2916
2942
is_first_hop_target : true ,
2917
2943
is_last_hop_target : false ,
2918
2944
value_contribution_msat : 0 ,
2945
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2946
+ best_path_from_hop_selected : false ,
2919
2947
} ) ;
2920
2948
}
2921
2949
for ( target_node_counter, candidates) in last_hop_candidates. iter ( ) {
@@ -2943,6 +2971,8 @@ where L::Target: Logger {
2943
2971
is_first_hop_target : false ,
2944
2972
is_last_hop_target : true ,
2945
2973
value_contribution_msat : 0 ,
2974
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2975
+ best_path_from_hop_selected : false ,
2946
2976
} ) ;
2947
2977
}
2948
2978
}
0 commit comments