@@ -1693,6 +1693,7 @@ where L::Target: Logger {
16931693 let mut num_ignored_path_length_limit = 0 ;
16941694 let mut num_ignored_cltv_delta_limit = 0 ;
16951695 let mut num_ignored_previously_failed = 0 ;
1696+ let mut num_ignored_total_fee_limit = 0 ;
16961697
16971698 macro_rules! add_entry {
16981699 // Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop.
@@ -1854,89 +1855,98 @@ where L::Target: Logger {
18541855 total_fee_msat = total_fee_msat. saturating_add( hop_use_fee_msat) ;
18551856 }
18561857
1857- let channel_usage = ChannelUsage {
1858- amount_msat: amount_to_transfer_over_msat,
1859- inflight_htlc_msat: used_liquidity_msat,
1860- effective_capacity,
1861- } ;
1862- let channel_penalty_msat = scid_opt. map_or( 0 ,
1863- |scid| scorer. channel_penalty_msat( scid, & $src_node_id, & $dest_node_id,
1864- channel_usage, score_params) ) ;
1865- let path_penalty_msat = $next_hops_path_penalty_msat
1866- . saturating_add( channel_penalty_msat) ;
1867- let new_graph_node = RouteGraphNode {
1868- node_id: $src_node_id,
1869- lowest_fee_to_node: total_fee_msat,
1870- total_cltv_delta: hop_total_cltv_delta,
1871- value_contribution_msat,
1872- path_htlc_minimum_msat,
1873- path_penalty_msat,
1874- path_length_to_node,
1875- } ;
1876-
1877- // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
1878- // if this way is cheaper than the already known
1879- // (considering the cost to "reach" this channel from the route destination,
1880- // the cost of using this channel,
1881- // and the cost of routing to the source node of this channel).
1882- // Also, consider that htlc_minimum_msat_difference, because we might end up
1883- // paying it. Consider the following exploit:
1884- // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
1885- // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
1886- // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
1887- // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
1888- // to this channel.
1889- // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
1890- // but it may require additional tracking - we don't want to double-count
1891- // the fees included in $next_hops_path_htlc_minimum_msat, but also
1892- // can't use something that may decrease on future hops.
1893- let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat)
1894- . saturating_add( old_entry. path_penalty_msat) ;
1895- let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat)
1896- . saturating_add( path_penalty_msat) ;
1897-
1898- if !old_entry. was_processed && new_cost < old_cost {
1899- targets. push( new_graph_node) ;
1900- old_entry. next_hops_fee_msat = $next_hops_fee_msat;
1901- old_entry. hop_use_fee_msat = hop_use_fee_msat;
1902- old_entry. total_fee_msat = total_fee_msat;
1903- old_entry. node_id = $dest_node_id. clone( ) ;
1904- old_entry. candidate = $candidate. clone( ) ;
1905- old_entry. fee_msat = 0 ; // This value will be later filled with hop_use_fee_msat of the following channel
1906- old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
1907- old_entry. path_penalty_msat = path_penalty_msat;
1908- #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1909- {
1910- old_entry. value_contribution_msat = value_contribution_msat;
1858+ // Ignore hops if augmenting the current path to them would put us over `max_total_routing_fee_msat`
1859+ let max_total_routing_fee_msat = route_params. max_total_routing_fee_msat. unwrap_or( u64 :: max_value( ) ) ;
1860+ if total_fee_msat > max_total_routing_fee_msat {
1861+ if should_log_candidate {
1862+ log_trace!( logger, "Ignoring {} due to exceeding max total routing fee limit." , LoggedCandidateHop ( & $candidate) ) ;
19111863 }
1912- did_add_update_path_to_src_node = Some ( value_contribution_msat) ;
1913- } else if old_entry. was_processed && new_cost < old_cost {
1914- #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1915- {
1916- // If we're skipping processing a node which was previously
1917- // processed even though we found another path to it with a
1918- // cheaper fee, check that it was because the second path we
1919- // found (which we are processing now) has a lower value
1920- // contribution due to an HTLC minimum limit.
1921- //
1922- // e.g. take a graph with two paths from node 1 to node 2, one
1923- // through channel A, and one through channel B. Channel A and
1924- // B are both in the to-process heap, with their scores set by
1925- // a higher htlc_minimum than fee.
1926- // Channel A is processed first, and the channels onwards from
1927- // node 1 are added to the to-process heap. Thereafter, we pop
1928- // Channel B off of the heap, note that it has a much more
1929- // restrictive htlc_maximum_msat, and recalculate the fees for
1930- // all of node 1's channels using the new, reduced, amount.
1931- //
1932- // This would be bogus - we'd be selecting a higher-fee path
1933- // with a lower htlc_maximum_msat instead of the one we'd
1934- // already decided to use.
1935- debug_assert!( path_htlc_minimum_msat < old_entry. path_htlc_minimum_msat) ;
1936- debug_assert!(
1937- value_contribution_msat + path_penalty_msat <
1938- old_entry. value_contribution_msat + old_entry. path_penalty_msat
1939- ) ;
1864+ num_ignored_total_fee_limit += 1 ;
1865+ } else {
1866+ let channel_usage = ChannelUsage {
1867+ amount_msat: amount_to_transfer_over_msat,
1868+ inflight_htlc_msat: used_liquidity_msat,
1869+ effective_capacity,
1870+ } ;
1871+ let channel_penalty_msat = scid_opt. map_or( 0 ,
1872+ |scid| scorer. channel_penalty_msat( scid, & $src_node_id, & $dest_node_id,
1873+ channel_usage, score_params) ) ;
1874+ let path_penalty_msat = $next_hops_path_penalty_msat
1875+ . saturating_add( channel_penalty_msat) ;
1876+ let new_graph_node = RouteGraphNode {
1877+ node_id: $src_node_id,
1878+ lowest_fee_to_node: total_fee_msat,
1879+ total_cltv_delta: hop_total_cltv_delta,
1880+ value_contribution_msat,
1881+ path_htlc_minimum_msat,
1882+ path_penalty_msat,
1883+ path_length_to_node,
1884+ } ;
1885+
1886+ // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
1887+ // if this way is cheaper than the already known
1888+ // (considering the cost to "reach" this channel from the route destination,
1889+ // the cost of using this channel,
1890+ // and the cost of routing to the source node of this channel).
1891+ // Also, consider that htlc_minimum_msat_difference, because we might end up
1892+ // paying it. Consider the following exploit:
1893+ // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
1894+ // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
1895+ // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
1896+ // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
1897+ // to this channel.
1898+ // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
1899+ // but it may require additional tracking - we don't want to double-count
1900+ // the fees included in $next_hops_path_htlc_minimum_msat, but also
1901+ // can't use something that may decrease on future hops.
1902+ let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat)
1903+ . saturating_add( old_entry. path_penalty_msat) ;
1904+ let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat)
1905+ . saturating_add( path_penalty_msat) ;
1906+
1907+ if !old_entry. was_processed && new_cost < old_cost {
1908+ targets. push( new_graph_node) ;
1909+ old_entry. next_hops_fee_msat = $next_hops_fee_msat;
1910+ old_entry. hop_use_fee_msat = hop_use_fee_msat;
1911+ old_entry. total_fee_msat = total_fee_msat;
1912+ old_entry. node_id = $dest_node_id. clone( ) ;
1913+ old_entry. candidate = $candidate. clone( ) ;
1914+ old_entry. fee_msat = 0 ; // This value will be later filled with hop_use_fee_msat of the following channel
1915+ old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
1916+ old_entry. path_penalty_msat = path_penalty_msat;
1917+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1918+ {
1919+ old_entry. value_contribution_msat = value_contribution_msat;
1920+ }
1921+ did_add_update_path_to_src_node = Some ( value_contribution_msat) ;
1922+ } else if old_entry. was_processed && new_cost < old_cost {
1923+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1924+ {
1925+ // If we're skipping processing a node which was previously
1926+ // processed even though we found another path to it with a
1927+ // cheaper fee, check that it was because the second path we
1928+ // found (which we are processing now) has a lower value
1929+ // contribution due to an HTLC minimum limit.
1930+ //
1931+ // e.g. take a graph with two paths from node 1 to node 2, one
1932+ // through channel A, and one through channel B. Channel A and
1933+ // B are both in the to-process heap, with their scores set by
1934+ // a higher htlc_minimum than fee.
1935+ // Channel A is processed first, and the channels onwards from
1936+ // node 1 are added to the to-process heap. Thereafter, we pop
1937+ // Channel B off of the heap, note that it has a much more
1938+ // restrictive htlc_maximum_msat, and recalculate the fees for
1939+ // all of node 1's channels using the new, reduced, amount.
1940+ //
1941+ // This would be bogus - we'd be selecting a higher-fee path
1942+ // with a lower htlc_maximum_msat instead of the one we'd
1943+ // already decided to use.
1944+ debug_assert!( path_htlc_minimum_msat < old_entry. path_htlc_minimum_msat) ;
1945+ debug_assert!(
1946+ value_contribution_msat + path_penalty_msat <
1947+ old_entry. value_contribution_msat + old_entry. path_penalty_msat
1948+ ) ;
1949+ }
19401950 }
19411951 }
19421952 }
@@ -2407,9 +2417,9 @@ where L::Target: Logger {
24072417 }
24082418
24092419 let num_ignored_total = num_ignored_value_contribution + num_ignored_path_length_limit +
2410- num_ignored_cltv_delta_limit + num_ignored_previously_failed;
2420+ num_ignored_cltv_delta_limit + num_ignored_previously_failed + num_ignored_total_fee_limit ;
24112421 if num_ignored_total > 0 {
2412- log_trace ! ( logger, "Ignored {} candidate hops due to insufficient value contribution, {} 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) ;
2422+ log_trace ! ( logger, "Ignored {} candidate hops due to insufficient value contribution, {} 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) ;
24132423 }
24142424
24152425 // Step (5).
@@ -2551,6 +2561,14 @@ where L::Target: Logger {
25512561 // Make sure we would never create a route with more paths than we allow.
25522562 debug_assert ! ( paths. len( ) <= payment_params. max_path_count. into( ) ) ;
25532563
2564+ // Make sure we would never create a route whose total fees exceed max_total_routing_fee_msat.
2565+ if let Some ( max_total_routing_fee_msat) = route_params. max_total_routing_fee_msat {
2566+ if paths. iter ( ) . map ( |p| p. fee_msat ( ) ) . sum :: < u64 > ( ) > max_total_routing_fee_msat {
2567+ return Err ( LightningError { err : format ! ( "Failed to find route that adheres to the maximum total fee limit of {}msat" ,
2568+ max_total_routing_fee_msat) , action : ErrorAction :: IgnoreError } ) ;
2569+ }
2570+ }
2571+
25542572 if let Some ( node_features) = payment_params. payee . node_features ( ) {
25552573 for path in paths. iter_mut ( ) {
25562574 path. hops . last_mut ( ) . unwrap ( ) . node_features = node_features. clone ( ) ;
0 commit comments