@@ -200,6 +200,12 @@ struct PathBuildingHop {
200
200
/// A mirror of the same field in RouteGraphNode. Note that this is only used during the graph
201
201
/// walk and may be invalid thereafter.
202
202
path_htlc_minimum_msat : u64 ,
203
+ /// If we've already processed a node as the best node, we shouldn't process it again. Normally
204
+ /// we'd just ignore it if we did as all channels would have a higher new fee, but because we
205
+ /// may decrease the amounts in use as we walk the graph, the actual calculated fee may
206
+ /// decrease as well. Thus, we have to explicitly track which nodes have been processed and
207
+ /// avoid processing them again.
208
+ was_processed : bool ,
203
209
}
204
210
205
211
// Instantiated with a list of hops with correct data in them collected during path finding,
@@ -614,85 +620,97 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
614
620
total_fee_msat: u64 :: max_value( ) ,
615
621
htlc_minimum_msat: $directional_info. htlc_minimum_msat,
616
622
path_htlc_minimum_msat,
623
+ was_processed: false ,
617
624
}
618
625
} ) ;
619
626
620
- let mut hop_use_fee_msat = 0 ;
621
- let mut total_fee_msat = $next_hops_fee_msat;
622
-
623
- // Ignore hop_use_fee_msat for channel-from-us as we assume all channels-from-us
624
- // will have the same effective-fee
625
- if $src_node_id != * our_node_id {
626
- match compute_fees( amount_to_transfer_over_msat, $directional_info. fees) {
627
- // max_value means we'll always fail
628
- // the old_entry.total_fee_msat > total_fee_msat check
629
- None => total_fee_msat = u64 :: max_value( ) ,
630
- Some ( fee_msat) => {
631
- hop_use_fee_msat = fee_msat;
632
- total_fee_msat += hop_use_fee_msat;
633
- // When calculating the lowest inbound fees to a node, we
634
- // calculate fees here not based on the actual value we think
635
- // will flow over this channel, but on the minimum value that
636
- // we'll accept flowing over it. Otherwise we may later find a
637
- // different path to the source node that is more expensive,
638
- // but which we consider to be cheaper because we are capacity
639
- // constrained and the relative fee becomes lower.
640
- match compute_fees( minimal_value_contribution_msat, old_entry. src_lowest_inbound_fees)
641
- . map( |a| a. checked_add( total_fee_msat) ) {
642
- Some ( Some ( v) ) => {
643
- total_fee_msat = v;
644
- } ,
645
- _ => {
646
- total_fee_msat = u64 :: max_value( ) ;
647
- }
648
- } ;
627
+ if !old_entry. was_processed {
628
+ let mut hop_use_fee_msat = 0 ;
629
+ let mut total_fee_msat = $next_hops_fee_msat;
630
+
631
+ // Ignore hop_use_fee_msat for channel-from-us as we assume all channels-from-us
632
+ // will have the same effective-fee
633
+ if $src_node_id != * our_node_id {
634
+ match compute_fees( amount_to_transfer_over_msat, $directional_info. fees) {
635
+ // max_value means we'll always fail
636
+ // the old_entry.total_fee_msat > total_fee_msat check
637
+ None => total_fee_msat = u64 :: max_value( ) ,
638
+ Some ( fee_msat) => {
639
+ hop_use_fee_msat = fee_msat;
640
+ total_fee_msat += hop_use_fee_msat;
641
+ // When calculating the lowest inbound fees to a node, we
642
+ // calculate fees here not based on the actual value we think
643
+ // will flow over this channel, but on the minimum value that
644
+ // we'll accept flowing over it. Otherwise we may later find a
645
+ // different path to the source node that is more expensive,
646
+ // but which we consider to be cheaper because we are capacity
647
+ // constrained and the relative fee becomes lower.
648
+ match compute_fees( minimal_value_contribution_msat, old_entry. src_lowest_inbound_fees)
649
+ . map( |a| a. checked_add( total_fee_msat) ) {
650
+ Some ( Some ( v) ) => {
651
+ total_fee_msat = v;
652
+ } ,
653
+ _ => {
654
+ total_fee_msat = u64 :: max_value( ) ;
655
+ }
656
+ } ;
657
+
658
+ match compute_fees( minimal_value_contribution_msat, old_entry. src_lowest_inbound_fees) . map( |a| a. checked_add( total_fee_msat) ) {
659
+ Some ( Some ( v) ) => {
660
+ total_fee_msat = v;
661
+ } ,
662
+ _ => {
663
+ total_fee_msat = u64 :: max_value( ) ;
664
+ }
665
+ } ;
666
+ }
649
667
}
650
668
}
651
- }
652
-
653
- let new_graph_node = RouteGraphNode {
654
- pubkey: $src_node_id,
655
- lowest_fee_to_peer_through_node: total_fee_msat,
656
- lowest_fee_to_node: $next_hops_fee_msat as u64 + hop_use_fee_msat,
657
- value_contribution_msat: value_contribution_msat,
658
- path_htlc_minimum_msat,
659
- } ;
660
669
661
- // Update the way of reaching $src_node_id with the given $chan_id (from $dest_node_id),
662
- // if this way is cheaper than the already known
663
- // (considering the cost to "reach" this channel from the route destination,
664
- // the cost of using this channel,
665
- // and the cost of routing to the source node of this channel).
666
- // Also, consider that htlc_minimum_msat_difference, because we might end up
667
- // paying it. Consider the following exploit:
668
- // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
669
- // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
670
- // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
671
- // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
672
- // to this channel.
673
- // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
674
- // but it may require additional tracking - we don't want to double-count
675
- // the fees included in $incl_fee_next_hops_htlc_minimum_msat, but also
676
- // can't use something that may decrease on future hops.
677
- let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat) ;
678
- let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat) ;
679
-
680
- if new_cost < old_cost {
681
- targets. push( new_graph_node) ;
682
- old_entry. next_hops_fee_msat = $next_hops_fee_msat;
683
- old_entry. hop_use_fee_msat = hop_use_fee_msat;
684
- old_entry. total_fee_msat = total_fee_msat;
685
- old_entry. route_hop = RouteHop {
686
- pubkey: $dest_node_id. clone( ) ,
687
- node_features: NodeFeatures :: empty( ) ,
688
- short_channel_id: $chan_id. clone( ) ,
689
- channel_features: $chan_features. clone( ) ,
690
- fee_msat: 0 , // This value will be later filled with hop_use_fee_msat of the following channel
691
- cltv_expiry_delta: $directional_info. cltv_expiry_delta as u32 ,
670
+ let new_graph_node = RouteGraphNode {
671
+ pubkey: $src_node_id,
672
+ lowest_fee_to_peer_through_node: total_fee_msat,
673
+ lowest_fee_to_node: $next_hops_fee_msat as u64 + hop_use_fee_msat,
674
+ value_contribution_msat: value_contribution_msat,
675
+ path_htlc_minimum_msat,
692
676
} ;
693
- old_entry. channel_fees = $directional_info. fees;
694
- old_entry. htlc_minimum_msat = $directional_info. htlc_minimum_msat;
695
- old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
677
+
678
+ // Update the way of reaching $src_node_id with the given $chan_id (from $dest_node_id),
679
+ // if this way is cheaper than the already known
680
+ // (considering the cost to "reach" this channel from the route destination,
681
+ // the cost of using this channel,
682
+ // and the cost of routing to the source node of this channel).
683
+ // Also, consider that htlc_minimum_msat_difference, because we might end up
684
+ // paying it. Consider the following exploit:
685
+ // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
686
+ // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
687
+ // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
688
+ // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
689
+ // to this channel.
690
+ // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
691
+ // but it may require additional tracking - we don't want to double-count
692
+ // the fees included in $incl_fee_next_hops_htlc_minimum_msat, but also
693
+ // can't use something that may decrease on future hops.
694
+ let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat) ;
695
+ let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat) ;
696
+
697
+ if new_cost < old_cost {
698
+ targets. push( new_graph_node) ;
699
+ old_entry. next_hops_fee_msat = $next_hops_fee_msat;
700
+ old_entry. hop_use_fee_msat = hop_use_fee_msat;
701
+ old_entry. total_fee_msat = total_fee_msat;
702
+ old_entry. route_hop = RouteHop {
703
+ pubkey: $dest_node_id. clone( ) ,
704
+ node_features: NodeFeatures :: empty( ) ,
705
+ short_channel_id: $chan_id. clone( ) ,
706
+ channel_features: $chan_features. clone( ) ,
707
+ fee_msat: 0 , // This value will be later filled with hop_use_fee_msat of the following channel
708
+ cltv_expiry_delta: $directional_info. cltv_expiry_delta as u32 ,
709
+ } ;
710
+ old_entry. channel_fees = $directional_info. fees;
711
+ old_entry. htlc_minimum_msat = $directional_info. htlc_minimum_msat;
712
+ old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
713
+ }
696
714
}
697
715
}
698
716
}
@@ -707,7 +725,19 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
707
725
// This data can later be helpful to optimize routing (pay lower fees).
708
726
macro_rules! add_entries_to_cheapest_to_target_node {
709
727
( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr, $incl_fee_next_hops_htlc_minimum_msat: expr ) => {
710
- if first_hops. is_some( ) {
728
+ let skip_node = if let Some ( elem) = dist. get_mut( $node_id) {
729
+ let v = elem. was_processed;
730
+ elem. was_processed = true ;
731
+ v
732
+ } else {
733
+ // Entries are added to dist in add_entry!() when there is a channel from a node.
734
+ // Because there are no channels from payee, it will not have a dist entry at this point.
735
+ // If we're processing any other node, it is always be the result of a channel from it.
736
+ assert_eq!( $node_id, payee) ;
737
+ false
738
+ } ;
739
+
740
+ if !skip_node && first_hops. is_some( ) {
711
741
if let Some ( & ( ref first_hop, ref features, ref outbound_capacity_msat) ) = first_hop_targets. get( & $node_id) {
712
742
add_entry!( first_hop, * our_node_id, $node_id, dummy_directional_info, Some ( outbound_capacity_msat / 1000 ) , features. to_context( ) , $fee_to_target_msat, $next_hops_value_contribution, $incl_fee_next_hops_htlc_minimum_msat) ;
713
743
}
@@ -720,7 +750,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
720
750
features = NodeFeatures :: empty( ) ;
721
751
}
722
752
723
- if !features. requires_unknown_bits( ) {
753
+ if !skip_node && ! features. requires_unknown_bits( ) {
724
754
for chan_id in $node. channels. iter( ) {
725
755
let chan = network. get_channels( ) . get( chan_id) . unwrap( ) ;
726
756
if !chan. features. requires_unknown_bits( ) {
@@ -940,6 +970,11 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
940
970
break ' path_construction;
941
971
}
942
972
973
+ // If we found a path back to the payee, we shouldn't try to process it again. This is
974
+ // the equivalent of the `elem.was_processed` check in
975
+ // add_entries_to_cheapest_to_target_node!() (see comment there for more info).
976
+ if pubkey == * payee { continue ' path_construction; }
977
+
943
978
// Otherwise, since the current target node is not us,
944
979
// keep "unrolling" the payment graph from payee to payer by
945
980
// finding a way to reach the current target from the payer side.
0 commit comments