@@ -1671,6 +1671,7 @@ where L::Target: Logger {
1671
1671
let mut num_ignored_path_length_limit = 0 ;
1672
1672
let mut num_ignored_cltv_delta_limit = 0 ;
1673
1673
let mut num_ignored_previously_failed = 0 ;
1674
+ let mut num_ignored_total_fee_limit = 0 ;
1674
1675
1675
1676
macro_rules! add_entry {
1676
1677
// Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop.
@@ -1832,89 +1833,98 @@ where L::Target: Logger {
1832
1833
total_fee_msat = total_fee_msat. saturating_add( hop_use_fee_msat) ;
1833
1834
}
1834
1835
1835
- let channel_usage = ChannelUsage {
1836
- amount_msat: amount_to_transfer_over_msat,
1837
- inflight_htlc_msat: used_liquidity_msat,
1838
- effective_capacity,
1839
- } ;
1840
- let channel_penalty_msat = scid_opt. map_or( 0 ,
1841
- |scid| scorer. channel_penalty_msat( scid, & $src_node_id, & $dest_node_id,
1842
- channel_usage, score_params) ) ;
1843
- let path_penalty_msat = $next_hops_path_penalty_msat
1844
- . saturating_add( channel_penalty_msat) ;
1845
- let new_graph_node = RouteGraphNode {
1846
- node_id: $src_node_id,
1847
- lowest_fee_to_node: total_fee_msat,
1848
- total_cltv_delta: hop_total_cltv_delta,
1849
- value_contribution_msat,
1850
- path_htlc_minimum_msat,
1851
- path_penalty_msat,
1852
- path_length_to_node,
1853
- } ;
1854
-
1855
- // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
1856
- // if this way is cheaper than the already known
1857
- // (considering the cost to "reach" this channel from the route destination,
1858
- // the cost of using this channel,
1859
- // and the cost of routing to the source node of this channel).
1860
- // Also, consider that htlc_minimum_msat_difference, because we might end up
1861
- // paying it. Consider the following exploit:
1862
- // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
1863
- // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
1864
- // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
1865
- // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
1866
- // to this channel.
1867
- // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
1868
- // but it may require additional tracking - we don't want to double-count
1869
- // the fees included in $next_hops_path_htlc_minimum_msat, but also
1870
- // can't use something that may decrease on future hops.
1871
- let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat)
1872
- . saturating_add( old_entry. path_penalty_msat) ;
1873
- let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat)
1874
- . saturating_add( path_penalty_msat) ;
1875
-
1876
- if !old_entry. was_processed && new_cost < old_cost {
1877
- targets. push( new_graph_node) ;
1878
- old_entry. next_hops_fee_msat = $next_hops_fee_msat;
1879
- old_entry. hop_use_fee_msat = hop_use_fee_msat;
1880
- old_entry. total_fee_msat = total_fee_msat;
1881
- old_entry. node_id = $dest_node_id. clone( ) ;
1882
- old_entry. candidate = $candidate. clone( ) ;
1883
- old_entry. fee_msat = 0 ; // This value will be later filled with hop_use_fee_msat of the following channel
1884
- old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
1885
- old_entry. path_penalty_msat = path_penalty_msat;
1886
- #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1887
- {
1888
- old_entry. value_contribution_msat = value_contribution_msat;
1836
+ // Ignore hops if augmenting the current path to them would put us over `max_total_routing_fee_msat`
1837
+ let max_total_routing_fee_msat = payment_params. max_total_routing_fee_msat. unwrap_or( u64 :: max_value( ) ) ;
1838
+ if total_fee_msat > max_total_routing_fee_msat {
1839
+ if should_log_candidate {
1840
+ log_trace!( logger, "Ignoring {} due to exceeding max total routing fee limit." , LoggedCandidateHop ( & $candidate) ) ;
1889
1841
}
1890
- did_add_update_path_to_src_node = Some ( value_contribution_msat) ;
1891
- } else if old_entry. was_processed && new_cost < old_cost {
1892
- #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1893
- {
1894
- // If we're skipping processing a node which was previously
1895
- // processed even though we found another path to it with a
1896
- // cheaper fee, check that it was because the second path we
1897
- // found (which we are processing now) has a lower value
1898
- // contribution due to an HTLC minimum limit.
1899
- //
1900
- // e.g. take a graph with two paths from node 1 to node 2, one
1901
- // through channel A, and one through channel B. Channel A and
1902
- // B are both in the to-process heap, with their scores set by
1903
- // a higher htlc_minimum than fee.
1904
- // Channel A is processed first, and the channels onwards from
1905
- // node 1 are added to the to-process heap. Thereafter, we pop
1906
- // Channel B off of the heap, note that it has a much more
1907
- // restrictive htlc_maximum_msat, and recalculate the fees for
1908
- // all of node 1's channels using the new, reduced, amount.
1909
- //
1910
- // This would be bogus - we'd be selecting a higher-fee path
1911
- // with a lower htlc_maximum_msat instead of the one we'd
1912
- // already decided to use.
1913
- debug_assert!( path_htlc_minimum_msat < old_entry. path_htlc_minimum_msat) ;
1914
- debug_assert!(
1915
- value_contribution_msat + path_penalty_msat <
1916
- old_entry. value_contribution_msat + old_entry. path_penalty_msat
1917
- ) ;
1842
+ num_ignored_total_fee_limit += 1 ;
1843
+ } else {
1844
+ let channel_usage = ChannelUsage {
1845
+ amount_msat: amount_to_transfer_over_msat,
1846
+ inflight_htlc_msat: used_liquidity_msat,
1847
+ effective_capacity,
1848
+ } ;
1849
+ let channel_penalty_msat = scid_opt. map_or( 0 ,
1850
+ |scid| scorer. channel_penalty_msat( scid, & $src_node_id, & $dest_node_id,
1851
+ channel_usage, score_params) ) ;
1852
+ let path_penalty_msat = $next_hops_path_penalty_msat
1853
+ . saturating_add( channel_penalty_msat) ;
1854
+ let new_graph_node = RouteGraphNode {
1855
+ node_id: $src_node_id,
1856
+ lowest_fee_to_node: total_fee_msat,
1857
+ total_cltv_delta: hop_total_cltv_delta,
1858
+ value_contribution_msat,
1859
+ path_htlc_minimum_msat,
1860
+ path_penalty_msat,
1861
+ path_length_to_node,
1862
+ } ;
1863
+
1864
+ // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
1865
+ // if this way is cheaper than the already known
1866
+ // (considering the cost to "reach" this channel from the route destination,
1867
+ // the cost of using this channel,
1868
+ // and the cost of routing to the source node of this channel).
1869
+ // Also, consider that htlc_minimum_msat_difference, because we might end up
1870
+ // paying it. Consider the following exploit:
1871
+ // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
1872
+ // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
1873
+ // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
1874
+ // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
1875
+ // to this channel.
1876
+ // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
1877
+ // but it may require additional tracking - we don't want to double-count
1878
+ // the fees included in $next_hops_path_htlc_minimum_msat, but also
1879
+ // can't use something that may decrease on future hops.
1880
+ let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat)
1881
+ . saturating_add( old_entry. path_penalty_msat) ;
1882
+ let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat)
1883
+ . saturating_add( path_penalty_msat) ;
1884
+
1885
+ if !old_entry. was_processed && new_cost < old_cost {
1886
+ targets. push( new_graph_node) ;
1887
+ old_entry. next_hops_fee_msat = $next_hops_fee_msat;
1888
+ old_entry. hop_use_fee_msat = hop_use_fee_msat;
1889
+ old_entry. total_fee_msat = total_fee_msat;
1890
+ old_entry. node_id = $dest_node_id. clone( ) ;
1891
+ old_entry. candidate = $candidate. clone( ) ;
1892
+ old_entry. fee_msat = 0 ; // This value will be later filled with hop_use_fee_msat of the following channel
1893
+ old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
1894
+ old_entry. path_penalty_msat = path_penalty_msat;
1895
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1896
+ {
1897
+ old_entry. value_contribution_msat = value_contribution_msat;
1898
+ }
1899
+ did_add_update_path_to_src_node = Some ( value_contribution_msat) ;
1900
+ } else if old_entry. was_processed && new_cost < old_cost {
1901
+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1902
+ {
1903
+ // If we're skipping processing a node which was previously
1904
+ // processed even though we found another path to it with a
1905
+ // cheaper fee, check that it was because the second path we
1906
+ // found (which we are processing now) has a lower value
1907
+ // contribution due to an HTLC minimum limit.
1908
+ //
1909
+ // e.g. take a graph with two paths from node 1 to node 2, one
1910
+ // through channel A, and one through channel B. Channel A and
1911
+ // B are both in the to-process heap, with their scores set by
1912
+ // a higher htlc_minimum than fee.
1913
+ // Channel A is processed first, and the channels onwards from
1914
+ // node 1 are added to the to-process heap. Thereafter, we pop
1915
+ // Channel B off of the heap, note that it has a much more
1916
+ // restrictive htlc_maximum_msat, and recalculate the fees for
1917
+ // all of node 1's channels using the new, reduced, amount.
1918
+ //
1919
+ // This would be bogus - we'd be selecting a higher-fee path
1920
+ // with a lower htlc_maximum_msat instead of the one we'd
1921
+ // already decided to use.
1922
+ debug_assert!( path_htlc_minimum_msat < old_entry. path_htlc_minimum_msat) ;
1923
+ debug_assert!(
1924
+ value_contribution_msat + path_penalty_msat <
1925
+ old_entry. value_contribution_msat + old_entry. path_penalty_msat
1926
+ ) ;
1927
+ }
1918
1928
}
1919
1929
}
1920
1930
}
@@ -2379,9 +2389,9 @@ where L::Target: Logger {
2379
2389
}
2380
2390
2381
2391
let num_ignored_total = num_ignored_value_contribution + num_ignored_path_length_limit +
2382
- num_ignored_cltv_delta_limit + num_ignored_previously_failed;
2392
+ num_ignored_cltv_delta_limit + num_ignored_previously_failed + num_ignored_total_fee_limit ;
2383
2393
if num_ignored_total > 0 {
2384
- log_trace ! ( logger, "Ignored {} candidate hops due to insufficient value contrib., {} due to path length limit, {} due to CLTV delta limit, {} due to previous payment failure. Total: {} ignored candidates." , num_ignored_value_contribution, num_ignored_path_length_limit, num_ignored_cltv_delta_limit, num_ignored_previously_failed, num_ignored_total) ;
2394
+ log_trace ! ( logger, "Ignored {} candidate hops due to insufficient value contrib., {} due to path length limit, {} due to CLTV delta limit, {} due to previous payment failure, {} due to maximum total fee limit . Total: {} ignored candidates." , num_ignored_value_contribution, num_ignored_path_length_limit, num_ignored_cltv_delta_limit, num_ignored_previously_failed, num_ignored_total_fee_limit , num_ignored_total) ;
2385
2395
}
2386
2396
2387
2397
// Step (5).
@@ -2502,6 +2512,14 @@ where L::Target: Logger {
2502
2512
// Make sure we would never create a route with more paths than we allow.
2503
2513
debug_assert ! ( paths. len( ) <= payment_params. max_path_count. into( ) ) ;
2504
2514
2515
+ // Make sure we would never create a route whose total fees exceed max_total_routing_fee_msat.
2516
+ if let Some ( max_total_routing_fee_msat) = payment_params. max_total_routing_fee_msat {
2517
+ if paths. iter ( ) . map ( |p| p. fee_msat ( ) ) . sum :: < u64 > ( ) > max_total_routing_fee_msat {
2518
+ return Err ( LightningError { err : format ! ( "Failed to find route that adheres to the maximum total fee limit of {}msat" ,
2519
+ max_total_routing_fee_msat) , action : ErrorAction :: IgnoreError } ) ;
2520
+ }
2521
+ }
2522
+
2505
2523
if let Some ( node_features) = payment_params. payee . node_features ( ) {
2506
2524
for path in paths. iter_mut ( ) {
2507
2525
path. hops . last_mut ( ) . unwrap ( ) . node_features = node_features. clone ( ) ;
0 commit comments