@@ -951,7 +951,10 @@ impl<'a> CandidateRouteHop<'a> {
951
951
liquidity_msat : details. next_outbound_htlc_limit_msat ,
952
952
} ,
953
953
CandidateRouteHop :: PublicHop { info, .. } => info. effective_capacity ( ) ,
954
- CandidateRouteHop :: PrivateHop { .. } => EffectiveCapacity :: Infinite ,
954
+ CandidateRouteHop :: PrivateHop { hint : RouteHintHop { htlc_maximum_msat : Some ( max) , .. } } =>
955
+ EffectiveCapacity :: HintMaxHTLC { amount_msat : * max } ,
956
+ CandidateRouteHop :: PrivateHop { hint : RouteHintHop { htlc_maximum_msat : None , .. } } =>
957
+ EffectiveCapacity :: Infinite ,
955
958
}
956
959
}
957
960
}
@@ -963,8 +966,11 @@ fn max_htlc_from_capacity(capacity: EffectiveCapacity, max_channel_saturation_po
963
966
EffectiveCapacity :: ExactLiquidity { liquidity_msat } => liquidity_msat,
964
967
EffectiveCapacity :: Infinite => u64:: max_value ( ) ,
965
968
EffectiveCapacity :: Unknown => EffectiveCapacity :: Unknown . as_msat ( ) ,
966
- EffectiveCapacity :: MaximumHTLC { amount_msat } =>
969
+ EffectiveCapacity :: AdvertisedMaxHTLC { amount_msat } =>
967
970
amount_msat. checked_shr ( saturation_shift) . unwrap_or ( 0 ) ,
971
+ // Treat htlc_maximum_msat from a route hint as an exact liquidity amount, since the invoice is
972
+ // expected to have been generated from up-to-date capacity information.
973
+ EffectiveCapacity :: HintMaxHTLC { amount_msat } => amount_msat,
968
974
EffectiveCapacity :: Total { capacity_msat, htlc_maximum_msat } =>
969
975
cmp:: min ( capacity_msat. checked_shr ( saturation_shift) . unwrap_or ( 0 ) , htlc_maximum_msat) ,
970
976
}
@@ -1192,6 +1198,41 @@ impl fmt::Display for LoggedPayeePubkey {
1192
1198
}
1193
1199
}
1194
1200
1201
+ #[ inline]
1202
+ fn sort_first_hop_channels (
1203
+ channels : & mut Vec < & ChannelDetails > , used_channel_liquidities : & HashMap < ( u64 , bool ) , u64 > ,
1204
+ recommended_value_msat : u64 , our_node_pubkey : & PublicKey
1205
+ ) {
1206
+ // Sort the first_hops channels to the same node(s) in priority order of which channel we'd
1207
+ // most like to use.
1208
+ //
1209
+ // First, if channels are below `recommended_value_msat`, sort them in descending order,
1210
+ // preferring larger channels to avoid splitting the payment into more MPP parts than is
1211
+ // required.
1212
+ //
1213
+ // Second, because simply always sorting in descending order would always use our largest
1214
+ // available outbound capacity, needlessly fragmenting our available channel capacities,
1215
+ // sort channels above `recommended_value_msat` in ascending order, preferring channels
1216
+ // which have enough, but not too much, capacity for the payment.
1217
+ //
1218
+ // Available outbound balances factor in liquidity already reserved for previously found paths.
1219
+ channels. sort_unstable_by ( |chan_a, chan_b| {
1220
+ let chan_a_outbound_limit_msat = chan_a. next_outbound_htlc_limit_msat
1221
+ . saturating_sub ( * used_channel_liquidities. get ( & ( chan_a. get_outbound_payment_scid ( ) . unwrap ( ) ,
1222
+ our_node_pubkey < & chan_a. counterparty . node_id ) ) . unwrap_or ( & 0 ) ) ;
1223
+ let chan_b_outbound_limit_msat = chan_b. next_outbound_htlc_limit_msat
1224
+ . saturating_sub ( * used_channel_liquidities. get ( & ( chan_b. get_outbound_payment_scid ( ) . unwrap ( ) ,
1225
+ our_node_pubkey < & chan_b. counterparty . node_id ) ) . unwrap_or ( & 0 ) ) ;
1226
+ if chan_b_outbound_limit_msat < recommended_value_msat || chan_a_outbound_limit_msat < recommended_value_msat {
1227
+ // Sort in descending order
1228
+ chan_b_outbound_limit_msat. cmp ( & chan_a_outbound_limit_msat)
1229
+ } else {
1230
+ // Sort in ascending order
1231
+ chan_a_outbound_limit_msat. cmp ( & chan_b_outbound_limit_msat)
1232
+ }
1233
+ } ) ;
1234
+ }
1235
+
1195
1236
/// Finds a route from us (payer) to the given target node (payee).
1196
1237
///
1197
1238
/// If the payee provided features in their invoice, they should be provided via `params.payee`.
@@ -1437,26 +1478,8 @@ where L::Target: Logger {
1437
1478
let mut already_collected_value_msat = 0 ;
1438
1479
1439
1480
for ( _, channels) in first_hop_targets. iter_mut ( ) {
1440
- // Sort the first_hops channels to the same node(s) in priority order of which channel we'd
1441
- // most like to use.
1442
- //
1443
- // First, if channels are below `recommended_value_msat`, sort them in descending order,
1444
- // preferring larger channels to avoid splitting the payment into more MPP parts than is
1445
- // required.
1446
- //
1447
- // Second, because simply always sorting in descending order would always use our largest
1448
- // available outbound capacity, needlessly fragmenting our available channel capacities,
1449
- // sort channels above `recommended_value_msat` in ascending order, preferring channels
1450
- // which have enough, but not too much, capacity for the payment.
1451
- channels. sort_unstable_by ( |chan_a, chan_b| {
1452
- if chan_b. next_outbound_htlc_limit_msat < recommended_value_msat || chan_a. next_outbound_htlc_limit_msat < recommended_value_msat {
1453
- // Sort in descending order
1454
- chan_b. next_outbound_htlc_limit_msat . cmp ( & chan_a. next_outbound_htlc_limit_msat )
1455
- } else {
1456
- // Sort in ascending order
1457
- chan_a. next_outbound_htlc_limit_msat . cmp ( & chan_b. next_outbound_htlc_limit_msat )
1458
- }
1459
- } ) ;
1481
+ sort_first_hop_channels ( channels, & used_channel_liquidities, recommended_value_msat,
1482
+ our_node_pubkey) ;
1460
1483
}
1461
1484
1462
1485
log_trace ! ( logger, "Building path from {} to payer {} for value {} msat." ,
@@ -1470,8 +1493,9 @@ where L::Target: Logger {
1470
1493
( $candidate: expr, $src_node_id: expr, $dest_node_id: expr, $next_hops_fee_msat: expr,
1471
1494
$next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr,
1472
1495
$next_hops_path_penalty_msat: expr, $next_hops_cltv_delta: expr, $next_hops_path_length: expr ) => { {
1473
- // We "return" whether we updated the path at the end, via this:
1474
- let mut did_add_update_path_to_src_node = false ;
1496
+ // We "return" whether we updated the path at the end, and how much we can route via
1497
+ // this channel, via this:
1498
+ let mut did_add_update_path_to_src_node = None ;
1475
1499
// Channels to self should not be used. This is more of belt-and-suspenders, because in
1476
1500
// practice these cases should be caught earlier:
1477
1501
// - for regular channels at channel announcement (TODO)
@@ -1652,7 +1676,7 @@ where L::Target: Logger {
1652
1676
{
1653
1677
old_entry. value_contribution_msat = value_contribution_msat;
1654
1678
}
1655
- did_add_update_path_to_src_node = true ;
1679
+ did_add_update_path_to_src_node = Some ( value_contribution_msat ) ;
1656
1680
} else if old_entry. was_processed && new_cost < old_cost {
1657
1681
#[ cfg( all( not( ldk_bench) , any( test, fuzzing) ) ) ]
1658
1682
{
@@ -1773,7 +1797,7 @@ where L::Target: Logger {
1773
1797
for details in first_channels {
1774
1798
let candidate = CandidateRouteHop :: FirstHop { details } ;
1775
1799
let added = add_entry ! ( candidate, our_node_id, payee, 0 , path_value_msat,
1776
- 0 , 0u64 , 0 , 0 ) ;
1800
+ 0 , 0u64 , 0 , 0 ) . is_some ( ) ;
1777
1801
log_trace ! ( logger, "{} direct route to payee via SCID {}" ,
1778
1802
if added { "Added" } else { "Skipped" } , candidate. short_channel_id( ) ) ;
1779
1803
}
@@ -1820,6 +1844,7 @@ where L::Target: Logger {
1820
1844
let mut aggregate_next_hops_path_penalty_msat: u64 = 0 ;
1821
1845
let mut aggregate_next_hops_cltv_delta: u32 = 0 ;
1822
1846
let mut aggregate_next_hops_path_length: u8 = 0 ;
1847
+ let mut aggregate_path_contribution_msat = path_value_msat;
1823
1848
1824
1849
for ( idx, ( hop, prev_hop_id) ) in hop_iter. zip ( prev_hop_iter) . enumerate ( ) {
1825
1850
let source = NodeId :: from_pubkey ( & hop. src_node_id ) ;
@@ -1833,10 +1858,13 @@ where L::Target: Logger {
1833
1858
} )
1834
1859
. unwrap_or_else ( || CandidateRouteHop :: PrivateHop { hint : hop } ) ;
1835
1860
1836
- if !add_entry ! ( candidate, source, target, aggregate_next_hops_fee_msat,
1837
- path_value_msat, aggregate_next_hops_path_htlc_minimum_msat,
1838
- aggregate_next_hops_path_penalty_msat,
1839
- aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length) {
1861
+ if let Some ( hop_used_msat) = add_entry ! ( candidate, source, target,
1862
+ aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
1863
+ aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
1864
+ aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length)
1865
+ {
1866
+ aggregate_path_contribution_msat = hop_used_msat;
1867
+ } else {
1840
1868
// If this hop was not used then there is no use checking the preceding
1841
1869
// hops in the RouteHint. We can break by just searching for a direct
1842
1870
// channel between last checked hop and first_hop_targets.
@@ -1863,14 +1891,15 @@ where L::Target: Logger {
1863
1891
. saturating_add ( 1 ) ;
1864
1892
1865
1893
// Searching for a direct channel between last checked hop and first_hop_targets
1866
- if let Some ( first_channels) = first_hop_targets. get ( & NodeId :: from_pubkey ( & prev_hop_id) ) {
1894
+ if let Some ( first_channels) = first_hop_targets. get_mut ( & NodeId :: from_pubkey ( & prev_hop_id) ) {
1895
+ sort_first_hop_channels ( first_channels, & used_channel_liquidities,
1896
+ recommended_value_msat, our_node_pubkey) ;
1867
1897
for details in first_channels {
1868
- let candidate = CandidateRouteHop :: FirstHop { details } ;
1869
- add_entry ! ( candidate, our_node_id, NodeId :: from_pubkey( & prev_hop_id) ,
1870
- aggregate_next_hops_fee_msat, path_value_msat,
1871
- aggregate_next_hops_path_htlc_minimum_msat,
1872
- aggregate_next_hops_path_penalty_msat, aggregate_next_hops_cltv_delta,
1873
- aggregate_next_hops_path_length) ;
1898
+ let first_hop_candidate = CandidateRouteHop :: FirstHop { details } ;
1899
+ add_entry ! ( first_hop_candidate, our_node_id, NodeId :: from_pubkey( & prev_hop_id) ,
1900
+ aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
1901
+ aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
1902
+ aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length) ;
1874
1903
}
1875
1904
}
1876
1905
@@ -1903,12 +1932,15 @@ where L::Target: Logger {
1903
1932
// Note that we *must* check if the last hop was added as `add_entry`
1904
1933
// always assumes that the third argument is a node to which we have a
1905
1934
// path.
1906
- if let Some ( first_channels) = first_hop_targets. get ( & NodeId :: from_pubkey ( & hop. src_node_id ) ) {
1935
+ if let Some ( first_channels) = first_hop_targets. get_mut ( & NodeId :: from_pubkey ( & hop. src_node_id ) ) {
1936
+ sort_first_hop_channels ( first_channels, & used_channel_liquidities,
1937
+ recommended_value_msat, our_node_pubkey) ;
1907
1938
for details in first_channels {
1908
- let candidate = CandidateRouteHop :: FirstHop { details } ;
1909
- add_entry ! ( candidate , our_node_id,
1939
+ let first_hop_candidate = CandidateRouteHop :: FirstHop { details } ;
1940
+ add_entry ! ( first_hop_candidate , our_node_id,
1910
1941
NodeId :: from_pubkey( & hop. src_node_id) ,
1911
- aggregate_next_hops_fee_msat, path_value_msat,
1942
+ aggregate_next_hops_fee_msat,
1943
+ aggregate_path_contribution_msat,
1912
1944
aggregate_next_hops_path_htlc_minimum_msat,
1913
1945
aggregate_next_hops_path_penalty_msat,
1914
1946
aggregate_next_hops_cltv_delta,
@@ -5892,6 +5924,127 @@ mod tests {
5892
5924
assert ! ( route. is_ok( ) ) ;
5893
5925
}
5894
5926
5927
+ #[ test]
5928
+ fn abide_by_route_hint_max_htlc ( ) {
5929
+ // Check that we abide by any htlc_maximum_msat provided in the route hints of the payment
5930
+ // params in the final route.
5931
+ let ( secp_ctx, network_graph, _, _, logger) = build_graph ( ) ;
5932
+ let netgraph = network_graph. read_only ( ) ;
5933
+ let ( _, our_id, _, nodes) = get_nodes ( & secp_ctx) ;
5934
+ let scorer = ln_test_utils:: TestScorer :: new ( ) ;
5935
+ let keys_manager = ln_test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
5936
+ let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
5937
+ let config = UserConfig :: default ( ) ;
5938
+
5939
+ let max_htlc_msat = 50_000 ;
5940
+ let route_hint_1 = RouteHint ( vec ! [ RouteHintHop {
5941
+ src_node_id: nodes[ 2 ] ,
5942
+ short_channel_id: 42 ,
5943
+ fees: RoutingFees {
5944
+ base_msat: 100 ,
5945
+ proportional_millionths: 0 ,
5946
+ } ,
5947
+ cltv_expiry_delta: 10 ,
5948
+ htlc_minimum_msat: None ,
5949
+ htlc_maximum_msat: Some ( max_htlc_msat) ,
5950
+ } ] ) ;
5951
+ let dest_node_id = ln_test_utils:: pubkey ( 42 ) ;
5952
+ let payment_params = PaymentParameters :: from_node_id ( dest_node_id, 42 )
5953
+ . with_route_hints ( vec ! [ route_hint_1. clone( ) ] ) . unwrap ( )
5954
+ . with_bolt11_features ( channelmanager:: provided_invoice_features ( & config) ) . unwrap ( ) ;
5955
+
5956
+ // Make sure we'll error if our route hints don't have enough liquidity according to their
5957
+ // htlc_maximum_msat.
5958
+ if let Err ( LightningError { err, action : ErrorAction :: IgnoreError } ) = get_route ( & our_id,
5959
+ & payment_params, & netgraph, None , max_htlc_msat + 1 , Arc :: clone ( & logger) , & scorer, & ( ) ,
5960
+ & random_seed_bytes)
5961
+ {
5962
+ assert_eq ! ( err, "Failed to find a sufficient route to the given destination" ) ;
5963
+ } else { panic ! ( ) ; }
5964
+
5965
+ // Make sure we'll split an MPP payment across route hints if their htlc_maximum_msat warrants.
5966
+ let mut route_hint_2 = route_hint_1. clone ( ) ;
5967
+ route_hint_2. 0 [ 0 ] . short_channel_id = 43 ;
5968
+ let payment_params = PaymentParameters :: from_node_id ( dest_node_id, 42 )
5969
+ . with_route_hints ( vec ! [ route_hint_1, route_hint_2] ) . unwrap ( )
5970
+ . with_bolt11_features ( channelmanager:: provided_invoice_features ( & config) ) . unwrap ( ) ;
5971
+ let route = get_route ( & our_id, & payment_params, & netgraph, None , max_htlc_msat + 1 ,
5972
+ Arc :: clone ( & logger) , & scorer, & ( ) , & random_seed_bytes) . unwrap ( ) ;
5973
+ assert_eq ! ( route. paths. len( ) , 2 ) ;
5974
+ assert ! ( route. paths[ 0 ] . hops. last( ) . unwrap( ) . fee_msat <= max_htlc_msat) ;
5975
+ assert ! ( route. paths[ 1 ] . hops. last( ) . unwrap( ) . fee_msat <= max_htlc_msat) ;
5976
+ }
5977
+
5978
+ #[ test]
5979
+ fn direct_channel_to_hints_with_max_htlc ( ) {
5980
+ // Check that if we have a first hop channel peer that's connected to multiple provided route
5981
+ // hints, that we properly split the payment between the route hints if needed.
5982
+ let logger = Arc :: new ( ln_test_utils:: TestLogger :: new ( ) ) ;
5983
+ let network_graph = Arc :: new ( NetworkGraph :: new ( Network :: Testnet , Arc :: clone ( & logger) ) ) ;
5984
+ let scorer = ln_test_utils:: TestScorer :: new ( ) ;
5985
+ let keys_manager = ln_test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
5986
+ let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
5987
+ let config = UserConfig :: default ( ) ;
5988
+
5989
+ let our_node_id = ln_test_utils:: pubkey ( 42 ) ;
5990
+ let intermed_node_id = ln_test_utils:: pubkey ( 43 ) ;
5991
+ let first_hop = vec ! [ get_channel_details( Some ( 42 ) , intermed_node_id, InitFeatures :: from_le_bytes( vec![ 0b11 ] ) , 10_000_000 ) ] ;
5992
+
5993
+ let amt_msat = 900_000 ;
5994
+ let max_htlc_msat = 500_000 ;
5995
+ let route_hint_1 = RouteHint ( vec ! [ RouteHintHop {
5996
+ src_node_id: intermed_node_id,
5997
+ short_channel_id: 44 ,
5998
+ fees: RoutingFees {
5999
+ base_msat: 100 ,
6000
+ proportional_millionths: 0 ,
6001
+ } ,
6002
+ cltv_expiry_delta: 10 ,
6003
+ htlc_minimum_msat: None ,
6004
+ htlc_maximum_msat: Some ( max_htlc_msat) ,
6005
+ } , RouteHintHop {
6006
+ src_node_id: intermed_node_id,
6007
+ short_channel_id: 45 ,
6008
+ fees: RoutingFees {
6009
+ base_msat: 100 ,
6010
+ proportional_millionths: 0 ,
6011
+ } ,
6012
+ cltv_expiry_delta: 10 ,
6013
+ htlc_minimum_msat: None ,
6014
+ // Check that later route hint max htlcs don't override earlier ones
6015
+ htlc_maximum_msat: Some ( max_htlc_msat - 50 ) ,
6016
+ } ] ) ;
6017
+ let mut route_hint_2 = route_hint_1. clone ( ) ;
6018
+ route_hint_2. 0 [ 0 ] . short_channel_id = 46 ;
6019
+ route_hint_2. 0 [ 1 ] . short_channel_id = 47 ;
6020
+ let dest_node_id = ln_test_utils:: pubkey ( 44 ) ;
6021
+ let payment_params = PaymentParameters :: from_node_id ( dest_node_id, 42 )
6022
+ . with_route_hints ( vec ! [ route_hint_1, route_hint_2] ) . unwrap ( )
6023
+ . with_bolt11_features ( channelmanager:: provided_invoice_features ( & config) ) . unwrap ( ) ;
6024
+
6025
+ let route = get_route ( & our_node_id, & payment_params, & network_graph. read_only ( ) ,
6026
+ Some ( & first_hop. iter ( ) . collect :: < Vec < _ > > ( ) ) , amt_msat, Arc :: clone ( & logger) , & scorer, & ( ) ,
6027
+ & random_seed_bytes) . unwrap ( ) ;
6028
+ assert_eq ! ( route. paths. len( ) , 2 ) ;
6029
+ assert ! ( route. paths[ 0 ] . hops. last( ) . unwrap( ) . fee_msat <= max_htlc_msat) ;
6030
+ assert ! ( route. paths[ 1 ] . hops. last( ) . unwrap( ) . fee_msat <= max_htlc_msat) ;
6031
+ assert_eq ! ( route. get_total_amount( ) , amt_msat) ;
6032
+
6033
+ // Re-run but with two first hop channels connected to the same route hint peers that must be
6034
+ // split between.
6035
+ let first_hops = vec ! [
6036
+ get_channel_details( Some ( 42 ) , intermed_node_id, InitFeatures :: from_le_bytes( vec![ 0b11 ] ) , amt_msat - 10 ) ,
6037
+ get_channel_details( Some ( 43 ) , intermed_node_id, InitFeatures :: from_le_bytes( vec![ 0b11 ] ) , amt_msat - 10 ) ,
6038
+ ] ;
6039
+ let route = get_route ( & our_node_id, & payment_params, & network_graph. read_only ( ) ,
6040
+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , amt_msat, Arc :: clone ( & logger) , & scorer, & ( ) ,
6041
+ & random_seed_bytes) . unwrap ( ) ;
6042
+ assert_eq ! ( route. paths. len( ) , 2 ) ;
6043
+ assert ! ( route. paths[ 0 ] . hops. last( ) . unwrap( ) . fee_msat <= max_htlc_msat) ;
6044
+ assert ! ( route. paths[ 1 ] . hops. last( ) . unwrap( ) . fee_msat <= max_htlc_msat) ;
6045
+ assert_eq ! ( route. get_total_amount( ) , amt_msat) ;
6046
+ }
6047
+
5895
6048
#[ test]
5896
6049
fn blinded_route_ser ( ) {
5897
6050
let blinded_path_1 = BlindedPath {
0 commit comments