@@ -199,7 +199,7 @@ pub type Scorer = ScorerUsingTime::<std::time::Instant>;
199199/// Used to apply a fixed penalty to each channel, thus avoiding long paths when shorter paths with
200200/// slightly higher fees are available. Will further penalize channels that fail to relay payments.
201201///
202- /// See [module-level documentation] for usage.
202+ /// See [module-level documentation] for usage and [`ScoringParameters`] for customization .
203203///
204204/// [module-level documentation]: crate::routing::scoring
205205#[ cfg( feature = "no-std" ) ]
@@ -235,7 +235,7 @@ pub struct ScoringParameters {
235235 /// A penalty in msats to apply to a channel upon failing to relay a payment.
236236 ///
237237 /// 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 .
239239 ///
240240 /// Default value: 1,024,000 msat
241241 ///
@@ -262,6 +262,8 @@ pub struct ScoringParameters {
262262 /// The time required to elapse before any accumulated [`failure_penalty_msat`] penalties are
263263 /// cut in half.
264264 ///
265+ /// Successfully routing through a channel will immediately cut the penalty in half as well.
266+ ///
265267 /// # Note
266268 ///
267269 /// When built with the `no-std` feature, time will never elapse. Therefore, this penalty will
@@ -283,11 +285,12 @@ impl_writeable_tlv_based!(ScoringParameters, {
283285///
284286/// Penalties decay over time, though accumulate as more failures occur.
285287struct 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 `.
287289 undecayed_penalty_msat : u64 ,
288290
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 ,
291294}
292295
293296impl < T : Time > ScorerUsingTime < T > {
@@ -316,17 +319,22 @@ impl<T: Time> ChannelFailure<T> {
316319 fn new ( failure_penalty_msat : u64 ) -> Self {
317320 Self {
318321 undecayed_penalty_msat : failure_penalty_msat,
319- last_failed : T :: now ( ) ,
322+ last_updated : T :: now ( ) ,
320323 }
321324 }
322325
323326 fn add_penalty ( & mut self , failure_penalty_msat : u64 , half_life : Duration ) {
324327 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 ( ) ;
326334 }
327335
328336 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 ( ) ) ;
330338 match decays {
331339 Some ( decays) => self . undecayed_penalty_msat >> decays,
332340 None => 0 ,
@@ -385,7 +393,14 @@ impl<T: Time> Score for ScorerUsingTime<T> {
385393 . or_insert_with ( || ChannelFailure :: new ( failure_penalty_msat) ) ;
386394 }
387395
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+ }
389404}
390405
391406impl < T : Time > Writeable for ScorerUsingTime < T > {
@@ -413,7 +428,7 @@ impl<T: Time> Readable for ScorerUsingTime<T> {
413428impl < T : Time > Writeable for ChannelFailure < T > {
414429 #[ inline]
415430 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 ( ) ;
417432 write_tlv_fields ! ( w, {
418433 ( 0 , self . undecayed_penalty_msat, required) ,
419434 ( 2 , duration_since_epoch, required) ,
@@ -433,7 +448,7 @@ impl<T: Time> Readable for ChannelFailure<T> {
433448 } ) ;
434449 Ok ( Self {
435450 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) ,
437452 } )
438453 }
439454}
@@ -505,8 +520,10 @@ mod tests {
505520 use super :: { ScoringParameters , ScorerUsingTime , Time } ;
506521 use super :: time:: Eternity ;
507522
523+ use ln:: features:: { ChannelFeatures , NodeFeatures } ;
508524 use routing:: scoring:: Score ;
509525 use routing:: network_graph:: NodeId ;
526+ use routing:: router:: RouteHop ;
510527 use util:: ser:: { Readable , Writeable } ;
511528
512529 use bitcoin:: secp256k1:: PublicKey ;
@@ -685,6 +702,40 @@ mod tests {
685702 assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , Some ( 1 ) , & source, & target) , 1_384 ) ;
686703 }
687704
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+
688739 #[ test]
689740 fn restores_persisted_channel_failure_penalties ( ) {
690741 let mut scorer = Scorer :: new ( ScoringParameters {
0 commit comments