@@ -1784,6 +1784,12 @@ struct PathBuildingHop<'a> {
17841784 /// decrease as well. Thus, we have to explicitly track which nodes have been processed and
17851785 /// avoid processing them again.
17861786 was_processed : bool ,
1787+ /// If we've already processed a channel backwards from a target node, we shouldn't update our
1788+ /// selected best path to that node. This should never happen, but with multiple codepaths
1789+ /// processing channels we've had issues here in the past, so in debug-mode we track it and
1790+ /// assert on it when processing a node.
1791+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1792+ best_path_from_hop_selected : bool ,
17871793 /// When processing a node as the next best-score candidate, we want to quickly check if it is
17881794 /// a direct counterparty of ours, using our local channel information immediately if we can.
17891795 ///
@@ -2425,6 +2431,19 @@ where L::Target: Logger {
24252431 // We "return" whether we updated the path at the end, and how much we can route via
24262432 // this channel, via this:
24272433 let mut hop_contribution_amt_msat = None ;
2434+
2435+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2436+ if let Some ( counter) = $candidate. target_node_counter( ) {
2437+ // Once we are adding paths backwards from a given target, we've sepected the best
2438+ // path from that target to the destination and it should no longer change. We thus
2439+ // set the best-path selected flag and check that it doesn't change below.
2440+ if let Some ( node) = & mut dist[ counter as usize ] {
2441+ node. best_path_from_hop_selected = true ;
2442+ } else if counter != payee_node_counter {
2443+ panic!( "No dist entry for target node counter {}" , counter) ;
2444+ }
2445+ }
2446+
24282447 // Channels to self should not be used. This is more of belt-and-suspenders, because in
24292448 // practice these cases should be caught earlier:
24302449 // - for regular channels at channel announcement (TODO)
@@ -2589,6 +2608,8 @@ where L::Target: Logger {
25892608 was_processed: false ,
25902609 is_first_hop_target: false ,
25912610 is_last_hop_target: false ,
2611+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2612+ best_path_from_hop_selected: false ,
25922613 value_contribution_msat,
25932614 } ) ;
25942615 dist_entry. as_mut( ) . unwrap( )
@@ -2669,6 +2690,11 @@ where L::Target: Logger {
26692690 || ( new_cost == old_cost && old_entry. value_contribution_msat < value_contribution_msat) ;
26702691
26712692 if !old_entry. was_processed && should_replace {
2693+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2694+ {
2695+ assert!( !old_entry. best_path_from_hop_selected) ;
2696+ }
2697+
26722698 let new_graph_node = RouteGraphNode {
26732699 node_id: src_node_id,
26742700 node_counter: src_node_counter,
@@ -2872,6 +2898,8 @@ where L::Target: Logger {
28722898 is_first_hop_target : true ,
28732899 is_last_hop_target : false ,
28742900 value_contribution_msat : 0 ,
2901+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2902+ best_path_from_hop_selected : false ,
28752903 } ) ;
28762904 }
28772905 for ( target_node_counter, candidates) in last_hop_candidates. iter ( ) {
@@ -2899,6 +2927,8 @@ where L::Target: Logger {
28992927 is_first_hop_target : false ,
29002928 is_last_hop_target : true ,
29012929 value_contribution_msat : 0 ,
2930+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2931+ best_path_from_hop_selected : false ,
29022932 } ) ;
29032933 }
29042934 }
0 commit comments