@@ -1709,7 +1709,117 @@ mod tests {
1709
1709
assert_eq ! ( scorer. channel_penalty_msat( 43 , 250 , 1_000 , & target, & recipient) , 300 ) ;
1710
1710
}
1711
1711
1712
- // TODO: Add test coverage for offset decay
1712
+ #[ test]
1713
+ fn decays_liquidity_bounds_over_time ( ) {
1714
+ let network_graph = network_graph ( ) ;
1715
+ let params = ProbabilisticScoringParameters {
1716
+ liquidity_penalty_multiplier_msat : 1_000 ,
1717
+ liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1718
+ } ;
1719
+ let mut scorer = ProbabilisticScorer :: new ( params, & sender_pubkey ( ) , & network_graph) ;
1720
+ let source = source_node_id ( ) ;
1721
+ let target = target_node_id ( ) ;
1722
+
1723
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 0 , 1_024 , & source, & target) , 0 ) ;
1724
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1_024 , 1_024 , & source, & target) , 3_010 ) ;
1725
+
1726
+ scorer. payment_path_failed ( & payment_path_for_amount ( 768 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 42 ) ;
1727
+ scorer. payment_path_failed ( & payment_path_for_amount ( 128 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 43 ) ;
1728
+
1729
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 0 ) ;
1730
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 256 , 1_024 , & source, & target) , 92 ) ;
1731
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 768 , 1_024 , & source, & target) , 1_424 ) ;
1732
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 896 , 1_024 , & source, & target) , u64 :: max_value( ) ) ;
1733
+
1734
+ SinceEpoch :: advance ( Duration :: from_secs ( 9 ) ) ;
1735
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 0 ) ;
1736
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 256 , 1_024 , & source, & target) , 92 ) ;
1737
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 768 , 1_024 , & source, & target) , 1_424 ) ;
1738
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 896 , 1_024 , & source, & target) , u64 :: max_value( ) ) ;
1739
+
1740
+ SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
1741
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 64 , 1_024 , & source, & target) , 0 ) ;
1742
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 34 ) ;
1743
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 896 , 1_024 , & source, & target) , 1_812 ) ;
1744
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 960 , 1_024 , & source, & target) , u64 :: max_value( ) ) ;
1745
+
1746
+ // Fully decay liquidity lower bound.
1747
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 * 7 ) ) ;
1748
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 0 , 1_024 , & source, & target) , 0 ) ;
1749
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1 , 1_024 , & source, & target) , 0 ) ;
1750
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1_023 , 1_024 , & source, & target) , 2_709 ) ;
1751
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1_024 , 1_024 , & source, & target) , 3_010 ) ;
1752
+
1753
+ // Fully decay liquidity upper bound.
1754
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
1755
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 0 , 1_024 , & source, & target) , 0 ) ;
1756
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1_024 , 1_024 , & source, & target) , 3_010 ) ;
1757
+
1758
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
1759
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 0 , 1_024 , & source, & target) , 0 ) ;
1760
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 1_024 , 1_024 , & source, & target) , 3_010 ) ;
1761
+ }
1762
+
1763
+ #[ test]
1764
+ fn decays_liquidity_bounds_without_shift_overflow ( ) {
1765
+ let network_graph = network_graph ( ) ;
1766
+ let params = ProbabilisticScoringParameters {
1767
+ liquidity_penalty_multiplier_msat : 1_000 ,
1768
+ liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1769
+ } ;
1770
+ let mut scorer = ProbabilisticScorer :: new ( params, & sender_pubkey ( ) , & network_graph) ;
1771
+ let source = source_node_id ( ) ;
1772
+ let target = target_node_id ( ) ;
1773
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 256 , 1_024 , & source, & target) , 124 ) ;
1774
+
1775
+ scorer. payment_path_failed ( & payment_path_for_amount ( 512 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 42 ) ;
1776
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 256 , 1_024 , & source, & target) , 281 ) ;
1777
+
1778
+ // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
1779
+ // would cause an overflow.
1780
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 * 64 ) ) ;
1781
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 256 , 1_024 , & source, & target) , 124 ) ;
1782
+
1783
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
1784
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 256 , 1_024 , & source, & target) , 124 ) ;
1785
+ }
1786
+
1787
+ #[ test]
1788
+ fn restricts_liquidity_bounds_after_decay ( ) {
1789
+ let network_graph = network_graph ( ) ;
1790
+ let params = ProbabilisticScoringParameters {
1791
+ liquidity_penalty_multiplier_msat : 1_000 ,
1792
+ liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1793
+ } ;
1794
+ let mut scorer = ProbabilisticScorer :: new ( params, & sender_pubkey ( ) , & network_graph) ;
1795
+ let source = source_node_id ( ) ;
1796
+ let target = target_node_id ( ) ;
1797
+
1798
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512 , 1_024 , & source, & target) , 300 ) ;
1799
+
1800
+ // More knowledge gives higher confidence (256, 768), meaning a lower penalty.
1801
+ scorer. payment_path_failed ( & payment_path_for_amount ( 768 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 42 ) ;
1802
+ scorer. payment_path_failed ( & payment_path_for_amount ( 256 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 43 ) ;
1803
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512 , 1_024 , & source, & target) , 281 ) ;
1804
+
1805
+ // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
1806
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
1807
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512 , 1_024 , & source, & target) , 293 ) ;
1808
+
1809
+ // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
1810
+ // is closer to the upper bound, meaning a higher penalty.
1811
+ scorer. payment_path_successful ( & payment_path_for_amount ( 64 ) . iter ( ) . collect :: < Vec < _ > > ( ) ) ;
1812
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512 , 1_024 , & source, & target) , 333 ) ;
1813
+
1814
+ // Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
1815
+ // is closer to the lower bound, meaning a lower penalty.
1816
+ scorer. payment_path_failed ( & payment_path_for_amount ( 256 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 43 ) ;
1817
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512 , 1_024 , & source, & target) , 247 ) ;
1818
+
1819
+ // Further decaying affects the lower bound more than the upper bound (128, 928).
1820
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
1821
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512 , 1_024 , & source, & target) , 280 ) ;
1822
+ }
1713
1823
1714
1824
// TODO: Add test coverage for serialization
1715
1825
}
0 commit comments