@@ -17,7 +17,7 @@ use bitcoin::secp256k1::PublicKey;
17
17
use ln:: channelmanager:: ChannelDetails ;
18
18
use ln:: features:: { ChannelFeatures , InvoiceFeatures , NodeFeatures } ;
19
19
use ln:: msgs:: { DecodeError , ErrorAction , LightningError , MAX_VALUE_MSAT } ;
20
- use routing:: scoring:: Score ;
20
+ use routing:: scoring:: { ChannelUsage , Score } ;
21
21
use routing:: network_graph:: { DirectedChannelInfoWithUpdate , EffectiveCapacity , NetworkGraph , ReadOnlyNetworkGraph , NodeId , RoutingFees } ;
22
22
use util:: ser:: { Writeable , Readable } ;
23
23
use util:: logger:: { Level , Logger } ;
@@ -414,6 +414,16 @@ impl<'a> CandidateRouteHop<'a> {
414
414
}
415
415
}
416
416
417
+ fn htlc_maximum_msat ( & self ) -> u64 {
418
+ match self {
419
+ CandidateRouteHop :: FirstHop { details } => details. next_outbound_htlc_limit_msat ,
420
+ CandidateRouteHop :: PublicHop { info, .. } => info. htlc_maximum_msat ( ) ,
421
+ CandidateRouteHop :: PrivateHop { hint } => {
422
+ hint. htlc_maximum_msat . unwrap_or ( u64:: max_value ( ) )
423
+ } ,
424
+ }
425
+ }
426
+
417
427
fn fees ( & self ) -> RoutingFees {
418
428
match self {
419
429
CandidateRouteHop :: FirstHop { .. } => RoutingFees {
@@ -481,7 +491,8 @@ struct PathBuildingHop<'a> {
481
491
482
492
impl < ' a > core:: fmt:: Debug for PathBuildingHop < ' a > {
483
493
fn fmt ( & self , f : & mut core:: fmt:: Formatter ) -> Result < ( ) , core:: fmt:: Error > {
484
- f. debug_struct ( "PathBuildingHop" )
494
+ let mut debug_struct = f. debug_struct ( "PathBuildingHop" ) ;
495
+ debug_struct
485
496
. field ( "node_id" , & self . node_id )
486
497
. field ( "short_channel_id" , & self . candidate . short_channel_id ( ) )
487
498
. field ( "total_fee_msat" , & self . total_fee_msat )
@@ -490,8 +501,11 @@ impl<'a> core::fmt::Debug for PathBuildingHop<'a> {
490
501
. field ( "total_fee_msat - (next_hops_fee_msat + hop_use_fee_msat)" , & ( & self . total_fee_msat - ( & self . next_hops_fee_msat + & self . hop_use_fee_msat ) ) )
491
502
. field ( "path_penalty_msat" , & self . path_penalty_msat )
492
503
. field ( "path_htlc_minimum_msat" , & self . path_htlc_minimum_msat )
493
- . field ( "cltv_expiry_delta" , & self . candidate . cltv_expiry_delta ( ) )
494
- . finish ( )
504
+ . field ( "cltv_expiry_delta" , & self . candidate . cltv_expiry_delta ( ) ) ;
505
+ #[ cfg( all( not( feature = "_bench_unstable" ) , any( test, fuzzing) ) ) ]
506
+ let debug_struct = debug_struct
507
+ . field ( "value_contribution_msat" , & self . value_contribution_msat ) ;
508
+ debug_struct. finish ( )
495
509
}
496
510
}
497
511
@@ -830,12 +844,12 @@ where L::Target: Logger {
830
844
let recommended_value_msat = final_value_msat * ROUTE_CAPACITY_PROVISION_FACTOR as u64 ;
831
845
let mut path_value_msat = final_value_msat;
832
846
833
- // We don't want multiple paths (as per MPP) share liquidity of the same channels.
834
- // This map allows paths to be aware of the channel use by other paths in the same call.
835
- // This would help to make a better path finding decisions and not "overbook" channels.
836
- // It is unaware of the directions (except for `next_outbound_htlc_limit_msat` in
837
- // `first_hops`).
838
- let mut bookkept_channels_liquidity_available_msat = HashMap :: with_capacity ( network_nodes. len ( ) ) ;
847
+ // Keep track of how much liquidity has been used in selected channels. Used to determine
848
+ // if the channel can be used by additional MPP paths or to inform path finding decisions. It is
849
+ // aware of direction *only* to ensure that the correct htlc_maximum_msat value is used. Hence,
850
+ // liquidity used in one direction will not offset any used in the opposite direction.
851
+ let mut used_channel_liquidities : HashMap < ( u64 , bool ) , u64 > =
852
+ HashMap :: with_capacity ( network_nodes. len ( ) ) ;
839
853
840
854
// Keeping track of how much value we already collected across other paths. Helps to decide:
841
855
// - how much a new path should be transferring (upper bound);
@@ -885,9 +899,7 @@ where L::Target: Logger {
885
899
// - for first and last hops early in get_route
886
900
if $src_node_id != $dest_node_id {
887
901
let short_channel_id = $candidate. short_channel_id( ) ;
888
- let available_liquidity_msat = bookkept_channels_liquidity_available_msat
889
- . entry( short_channel_id)
890
- . or_insert_with( || $candidate. effective_capacity( ) . as_msat( ) ) ;
902
+ let htlc_maximum_msat = $candidate. htlc_maximum_msat( ) ;
891
903
892
904
// It is tricky to subtract $next_hops_fee_msat from available liquidity here.
893
905
// It may be misleading because we might later choose to reduce the value transferred
@@ -896,7 +908,14 @@ where L::Target: Logger {
896
908
// fees caused by one expensive channel, but then this channel could have been used
897
909
// if the amount being transferred over this path is lower.
898
910
// We do this for now, but this is a subject for removal.
899
- if let Some ( available_value_contribution_msat) = available_liquidity_msat. checked_sub( $next_hops_fee_msat) {
911
+ if let Some ( mut available_value_contribution_msat) = htlc_maximum_msat. checked_sub( $next_hops_fee_msat) {
912
+ let used_liquidity_msat = used_channel_liquidities
913
+ . get( & ( short_channel_id, $src_node_id < $dest_node_id) )
914
+ . map_or( 0 , |used_liquidity_msat| {
915
+ available_value_contribution_msat = available_value_contribution_msat
916
+ . saturating_sub( * used_liquidity_msat) ;
917
+ * used_liquidity_msat
918
+ } ) ;
900
919
901
920
// Routing Fragmentation Mitigation heuristic:
902
921
//
@@ -1047,9 +1066,16 @@ where L::Target: Logger {
1047
1066
}
1048
1067
}
1049
1068
1050
- let path_penalty_msat = $next_hops_path_penalty_msat. saturating_add(
1051
- scorer. channel_penalty_msat( short_channel_id, amount_to_transfer_over_msat,
1052
- * available_liquidity_msat, & $src_node_id, & $dest_node_id) ) ;
1069
+ let channel_usage = ChannelUsage {
1070
+ amount_msat: amount_to_transfer_over_msat,
1071
+ inflight_htlc_msat: used_liquidity_msat,
1072
+ effective_capacity: $candidate. effective_capacity( ) ,
1073
+ } ;
1074
+ let channel_penalty_msat = scorer. channel_penalty_msat(
1075
+ short_channel_id, & $src_node_id, & $dest_node_id, channel_usage
1076
+ ) ;
1077
+ let path_penalty_msat = $next_hops_path_penalty_msat
1078
+ . saturating_add( channel_penalty_msat) ;
1053
1079
let new_graph_node = RouteGraphNode {
1054
1080
node_id: $src_node_id,
1055
1081
lowest_fee_to_peer_through_node: total_fee_msat,
@@ -1207,9 +1233,8 @@ where L::Target: Logger {
1207
1233
1208
1234
// TODO: diversify by nodes (so that all paths aren't doomed if one node is offline).
1209
1235
' paths_collection: loop {
1210
- // For every new path, start from scratch, except
1211
- // bookkept_channels_liquidity_available_msat, which will improve
1212
- // the further iterations of path finding. Also don't erase first_hop_targets.
1236
+ // For every new path, start from scratch, except for used_channel_liquidities, which
1237
+ // helps to avoid reusing previously selected paths in future iterations.
1213
1238
targets. clear ( ) ;
1214
1239
dist. clear ( ) ;
1215
1240
hit_minimum_limit = false ;
@@ -1276,16 +1301,6 @@ where L::Target: Logger {
1276
1301
short_channel_id : hop. short_channel_id ,
1277
1302
} )
1278
1303
. unwrap_or_else ( || CandidateRouteHop :: PrivateHop { hint : hop } ) ;
1279
- let capacity_msat = candidate. effective_capacity ( ) . as_msat ( ) ;
1280
- aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
1281
- . saturating_add ( scorer. channel_penalty_msat ( hop. short_channel_id ,
1282
- final_value_msat, capacity_msat, & source, & target) ) ;
1283
-
1284
- aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
1285
- . saturating_add ( hop. cltv_expiry_delta as u32 ) ;
1286
-
1287
- aggregate_next_hops_path_length = aggregate_next_hops_path_length
1288
- . saturating_add ( 1 ) ;
1289
1304
1290
1305
if !add_entry ! ( candidate, source, target, aggregate_next_hops_fee_msat,
1291
1306
path_value_msat, aggregate_next_hops_path_htlc_minimum_msat,
@@ -1297,6 +1312,25 @@ where L::Target: Logger {
1297
1312
hop_used = false ;
1298
1313
}
1299
1314
1315
+ let used_liquidity_msat = used_channel_liquidities
1316
+ . get ( & ( hop. short_channel_id , source < target) ) . copied ( ) . unwrap_or ( 0 ) ;
1317
+ let channel_usage = ChannelUsage {
1318
+ amount_msat : final_value_msat + aggregate_next_hops_fee_msat,
1319
+ inflight_htlc_msat : used_liquidity_msat,
1320
+ effective_capacity : candidate. effective_capacity ( ) ,
1321
+ } ;
1322
+ let channel_penalty_msat = scorer. channel_penalty_msat (
1323
+ hop. short_channel_id , & source, & target, channel_usage
1324
+ ) ;
1325
+ aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
1326
+ . saturating_add ( channel_penalty_msat) ;
1327
+
1328
+ aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
1329
+ . saturating_add ( hop. cltv_expiry_delta as u32 ) ;
1330
+
1331
+ aggregate_next_hops_path_length = aggregate_next_hops_path_length
1332
+ . saturating_add ( 1 ) ;
1333
+
1300
1334
// Searching for a direct channel between last checked hop and first_hop_targets
1301
1335
if let Some ( first_channels) = first_hop_targets. get ( & NodeId :: from_pubkey ( & prev_hop_id) ) {
1302
1336
for details in first_channels {
@@ -1448,26 +1482,30 @@ where L::Target: Logger {
1448
1482
// Remember that we used these channels so that we don't rely
1449
1483
// on the same liquidity in future paths.
1450
1484
let mut prevented_redundant_path_selection = false ;
1451
- for ( payment_hop, _) in payment_path. hops . iter ( ) {
1452
- let channel_liquidity_available_msat = bookkept_channels_liquidity_available_msat. get_mut ( & payment_hop. candidate . short_channel_id ( ) ) . unwrap ( ) ;
1453
- let mut spent_on_hop_msat = value_contribution_msat;
1454
- let next_hops_fee_msat = payment_hop. next_hops_fee_msat ;
1455
- spent_on_hop_msat += next_hops_fee_msat;
1456
- if spent_on_hop_msat == * channel_liquidity_available_msat {
1485
+ let prev_hop_iter = core:: iter:: once ( & our_node_id)
1486
+ . chain ( payment_path. hops . iter ( ) . map ( |( hop, _) | & hop. node_id ) ) ;
1487
+ for ( prev_hop, ( hop, _) ) in prev_hop_iter. zip ( payment_path. hops . iter ( ) ) {
1488
+ let spent_on_hop_msat = value_contribution_msat + hop. next_hops_fee_msat ;
1489
+ let used_liquidity_msat = used_channel_liquidities
1490
+ . entry ( ( hop. candidate . short_channel_id ( ) , * prev_hop < hop. node_id ) )
1491
+ . and_modify ( |used_liquidity_msat| * used_liquidity_msat += spent_on_hop_msat)
1492
+ . or_insert ( spent_on_hop_msat) ;
1493
+ if * used_liquidity_msat == hop. candidate . htlc_maximum_msat ( ) {
1457
1494
// If this path used all of this channel's available liquidity, we know
1458
1495
// this path will not be selected again in the next loop iteration.
1459
1496
prevented_redundant_path_selection = true ;
1460
1497
}
1461
- * channel_liquidity_available_msat -= spent_on_hop_msat ;
1498
+ debug_assert ! ( * used_liquidity_msat <= hop . candidate . htlc_maximum_msat ( ) ) ;
1462
1499
}
1463
1500
if !prevented_redundant_path_selection {
1464
1501
// If we weren't capped by hitting a liquidity limit on a channel in the path,
1465
1502
// we'll probably end up picking the same path again on the next iteration.
1466
1503
// Decrease the available liquidity of a hop in the middle of the path.
1467
1504
let victim_scid = payment_path. hops [ ( payment_path. hops . len ( ) ) / 2 ] . 0 . candidate . short_channel_id ( ) ;
1505
+ let exhausted = u64:: max_value ( ) ;
1468
1506
log_trace ! ( logger, "Disabling channel {} for future path building iterations to avoid duplicates." , victim_scid) ;
1469
- let victim_liquidity = bookkept_channels_liquidity_available_msat . get_mut ( & victim_scid) . unwrap ( ) ;
1470
- * victim_liquidity = 0 ;
1507
+ * used_channel_liquidities . entry ( ( victim_scid, false ) ) . or_default ( ) = exhausted ;
1508
+ * used_channel_liquidities . entry ( ( victim_scid , true ) ) . or_default ( ) = exhausted ;
1471
1509
}
1472
1510
1473
1511
// Track the total amount all our collected paths allow to send so that we:
@@ -1753,7 +1791,7 @@ mod tests {
1753
1791
use routing:: router:: { get_route, add_random_cltv_offset, default_node_features,
1754
1792
PaymentParameters , Route , RouteHint , RouteHintHop , RouteHop , RoutingFees ,
1755
1793
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA , MAX_PATH_LENGTH_ESTIMATE } ;
1756
- use routing:: scoring:: Score ;
1794
+ use routing:: scoring:: { ChannelUsage , Score } ;
1757
1795
use chain:: transaction:: OutPoint ;
1758
1796
use chain:: keysinterface:: KeysInterface ;
1759
1797
use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
@@ -5145,7 +5183,7 @@ mod tests {
5145
5183
fn write < W : Writer > ( & self , _w : & mut W ) -> Result < ( ) , :: io:: Error > { unimplemented ! ( ) }
5146
5184
}
5147
5185
impl Score for BadChannelScorer {
5148
- fn channel_penalty_msat ( & self , short_channel_id : u64 , _send_amt : u64 , _capacity_msat : u64 , _source : & NodeId , _target : & NodeId ) -> u64 {
5186
+ fn channel_penalty_msat ( & self , short_channel_id : u64 , _ : & NodeId , _ : & NodeId , _ : ChannelUsage ) -> u64 {
5149
5187
if short_channel_id == self . short_channel_id { u64:: max_value ( ) } else { 0 }
5150
5188
}
5151
5189
@@ -5163,7 +5201,7 @@ mod tests {
5163
5201
}
5164
5202
5165
5203
impl Score for BadNodeScorer {
5166
- fn channel_penalty_msat ( & self , _short_channel_id : u64 , _send_amt : u64 , _capacity_msat : u64 , _source : & NodeId , target : & NodeId ) -> u64 {
5204
+ fn channel_penalty_msat ( & self , _ : u64 , _ : & NodeId , target : & NodeId , _ : ChannelUsage ) -> u64 {
5167
5205
if * target == self . node_id { u64:: max_value ( ) } else { 0 }
5168
5206
}
5169
5207
0 commit comments