@@ -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,15 @@ 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
+ eprintln ! ( "tsu {:?} ({:?} - {:?})" , time_since_update, last_duration_since_epoch, * self . last_datapoint) ;
1275
+ let mul = Duration :: from_secs ( 60 * 60 * 24 ) . saturating_sub ( time_since_update) . as_secs ( ) ;
1276
+ eprintln ! ( "{:?}^2" , mul) ;
1277
+ let penalty = score_params. probing_diversity_penalty_msat . saturating_mul ( mul * mul) ;
1278
+ res = res. saturating_add ( penalty / ( ( 60 * 60 * 24 ) * ( 60 * 60 * 24 ) ) ) ;
1279
+ }
1280
+
1250
1281
res
1251
1282
}
1252
1283
@@ -1398,11 +1429,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreLookUp for Probabilistic
1398
1429
}
1399
1430
1400
1431
let capacity_msat = usage. effective_capacity . as_msat ( ) ;
1432
+ let time = self . last_duration_since_epoch ;
1401
1433
self . channel_liquidities
1402
1434
. get ( scid)
1403
1435
. unwrap_or ( & ChannelLiquidity :: new ( Duration :: ZERO ) )
1404
1436
. as_directed ( & source, & target, capacity_msat)
1405
- . penalty_msat ( usage. amount_msat , usage. inflight_htlc_msat , score_params)
1437
+ . penalty_msat ( usage. amount_msat , usage. inflight_htlc_msat , time , score_params)
1406
1438
. saturating_add ( anti_probing_penalty_msat)
1407
1439
. saturating_add ( base_penalty_msat)
1408
1440
}
@@ -1448,6 +1480,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
1448
1480
}
1449
1481
if at_failed_channel { break ; }
1450
1482
}
1483
+ self . last_duration_since_epoch = duration_since_epoch;
1451
1484
}
1452
1485
1453
1486
fn payment_path_successful ( & mut self , path : & Path , duration_since_epoch : Duration ) {
@@ -1475,6 +1508,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
1475
1508
hop. short_channel_id) ;
1476
1509
}
1477
1510
}
1511
+ self . last_duration_since_epoch = duration_since_epoch;
1478
1512
}
1479
1513
1480
1514
fn probe_failed ( & mut self , path : & Path , short_channel_id : u64 , duration_since_epoch : Duration ) {
@@ -1506,6 +1540,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
1506
1540
liquidity. min_liquidity_offset_msat != 0 || liquidity. max_liquidity_offset_msat != 0 ||
1507
1541
liquidity. liquidity_history . has_datapoints ( )
1508
1542
} ) ;
1543
+ self . last_duration_since_epoch = duration_since_epoch;
1509
1544
}
1510
1545
}
1511
1546
@@ -1939,15 +1974,20 @@ ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScore
1939
1974
r : & mut R , args : ( ProbabilisticScoringDecayParameters , G , L )
1940
1975
) -> Result < Self , DecodeError > {
1941
1976
let ( decay_params, network_graph, logger) = args;
1942
- let mut channel_liquidities = new_hash_map ( ) ;
1977
+ let mut channel_liquidities: HashMap < u64 , ChannelLiquidity > = new_hash_map ( ) ;
1943
1978
read_tlv_fields ! ( r, {
1944
1979
( 0 , channel_liquidities, required) ,
1945
1980
} ) ;
1981
+ let mut last_duration_since_epoch = Duration :: from_secs ( 0 ) ;
1982
+ for ( _, liq) in channel_liquidities. iter ( ) {
1983
+ last_duration_since_epoch = cmp:: max ( last_duration_since_epoch, liq. last_updated ) ;
1984
+ }
1946
1985
Ok ( Self {
1947
1986
decay_params,
1948
1987
network_graph,
1949
1988
logger,
1950
1989
channel_liquidities,
1990
+ last_duration_since_epoch,
1951
1991
} )
1952
1992
}
1953
1993
}
@@ -3542,6 +3582,52 @@ mod tests {
3542
3582
assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat, & params) ,
3543
3583
Some ( 0.0 ) ) ;
3544
3584
}
3585
+
3586
+ #[ test]
3587
+ fn probes_for_diversity ( ) {
3588
+ // Tests the probing_diversity_penalty_msat is applied
3589
+ let logger = TestLogger :: new ( ) ;
3590
+ let network_graph = network_graph ( & logger) ;
3591
+ let params = ProbabilisticScoringFeeParameters {
3592
+ probing_diversity_penalty_msat : 1_000_000 ,
3593
+ ..ProbabilisticScoringFeeParameters :: zero_penalty ( )
3594
+ } ;
3595
+ let decay_params = ProbabilisticScoringDecayParameters {
3596
+ liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
3597
+ ..ProbabilisticScoringDecayParameters :: zero_penalty ( )
3598
+ } ;
3599
+ let mut scorer = ProbabilisticScorer :: new ( decay_params, & network_graph, & logger) ;
3600
+ let source = source_node_id ( ) ;
3601
+
3602
+ let usage = ChannelUsage {
3603
+ amount_msat : 512 ,
3604
+ inflight_htlc_msat : 0 ,
3605
+ effective_capacity : EffectiveCapacity :: Total { capacity_msat : 1_024 , htlc_maximum_msat : 1_024 } ,
3606
+ } ;
3607
+ let channel = network_graph. read_only ( ) . channel ( 42 ) . unwrap ( ) . to_owned ( ) ;
3608
+ let ( info, _) = channel. as_directed_from ( & source) . unwrap ( ) ;
3609
+ let candidate = CandidateRouteHop :: PublicHop ( PublicHopCandidate {
3610
+ info,
3611
+ short_channel_id : 42 ,
3612
+ } ) ;
3613
+
3614
+ // Apply some update to set the last-update time to now
3615
+ scorer. payment_path_failed ( & payment_path_for_amount ( 1000 ) , 42 , Duration :: ZERO ) ;
3616
+
3617
+ // If no time has passed, we get the full probing_diversity_penalty_msat
3618
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 1_000_000 ) ;
3619
+
3620
+ // As time passes the penalty decreases.
3621
+ scorer. time_passed ( Duration :: from_secs ( 1 ) ) ;
3622
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 999_976 ) ;
3623
+
3624
+ scorer. time_passed ( Duration :: from_secs ( 2 ) ) ;
3625
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 999_953 ) ;
3626
+
3627
+ // Once we've gotten halfway through the day our penalty is 1/4 the configured value.
3628
+ scorer. time_passed ( Duration :: from_secs ( 86400 /2 ) ) ;
3629
+ assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 250_000 ) ;
3630
+ }
3545
3631
}
3546
3632
3547
3633
#[ cfg( ldk_bench) ]
0 commit comments