@@ -199,7 +199,7 @@ pub type Scorer = ScorerUsingTime::<std::time::Instant>;
199
199
/// Used to apply a fixed penalty to each channel, thus avoiding long paths when shorter paths with
200
200
/// slightly higher fees are available. Will further penalize channels that fail to relay payments.
201
201
///
202
- /// See [module-level documentation] for usage.
202
+ /// See [module-level documentation] for usage and [`ScoringParameters`] for customization .
203
203
///
204
204
/// [module-level documentation]: crate::routing::scoring
205
205
#[ cfg( feature = "no-std" ) ]
@@ -235,7 +235,7 @@ pub struct ScoringParameters {
235
235
/// A penalty in msats to apply to a channel upon failing to relay a payment.
236
236
///
237
237
/// This accumulates for each failure but may be reduced over time based on
238
- /// [`failure_penalty_half_life`].
238
+ /// [`failure_penalty_half_life`] or when successfully routing through a channel .
239
239
///
240
240
/// Default value: 1,024,000 msat
241
241
///
@@ -262,6 +262,8 @@ pub struct ScoringParameters {
262
262
/// The time required to elapse before any accumulated [`failure_penalty_msat`] penalties are
263
263
/// cut in half.
264
264
///
265
+ /// Successfully routing through a channel will immediately cut the penalty in half as well.
266
+ ///
265
267
/// # Note
266
268
///
267
269
/// When built with the `no-std` feature, time will never elapse. Therefore, this penalty will
@@ -283,11 +285,12 @@ impl_writeable_tlv_based!(ScoringParameters, {
283
285
///
284
286
/// Penalties decay over time, though accumulate as more failures occur.
285
287
struct ChannelFailure < T : Time > {
286
- /// Accumulated penalty in msats for the channel as of `last_failed `.
288
+ /// Accumulated penalty in msats for the channel as of `last_updated `.
287
289
undecayed_penalty_msat : u64 ,
288
290
289
- /// Last time the channel failed. Used to decay `undecayed_penalty_msat`.
290
- last_failed : T ,
291
+ /// Last time the channel either failed to route or successfully routed a payment. Used to decay
292
+ /// `undecayed_penalty_msat`.
293
+ last_updated : T ,
291
294
}
292
295
293
296
impl < T : Time > ScorerUsingTime < T > {
@@ -316,17 +319,22 @@ impl<T: Time> ChannelFailure<T> {
316
319
fn new ( failure_penalty_msat : u64 ) -> Self {
317
320
Self {
318
321
undecayed_penalty_msat : failure_penalty_msat,
319
- last_failed : T :: now ( ) ,
322
+ last_updated : T :: now ( ) ,
320
323
}
321
324
}
322
325
323
326
fn add_penalty ( & mut self , failure_penalty_msat : u64 , half_life : Duration ) {
324
327
self . undecayed_penalty_msat = self . decayed_penalty_msat ( half_life) + failure_penalty_msat;
325
- self . last_failed = T :: now ( ) ;
328
+ self . last_updated = T :: now ( ) ;
329
+ }
330
+
331
+ fn reduce_penalty ( & mut self , half_life : Duration ) {
332
+ self . undecayed_penalty_msat = self . decayed_penalty_msat ( half_life) >> 1 ;
333
+ self . last_updated = T :: now ( ) ;
326
334
}
327
335
328
336
fn decayed_penalty_msat ( & self , half_life : Duration ) -> u64 {
329
- let decays = self . last_failed . elapsed ( ) . as_secs ( ) . checked_div ( half_life. as_secs ( ) ) ;
337
+ let decays = self . last_updated . elapsed ( ) . as_secs ( ) . checked_div ( half_life. as_secs ( ) ) ;
330
338
match decays {
331
339
Some ( decays) => self . undecayed_penalty_msat >> decays,
332
340
None => 0 ,
@@ -385,7 +393,14 @@ impl<T: Time> Score for ScorerUsingTime<T> {
385
393
. or_insert_with ( || ChannelFailure :: new ( failure_penalty_msat) ) ;
386
394
}
387
395
388
- fn payment_path_successful ( & mut self , _path : & [ & RouteHop ] ) { }
396
+ fn payment_path_successful ( & mut self , path : & [ & RouteHop ] ) {
397
+ let half_life = self . params . failure_penalty_half_life ;
398
+ for hop in path. iter ( ) {
399
+ self . channel_failures
400
+ . entry ( hop. short_channel_id )
401
+ . and_modify ( |failure| failure. reduce_penalty ( half_life) ) ;
402
+ }
403
+ }
389
404
}
390
405
391
406
impl < T : Time > Writeable for ScorerUsingTime < T > {
@@ -413,7 +428,7 @@ impl<T: Time> Readable for ScorerUsingTime<T> {
413
428
impl < T : Time > Writeable for ChannelFailure < T > {
414
429
#[ inline]
415
430
fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
416
- let duration_since_epoch = T :: duration_since_epoch ( ) - self . last_failed . elapsed ( ) ;
431
+ let duration_since_epoch = T :: duration_since_epoch ( ) - self . last_updated . elapsed ( ) ;
417
432
write_tlv_fields ! ( w, {
418
433
( 0 , self . undecayed_penalty_msat, required) ,
419
434
( 2 , duration_since_epoch, required) ,
@@ -433,7 +448,7 @@ impl<T: Time> Readable for ChannelFailure<T> {
433
448
} ) ;
434
449
Ok ( Self {
435
450
undecayed_penalty_msat,
436
- last_failed : T :: now ( ) - ( T :: duration_since_epoch ( ) - duration_since_epoch) ,
451
+ last_updated : T :: now ( ) - ( T :: duration_since_epoch ( ) - duration_since_epoch) ,
437
452
} )
438
453
}
439
454
}
@@ -505,8 +520,10 @@ mod tests {
505
520
use super :: { ScoringParameters , ScorerUsingTime , Time } ;
506
521
use super :: time:: Eternity ;
507
522
523
+ use ln:: features:: { ChannelFeatures , NodeFeatures } ;
508
524
use routing:: scoring:: Score ;
509
525
use routing:: network_graph:: NodeId ;
526
+ use routing:: router:: RouteHop ;
510
527
use util:: ser:: { Readable , Writeable } ;
511
528
512
529
use bitcoin:: secp256k1:: PublicKey ;
@@ -685,6 +702,40 @@ mod tests {
685
702
assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , Some ( 1 ) , & source, & target) , 1_384 ) ;
686
703
}
687
704
705
+ #[ test]
706
+ fn reduces_channel_failure_penalties_after_success ( ) {
707
+ let mut scorer = Scorer :: new ( ScoringParameters {
708
+ base_penalty_msat : 1_000 ,
709
+ failure_penalty_msat : 512 ,
710
+ failure_penalty_half_life : Duration :: from_secs ( 10 ) ,
711
+ overuse_penalty_start_1024th : 1024 ,
712
+ overuse_penalty_msat_per_1024th : 0 ,
713
+ } ) ;
714
+ let source = source_node_id ( ) ;
715
+ let target = target_node_id ( ) ;
716
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , Some ( 1 ) , & source, & target) , 1_000 ) ;
717
+
718
+ scorer. payment_path_failed ( & [ ] , 42 ) ;
719
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , Some ( 1 ) , & source, & target) , 1_512 ) ;
720
+
721
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
722
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , Some ( 1 ) , & source, & target) , 1_256 ) ;
723
+
724
+ let hop = RouteHop {
725
+ pubkey : PublicKey :: from_slice ( target. as_slice ( ) ) . unwrap ( ) ,
726
+ node_features : NodeFeatures :: known ( ) ,
727
+ short_channel_id : 42 ,
728
+ channel_features : ChannelFeatures :: known ( ) ,
729
+ fee_msat : 1 ,
730
+ cltv_expiry_delta : 18 ,
731
+ } ;
732
+ scorer. payment_path_successful ( & [ & hop] ) ;
733
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , Some ( 1 ) , & source, & target) , 1_128 ) ;
734
+
735
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
736
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , Some ( 1 ) , & source, & target) , 1_064 ) ;
737
+ }
738
+
688
739
#[ test]
689
740
fn restores_persisted_channel_failure_penalties ( ) {
690
741
let mut scorer = Scorer :: new ( ScoringParameters {
0 commit comments