@@ -247,6 +247,7 @@ impl Time for std::time::Instant {
247
247
}
248
248
249
249
/// A state in which time has no meaning.
250
+ #[ derive( Debug , PartialEq , Eq ) ]
250
251
pub struct Eternity ;
251
252
252
253
impl Time for Eternity {
@@ -320,3 +321,232 @@ impl<T: Time> Readable for ChannelFailure<T> {
320
321
} )
321
322
}
322
323
}
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