@@ -475,6 +475,9 @@ where L::Target: Logger {
475
475
network_graph : G ,
476
476
logger : L ,
477
477
channel_liquidities : HashMap < u64 , ChannelLiquidity > ,
478
+ /// The last time we were given via a [`ScoreUpdate`] method. This does not imply that we've
479
+ /// decayed every liquidity bound up to that time.
480
+ last_duration_since_epoch : Duration ,
478
481
}
479
482
480
483
/// Parameters for configuring [`ProbabilisticScorer`].
@@ -637,6 +640,22 @@ pub struct ProbabilisticScoringFeeParameters {
637
640
///
638
641
/// Default value: false
639
642
pub linear_success_probability : bool ,
643
+
644
+ /// In order to ensure we have knowledge for as many paths as possible, when probing it makes
645
+ /// sense to bias away from channels for which we have very recent data.
646
+ ///
647
+ /// This value is a penalty that is applied based on the last time that we updated the bounds
648
+ /// on the available liquidity in a channel. The specified value is the maximum penalty that
649
+ /// will be applied.
650
+ ///
651
+ /// It obviously does not make sense to assign a non-0 value here unless you are using the
652
+ /// pathfinding result for background probing.
653
+ ///
654
+ /// Specifically, the following penalty is applied
655
+ /// `probing_diversity_penalty_msat * max(0, (86400 - current time + last update))^2 / 86400^2` is
656
+ ///
657
+ /// Default value: 0
658
+ pub probing_diversity_penalty_msat : u64 ,
640
659
}
641
660
642
661
impl Default for ProbabilisticScoringFeeParameters {
@@ -652,6 +671,7 @@ impl Default for ProbabilisticScoringFeeParameters {
652
671
historical_liquidity_penalty_multiplier_msat : 10_000 ,
653
672
historical_liquidity_penalty_amount_multiplier_msat : 64 ,
654
673
linear_success_probability : false ,
674
+ probing_diversity_penalty_msat : 0 ,
655
675
}
656
676
}
657
677
}
@@ -706,6 +726,7 @@ impl ProbabilisticScoringFeeParameters {
706
726
anti_probing_penalty_msat : 0 ,
707
727
considered_impossible_penalty_msat : 0 ,
708
728
linear_success_probability : true ,
729
+ probing_diversity_penalty_msat : 0 ,
709
730
}
710
731
}
711
732
}
@@ -850,6 +871,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ProbabilisticScorer<G, L> whe
850
871
network_graph,
851
872
logger,
852
873
channel_liquidities : new_hash_map ( ) ,
874
+ last_duration_since_epoch : Duration :: from_secs ( 0 ) ,
853
875
}
854
876
}
855
877
@@ -1172,7 +1194,7 @@ DirectedChannelLiquidity< L, HT, T> {
1172
1194
/// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
1173
1195
/// this direction.
1174
1196
fn penalty_msat (
1175
- & self , amount_msat : u64 , inflight_htlc_msat : u64 ,
1197
+ & self , amount_msat : u64 , inflight_htlc_msat : u64 , last_duration_since_epoch : Duration ,
1176
1198
score_params : & ProbabilisticScoringFeeParameters ,
1177
1199
) -> u64 {
1178
1200
let total_inflight_amount_msat = amount_msat. saturating_add ( inflight_htlc_msat) ;
@@ -1247,6 +1269,13 @@ DirectedChannelLiquidity< L, HT, T> {
1247
1269
}
1248
1270
}
1249
1271
1272
+ if score_params. probing_diversity_penalty_msat != 0 {
1273
+ let time_since_update = last_duration_since_epoch. saturating_sub ( * self . last_datapoint ) ;
1274
+ let mul = Duration :: from_secs ( 60 * 60 * 24 ) . saturating_sub ( time_since_update) . as_secs ( ) ;
1275
+ let penalty = score_params. probing_diversity_penalty_msat . saturating_mul ( mul * mul) ;
1276
+ res = res. saturating_add ( penalty / ( ( 60 * 60 * 24 ) * ( 60 * 60 * 24 ) ) ) ;
1277
+ }
1278
+
1250
1279
res
1251
1280
}
1252
1281
@@ -1398,11 +1427,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreLookUp for Probabilistic
1398
1427
}
1399
1428
1400
1429
let capacity_msat = usage. effective_capacity . as_msat ( ) ;
1430
+ let time = self . last_duration_since_epoch ;
1401
1431
self . channel_liquidities
1402
1432
. get ( scid)
1403
1433
. unwrap_or ( & ChannelLiquidity :: new ( Duration :: ZERO ) )
1404
1434
. as_directed ( & source, & target, capacity_msat)
1405
- . penalty_msat ( usage. amount_msat , usage. inflight_htlc_msat , score_params)
1435
+ . penalty_msat ( usage. amount_msat , usage. inflight_htlc_msat , time , score_params)
1406
1436
. saturating_add ( anti_probing_penalty_msat)
1407
1437
. saturating_add ( base_penalty_msat)
1408
1438
}
@@ -1448,6 +1478,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
1448
1478
}
1449
1479
if at_failed_channel { break ; }
1450
1480
}
1481
+ self . last_duration_since_epoch = duration_since_epoch;
1451
1482
}
1452
1483
1453
1484
fn payment_path_successful ( & mut self , path : & Path , duration_since_epoch : Duration ) {
@@ -1475,6 +1506,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
1475
1506
hop. short_channel_id) ;
1476
1507
}
1477
1508
}
1509
+ self . last_duration_since_epoch = duration_since_epoch;
1478
1510
}
1479
1511
1480
1512
fn probe_failed ( & mut self , path : & Path , short_channel_id : u64 , duration_since_epoch : Duration ) {
@@ -1506,6 +1538,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
1506
1538
liquidity. min_liquidity_offset_msat != 0 || liquidity. max_liquidity_offset_msat != 0 ||
1507
1539
liquidity. liquidity_history . has_datapoints ( )
1508
1540
} ) ;
1541
+ self . last_duration_since_epoch = duration_since_epoch;
1509
1542
}
1510
1543
}
1511
1544
@@ -1939,15 +1972,20 @@ ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScore
1939
1972
r : & mut R , args : ( ProbabilisticScoringDecayParameters , G , L )
1940
1973
) -> Result < Self , DecodeError > {
1941
1974
let ( decay_params, network_graph, logger) = args;
1942
- let mut channel_liquidities = new_hash_map ( ) ;
1975
+ let mut channel_liquidities: HashMap < u64 , ChannelLiquidity > = new_hash_map ( ) ;
1943
1976
read_tlv_fields ! ( r, {
1944
1977
( 0 , channel_liquidities, required) ,
1945
1978
} ) ;
1979
+ let mut last_duration_since_epoch = Duration :: from_secs ( 0 ) ;
1980
+ for ( _, liq) in channel_liquidities. iter ( ) {
1981
+ last_duration_since_epoch = cmp:: max ( last_duration_since_epoch, liq. last_updated ) ;
1982
+ }
1946
1983
Ok ( Self {
1947
1984
decay_params,
1948
1985
network_graph,
1949
1986
logger,
1950
1987
channel_liquidities,
1988
+ last_duration_since_epoch,
1951
1989
} )
1952
1990
}
1953
1991
}
@@ -3542,6 +3580,52 @@ mod tests {
3542
3580
assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat, & params) ,
3543
3581
Some ( 0.0 ) ) ;
3544
3582
}
3583
+
3584
+ #[ test]
3585
+ fn probes_for_diversity ( ) {
3586
+ // Tests the probing_diversity_penalty_msat is applied
3587
+ let logger = TestLogger :: new ( ) ;
3588
+ let network_graph = network_graph ( & logger) ;
3589
+ let params = ProbabilisticScoringFeeParameters {
3590
+ probing_diversity_penalty_msat : 1_000_000 ,
3591
+ ..ProbabilisticScoringFeeParameters :: zero_penalty ( )
3592
+ } ;
3593
+ let decay_params = ProbabilisticScoringDecayParameters {
3594
+ liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
3595
+ ..ProbabilisticScoringDecayParameters :: zero_penalty ( )
3596
+ } ;
3597
+ let mut scorer = ProbabilisticScorer :: new ( decay_params, & network_graph, & logger) ;
3598
+ let source = source_node_id ( ) ;
3599
+
3600
+ let usage = ChannelUsage {
3601
+ amount_msat : 512 ,
3602
+ inflight_htlc_msat : 0 ,
3603
+ effective_capacity : EffectiveCapacity :: Total { capacity_msat : 1_024 , htlc_maximum_msat : 1_024 } ,
3604
+ } ;
3605
+ let channel = network_graph. read_only ( ) . channel ( 42 ) . unwrap ( ) . to_owned ( ) ;
3606
+ let ( info, _) = channel. as_directed_from ( & source) . unwrap ( ) ;
3607
+ let candidate = CandidateRouteHop :: PublicHop ( PublicHopCandidate {
3608
+ info,
3609
+ short_channel_id : 42 ,
3610
+ } ) ;
3611
+
3612
+ // Apply some update to set the last-update time to now
3613
+ scorer. payment_path_failed ( & payment_path_for_amount ( 1000 ) , 42 , Duration :: ZERO ) ;
3614
+
3615
+ // If no time has passed, we get the full probing_diversity_penalty_msat
3616
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 1_000_000 ) ;
3617
+
3618
+ // As time passes the penalty decreases.
3619
+ scorer. time_passed ( Duration :: from_secs ( 1 ) ) ;
3620
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 999_976 ) ;
3621
+
3622
+ scorer. time_passed ( Duration :: from_secs ( 2 ) ) ;
3623
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 999_953 ) ;
3624
+
3625
+ // Once we've gotten halfway through the day our penalty is 1/4 the configured value.
3626
+ scorer. time_passed ( Duration :: from_secs ( 86400 /2 ) ) ;
3627
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 250_000 ) ;
3628
+ }
3545
3629
}
3546
3630
3547
3631
#[ cfg( ldk_bench) ]
0 commit comments