@@ -1829,89 +1829,96 @@ where L::Target: Logger {
18291829 total_fee_msat = total_fee_msat. saturating_add( hop_use_fee_msat) ;
18301830 }
18311831
1832- let channel_usage = ChannelUsage {
1833- amount_msat: amount_to_transfer_over_msat,
1834- inflight_htlc_msat: used_liquidity_msat,
1835- effective_capacity,
1836- } ;
1837- let channel_penalty_msat = scid_opt. map_or( 0 ,
1838- |scid| scorer. channel_penalty_msat( scid, & $src_node_id, & $dest_node_id,
1839- channel_usage, score_params) ) ;
1840- let path_penalty_msat = $next_hops_path_penalty_msat
1841- . saturating_add( channel_penalty_msat) ;
1842- let new_graph_node = RouteGraphNode {
1843- node_id: $src_node_id,
1844- lowest_fee_to_node: total_fee_msat,
1845- total_cltv_delta: hop_total_cltv_delta,
1846- value_contribution_msat,
1847- path_htlc_minimum_msat,
1848- path_penalty_msat,
1849- path_length_to_node,
1850- } ;
1851-
1852- // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
1853- // if this way is cheaper than the already known
1854- // (considering the cost to "reach" this channel from the route destination,
1855- // the cost of using this channel,
1856- // and the cost of routing to the source node of this channel).
1857- // Also, consider that htlc_minimum_msat_difference, because we might end up
1858- // paying it. Consider the following exploit:
1859- // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
1860- // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
1861- // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
1862- // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
1863- // to this channel.
1864- // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
1865- // but it may require additional tracking - we don't want to double-count
1866- // the fees included in $next_hops_path_htlc_minimum_msat, but also
1867- // can't use something that may decrease on future hops.
1868- let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat)
1869- . saturating_add( old_entry. path_penalty_msat) ;
1870- let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat)
1871- . saturating_add( path_penalty_msat) ;
1872-
1873- if !old_entry. was_processed && new_cost < old_cost {
1874- targets. push( new_graph_node) ;
1875- old_entry. next_hops_fee_msat = $next_hops_fee_msat;
1876- old_entry. hop_use_fee_msat = hop_use_fee_msat;
1877- old_entry. total_fee_msat = total_fee_msat;
1878- old_entry. node_id = $dest_node_id. clone( ) ;
1879- old_entry. candidate = $candidate. clone( ) ;
1880- old_entry. fee_msat = 0 ; // This value will be later filled with hop_use_fee_msat of the following channel
1881- old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
1882- old_entry. path_penalty_msat = path_penalty_msat;
1883- #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1884- {
1885- old_entry. value_contribution_msat = value_contribution_msat;
1886- }
1887- did_add_update_path_to_src_node = Some ( value_contribution_msat) ;
1888- } else if old_entry. was_processed && new_cost < old_cost {
1889- #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1890- {
1891- // If we're skipping processing a node which was previously
1892- // processed even though we found another path to it with a
1893- // cheaper fee, check that it was because the second path we
1894- // found (which we are processing now) has a lower value
1895- // contribution due to an HTLC minimum limit.
1896- //
1897- // e.g. take a graph with two paths from node 1 to node 2, one
1898- // through channel A, and one through channel B. Channel A and
1899- // B are both in the to-process heap, with their scores set by
1900- // a higher htlc_minimum than fee.
1901- // Channel A is processed first, and the channels onwards from
1902- // node 1 are added to the to-process heap. Thereafter, we pop
1903- // Channel B off of the heap, note that it has a much more
1904- // restrictive htlc_maximum_msat, and recalculate the fees for
1905- // all of node 1's channels using the new, reduced, amount.
1906- //
1907- // This would be bogus - we'd be selecting a higher-fee path
1908- // with a lower htlc_maximum_msat instead of the one we'd
1909- // already decided to use.
1910- debug_assert!( path_htlc_minimum_msat < old_entry. path_htlc_minimum_msat) ;
1911- debug_assert!(
1912- value_contribution_msat + path_penalty_msat <
1913- old_entry. value_contribution_msat + old_entry. path_penalty_msat
1914- ) ;
1832+ // Ignore hops if they by themselves would already put us over `max_total_routing_fee_msat`
1833+ let max_total_routing_fee_msat = payment_params. max_total_routing_fee_msat. unwrap_or( u64 :: max_value( ) ) ;
1834+ if total_fee_msat > max_total_routing_fee_msat {
1835+ log_trace!( logger, "Ignoring candidate hop {} as the path's total fee {} would exceed the maximum total routing fee limit {}" ,
1836+ LoggedCandidateHop ( & $candidate) , total_fee_msat, max_total_routing_fee_msat) ;
1837+ } else {
1838+ let channel_usage = ChannelUsage {
1839+ amount_msat: amount_to_transfer_over_msat,
1840+ inflight_htlc_msat: used_liquidity_msat,
1841+ effective_capacity,
1842+ } ;
1843+ let channel_penalty_msat = scid_opt. map_or( 0 ,
1844+ |scid| scorer. channel_penalty_msat( scid, & $src_node_id, & $dest_node_id,
1845+ channel_usage, score_params) ) ;
1846+ let path_penalty_msat = $next_hops_path_penalty_msat
1847+ . saturating_add( channel_penalty_msat) ;
1848+ let new_graph_node = RouteGraphNode {
1849+ node_id: $src_node_id,
1850+ lowest_fee_to_node: total_fee_msat,
1851+ total_cltv_delta: hop_total_cltv_delta,
1852+ value_contribution_msat,
1853+ path_htlc_minimum_msat,
1854+ path_penalty_msat,
1855+ path_length_to_node,
1856+ } ;
1857+
1858+ // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
1859+ // if this way is cheaper than the already known
1860+ // (considering the cost to "reach" this channel from the route destination,
1861+ // the cost of using this channel,
1862+ // and the cost of routing to the source node of this channel).
1863+ // Also, consider that htlc_minimum_msat_difference, because we might end up
1864+ // paying it. Consider the following exploit:
1865+ // we use 2 paths to transfer 1.5 BTC. One of them is 0-fee normal 1 BTC path,
1866+ // and for the other one we picked a 1sat-fee path with htlc_minimum_msat of
1867+ // 1 BTC. Now, since the latter is more expensive, we gonna try to cut it
1868+ // by 0.5 BTC, but then match htlc_minimum_msat by paying a fee of 0.5 BTC
1869+ // to this channel.
1870+ // Ideally the scoring could be smarter (e.g. 0.5*htlc_minimum_msat here),
1871+ // but it may require additional tracking - we don't want to double-count
1872+ // the fees included in $next_hops_path_htlc_minimum_msat, but also
1873+ // can't use something that may decrease on future hops.
1874+ let old_cost = cmp:: max( old_entry. total_fee_msat, old_entry. path_htlc_minimum_msat)
1875+ . saturating_add( old_entry. path_penalty_msat) ;
1876+ let new_cost = cmp:: max( total_fee_msat, path_htlc_minimum_msat)
1877+ . saturating_add( path_penalty_msat) ;
1878+
1879+ if !old_entry. was_processed && new_cost < old_cost {
1880+ targets. push( new_graph_node) ;
1881+ old_entry. next_hops_fee_msat = $next_hops_fee_msat;
1882+ old_entry. hop_use_fee_msat = hop_use_fee_msat;
1883+ old_entry. total_fee_msat = total_fee_msat;
1884+ old_entry. node_id = $dest_node_id. clone( ) ;
1885+ old_entry. candidate = $candidate. clone( ) ;
1886+ old_entry. fee_msat = 0 ; // This value will be later filled with hop_use_fee_msat of the following channel
1887+ old_entry. path_htlc_minimum_msat = path_htlc_minimum_msat;
1888+ old_entry. path_penalty_msat = path_penalty_msat;
1889+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1890+ {
1891+ old_entry. value_contribution_msat = value_contribution_msat;
1892+ }
1893+ did_add_update_path_to_src_node = Some ( value_contribution_msat) ;
1894+ } else if old_entry. was_processed && new_cost < old_cost {
1895+ #[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1896+ {
1897+ // If we're skipping processing a node which was previously
1898+ // processed even though we found another path to it with a
1899+ // cheaper fee, check that it was because the second path we
1900+ // found (which we are processing now) has a lower value
1901+ // contribution due to an HTLC minimum limit.
1902+ //
1903+ // e.g. take a graph with two paths from node 1 to node 2, one
1904+ // through channel A, and one through channel B. Channel A and
1905+ // B are both in the to-process heap, with their scores set by
1906+ // a higher htlc_minimum than fee.
1907+ // Channel A is processed first, and the channels onwards from
1908+ // node 1 are added to the to-process heap. Thereafter, we pop
1909+ // Channel B off of the heap, note that it has a much more
1910+ // restrictive htlc_maximum_msat, and recalculate the fees for
1911+ // all of node 1's channels using the new, reduced, amount.
1912+ //
1913+ // This would be bogus - we'd be selecting a higher-fee path
1914+ // with a lower htlc_maximum_msat instead of the one we'd
1915+ // already decided to use.
1916+ debug_assert!( path_htlc_minimum_msat < old_entry. path_htlc_minimum_msat) ;
1917+ debug_assert!(
1918+ value_contribution_msat + path_penalty_msat <
1919+ old_entry. value_contribution_msat + old_entry. path_penalty_msat
1920+ ) ;
1921+ }
19151922 }
19161923 }
19171924 }
@@ -2499,6 +2506,14 @@ where L::Target: Logger {
24992506 // Make sure we would never create a route with more paths than we allow.
25002507 debug_assert ! ( paths. len( ) <= payment_params. max_path_count. into( ) ) ;
25012508
2509+ // Make sure we would never create a route whose total fees exceed max_total_routing_fee_msat.
2510+ if let Some ( max_total_routing_fee_msat) = payment_params. max_total_routing_fee_msat {
2511+ if paths. iter ( ) . map ( |p| p. fee_msat ( ) ) . sum :: < u64 > ( ) > max_total_routing_fee_msat {
2512+ return Err ( LightningError { err : format ! ( "Failed to find route that adheres to the maximum total fee limit of {}msat" ,
2513+ max_total_routing_fee_msat) , action : ErrorAction :: IgnoreError } ) ;
2514+ }
2515+ }
2516+
25022517 if let Some ( node_features) = payment_params. payee . node_features ( ) {
25032518 for path in paths. iter_mut ( ) {
25042519 path. hops . last_mut ( ) . unwrap ( ) . node_features = node_features. clone ( ) ;
0 commit comments