@@ -247,6 +247,7 @@ impl Time for std::time::Instant {
247247}
248248
249249/// A state in which time has no meaning.
250+ #[ derive( Debug , PartialEq , Eq ) ]
250251pub struct Eternity ;
251252
252253impl Time for Eternity {
@@ -320,3 +321,232 @@ impl<T: Time> Readable for ChannelFailure<T> {
320321 } )
321322 }
322323}
324+
325+ #[ cfg( test) ]
326+ mod tests {
327+ use super :: { Eternity , ScoringParameters , ScorerUsingTime , Time } ;
328+
329+ use routing:: Score ;
330+ use routing:: network_graph:: NodeId ;
331+ use util:: ser:: { Readable , Writeable } ;
332+
333+ use bitcoin:: secp256k1:: PublicKey ;
334+ use core:: cell:: Cell ;
335+ use core:: ops:: Sub ;
336+ use core:: time:: Duration ;
337+ use io;
338+
339+ /// Time that can be advanced manually in tests.
340+ #[ derive( Debug , PartialEq , Eq ) ]
341+ struct SinceEpoch ( Duration ) ;
342+
343+ impl SinceEpoch {
344+ thread_local ! {
345+ static ELAPSED : Cell <Duration > = core:: cell:: Cell :: new( Duration :: from_secs( 0 ) ) ;
346+ }
347+
348+ fn advance ( duration : Duration ) {
349+ Self :: ELAPSED . with ( |elapsed| elapsed. set ( elapsed. get ( ) + duration) )
350+ }
351+ }
352+
353+ impl Time for SinceEpoch {
354+ fn now ( ) -> Self {
355+ Self ( Self :: duration_since_epoch ( ) )
356+ }
357+
358+ fn duration_since_epoch ( ) -> Duration {
359+ Self :: ELAPSED . with ( |elapsed| elapsed. get ( ) )
360+ }
361+
362+ fn elapsed ( & self ) -> Duration {
363+ Self :: duration_since_epoch ( ) - self . 0
364+ }
365+ }
366+
367+ impl Sub < Duration > for SinceEpoch {
368+ type Output = Self ;
369+
370+ fn sub ( self , other : Duration ) -> Self {
371+ Self ( self . 0 - other)
372+ }
373+ }
374+
375+ #[ test]
376+ fn time_passes_when_advanced ( ) {
377+ let now = SinceEpoch :: now ( ) ;
378+ assert_eq ! ( now. elapsed( ) , Duration :: from_secs( 0 ) ) ;
379+
380+ SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
381+ SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
382+
383+ let elapsed = now. elapsed ( ) ;
384+ let later = SinceEpoch :: now ( ) ;
385+
386+ assert_eq ! ( elapsed, Duration :: from_secs( 2 ) ) ;
387+ assert_eq ! ( later - elapsed, now) ;
388+ }
389+
390+ #[ test]
391+ fn time_never_passes_in_an_eternity ( ) {
392+ let now = Eternity :: now ( ) ;
393+ let elapsed = now. elapsed ( ) ;
394+ let later = Eternity :: now ( ) ;
395+
396+ assert_eq ! ( now. elapsed( ) , Duration :: from_secs( 0 ) ) ;
397+ assert_eq ! ( later - elapsed, now) ;
398+ }
399+
400+ /// A scorer for testing with time that can be manually advanced.
401+ type Scorer = ScorerUsingTime :: < SinceEpoch > ;
402+
403+ fn source_node_id ( ) -> NodeId {
404+ NodeId :: from_pubkey ( & PublicKey :: from_slice ( & hex:: decode ( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap ( ) [ ..] ) . unwrap ( ) )
405+ }
406+
407+ fn target_node_id ( ) -> NodeId {
408+ NodeId :: from_pubkey ( & PublicKey :: from_slice ( & hex:: decode ( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap ( ) [ ..] ) . unwrap ( ) )
409+ }
410+
411+ #[ test]
412+ fn penalizes_without_channel_failures ( ) {
413+ let scorer = Scorer :: new ( ScoringParameters {
414+ base_penalty_msat : 1_000 ,
415+ failure_penalty_msat : 512 ,
416+ failure_penalty_half_life : Duration :: from_secs ( 1 ) ,
417+ } ) ;
418+ let source = source_node_id ( ) ;
419+ let target = target_node_id ( ) ;
420+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_000 ) ;
421+
422+ SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
423+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_000 ) ;
424+ }
425+
426+ #[ test]
427+ fn accumulates_channel_failure_penalties ( ) {
428+ let mut scorer = Scorer :: new ( ScoringParameters {
429+ base_penalty_msat : 1_000 ,
430+ failure_penalty_msat : 64 ,
431+ failure_penalty_half_life : Duration :: from_secs ( 10 ) ,
432+ } ) ;
433+ let source = source_node_id ( ) ;
434+ let target = target_node_id ( ) ;
435+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_000 ) ;
436+
437+ scorer. payment_path_failed ( & [ ] , 42 ) ;
438+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_064 ) ;
439+
440+ scorer. payment_path_failed ( & [ ] , 42 ) ;
441+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_128 ) ;
442+
443+ scorer. payment_path_failed ( & [ ] , 42 ) ;
444+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_192 ) ;
445+ }
446+
447+ #[ test]
448+ fn decays_channel_failure_penalties_over_time ( ) {
449+ let mut scorer = Scorer :: new ( ScoringParameters {
450+ base_penalty_msat : 1_000 ,
451+ failure_penalty_msat : 512 ,
452+ failure_penalty_half_life : Duration :: from_secs ( 10 ) ,
453+ } ) ;
454+ let source = source_node_id ( ) ;
455+ let target = target_node_id ( ) ;
456+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_000 ) ;
457+
458+ scorer. payment_path_failed ( & [ ] , 42 ) ;
459+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_512 ) ;
460+
461+ SinceEpoch :: advance ( Duration :: from_secs ( 9 ) ) ;
462+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_512 ) ;
463+
464+ SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
465+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_256 ) ;
466+
467+ SinceEpoch :: advance ( Duration :: from_secs ( 10 * 8 ) ) ;
468+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_001 ) ;
469+
470+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
471+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_000 ) ;
472+
473+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
474+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_000 ) ;
475+ }
476+
477+ #[ test]
478+ fn accumulates_channel_failure_penalties_after_decay ( ) {
479+ let mut scorer = Scorer :: new ( ScoringParameters {
480+ base_penalty_msat : 1_000 ,
481+ failure_penalty_msat : 512 ,
482+ failure_penalty_half_life : Duration :: from_secs ( 10 ) ,
483+ } ) ;
484+ let source = source_node_id ( ) ;
485+ let target = target_node_id ( ) ;
486+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_000 ) ;
487+
488+ scorer. payment_path_failed ( & [ ] , 42 ) ;
489+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_512 ) ;
490+
491+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
492+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_256 ) ;
493+
494+ scorer. payment_path_failed ( & [ ] , 42 ) ;
495+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_768 ) ;
496+
497+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
498+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_384 ) ;
499+ }
500+
501+ #[ test]
502+ fn restores_persisted_channel_failure_penalties ( ) {
503+ let mut scorer = Scorer :: new ( ScoringParameters {
504+ base_penalty_msat : 1_000 ,
505+ failure_penalty_msat : 512 ,
506+ failure_penalty_half_life : Duration :: from_secs ( 10 ) ,
507+ } ) ;
508+ let source = source_node_id ( ) ;
509+ let target = target_node_id ( ) ;
510+
511+ scorer. payment_path_failed ( & [ ] , 42 ) ;
512+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_512 ) ;
513+
514+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
515+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_256 ) ;
516+
517+ scorer. payment_path_failed ( & [ ] , 43 ) ;
518+ assert_eq ! ( scorer. channel_penalty_msat( 43 , & source, & target) , 1_512 ) ;
519+
520+ let mut serialized_scorer = Vec :: new ( ) ;
521+ scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
522+
523+ let deserialized_scorer = <Scorer >:: read ( & mut io:: Cursor :: new ( & serialized_scorer) ) . unwrap ( ) ;
524+ assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target) , 1_256 ) ;
525+ assert_eq ! ( deserialized_scorer. channel_penalty_msat( 43 , & source, & target) , 1_512 ) ;
526+ }
527+
528+ #[ test]
529+ fn decays_persisted_channel_failure_penalties ( ) {
530+ let mut scorer = Scorer :: new ( ScoringParameters {
531+ base_penalty_msat : 1_000 ,
532+ failure_penalty_msat : 512 ,
533+ failure_penalty_half_life : Duration :: from_secs ( 10 ) ,
534+ } ) ;
535+ let source = source_node_id ( ) ;
536+ let target = target_node_id ( ) ;
537+
538+ scorer. payment_path_failed ( & [ ] , 42 ) ;
539+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target) , 1_512 ) ;
540+
541+ let mut serialized_scorer = Vec :: new ( ) ;
542+ scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
543+
544+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
545+
546+ let deserialized_scorer = <Scorer >:: read ( & mut io:: Cursor :: new ( & serialized_scorer) ) . unwrap ( ) ;
547+ assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target) , 1_256 ) ;
548+
549+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
550+ assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target) , 1_128 ) ;
551+ }
552+ }
0 commit comments