@@ -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 from that node to the destination. This should never happen, but with
1789+ /// multiple codepaths processing channels we've had issues here in the past, so in debug-mode
1790+ /// we track it and 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 ///
@@ -2427,6 +2433,19 @@ where L::Target: Logger {
24272433 // We "return" whether we updated the path at the end, and how much we can route via
24282434 // this channel, via this:
24292435 let mut hop_contribution_amt_msat = None ;
2436+
2437+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2438+ if let Some ( counter) = $candidate. target_node_counter( ) {
2439+ // Once we are adding paths backwards from a given target, we've selected the best
2440+ // path from that target to the destination and it should no longer change. We thus
2441+ // set the best-path selected flag and check that it doesn't change below.
2442+ if let Some ( node) = & mut dist[ counter as usize ] {
2443+ node. best_path_from_hop_selected = true ;
2444+ } else if counter != payee_node_counter {
2445+ panic!( "No dist entry for target node counter {}" , counter) ;
2446+ }
2447+ }
2448+
24302449 // Channels to self should not be used. This is more of belt-and-suspenders, because in
24312450 // practice these cases should be caught earlier:
24322451 // - for regular channels at channel announcement (TODO)
@@ -2591,6 +2610,8 @@ where L::Target: Logger {
25912610 was_processed: false ,
25922611 is_first_hop_target: false ,
25932612 is_last_hop_target: false ,
2613+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2614+ best_path_from_hop_selected: false ,
25942615 value_contribution_msat,
25952616 } ) ;
25962617 dist_entry. as_mut( ) . unwrap( )
@@ -2671,6 +2692,11 @@ where L::Target: Logger {
26712692 || ( new_cost == old_cost && old_entry. value_contribution_msat < value_contribution_msat) ;
26722693
26732694 if !old_entry. was_processed && should_replace {
2695+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2696+ {
2697+ assert!( !old_entry. best_path_from_hop_selected) ;
2698+ }
2699+
26742700 let new_graph_node = RouteGraphNode {
26752701 node_id: src_node_id,
26762702 node_counter: src_node_counter,
@@ -2884,6 +2910,8 @@ where L::Target: Logger {
28842910 is_first_hop_target : true ,
28852911 is_last_hop_target : false ,
28862912 value_contribution_msat : 0 ,
2913+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2914+ best_path_from_hop_selected : false ,
28872915 } ) ;
28882916 }
28892917 for ( target_node_counter, candidates) in last_hop_candidates. iter ( ) {
@@ -2911,6 +2939,8 @@ where L::Target: Logger {
29112939 is_first_hop_target : false ,
29122940 is_last_hop_target : true ,
29132941 value_contribution_msat : 0 ,
2942+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
2943+ best_path_from_hop_selected : false ,
29142944 } ) ;
29152945 }
29162946 }
0 commit comments