@@ -52,7 +52,6 @@ use routing::network_graph::NodeId;
52
52
use routing:: router:: RouteHop ;
53
53
54
54
use prelude:: * ;
55
- #[ cfg( not( feature = "no-std" ) ) ]
56
55
use core:: time:: Duration ;
57
56
#[ cfg( not( feature = "no-std" ) ) ]
58
57
use std:: time:: Instant ;
@@ -67,29 +66,42 @@ use std::time::Instant;
67
66
/// [module-level documentation]: crate::routing::scorer
68
67
pub struct Scorer {
69
68
params : ScoringParameters ,
70
- #[ cfg( not( feature = "no-std" ) ) ]
71
- channel_failures : HashMap < u64 , ( u64 , Instant ) > ,
72
- #[ cfg( feature = "no-std" ) ]
73
- channel_failures : HashMap < u64 , u64 > ,
69
+ // TODO: Remove entries of closed channels.
70
+ channel_failures : HashMap < u64 , ChannelFailure > ,
74
71
}
75
72
76
73
/// Parameters for configuring [`Scorer`].
77
74
pub struct ScoringParameters {
78
75
/// A fixed penalty in msats to apply to each channel.
79
76
pub base_penalty_msat : u64 ,
80
77
81
- /// A penalty in msats to apply to a channel upon failure .
78
+ /// A penalty in msats to apply to a channel upon failing to relay a payment .
82
79
///
83
- /// This may be reduced over time based on [`failure_penalty_half_life`].
80
+ /// This accumulates for each failure but may be reduced over time based on
81
+ /// [`failure_penalty_half_life`].
84
82
///
85
83
/// [`failure_penalty_half_life`]: Self::failure_penalty_half_life
86
84
pub failure_penalty_msat : u64 ,
87
85
88
- /// The time needed before any accumulated channel failure penalties are cut in half.
89
- #[ cfg( not( feature = "no-std" ) ) ]
86
+ /// The time required to elapse before any accumulated [`failure_penalty_msat`] penalties are
87
+ /// cut in half.
88
+ ///
89
+ /// [`failure_penalty_msat`]: Self::failure_penalty_msat
90
90
pub failure_penalty_half_life : Duration ,
91
91
}
92
92
93
+ /// Accounting for penalties against a channel for failing to relay any payments.
94
+ ///
95
+ /// Penalties decay over time, though accumulate as more failures occur.
96
+ struct ChannelFailure {
97
+ /// Accumulated penalty in msats for the channel as of `last_failed`.
98
+ undecayed_penalty_msat : u64 ,
99
+
100
+ /// Last time the channel failed. Used to decay `undecayed_penalty_msat`.
101
+ #[ cfg( not( feature = "no-std" ) ) ]
102
+ last_failed : Instant ,
103
+ }
104
+
93
105
impl Scorer {
94
106
/// Creates a new scorer using the given scoring parameters.
95
107
pub fn new ( params : ScoringParameters ) -> Self {
@@ -105,14 +117,41 @@ impl Scorer {
105
117
Self :: new ( ScoringParameters {
106
118
base_penalty_msat : penalty_msat,
107
119
failure_penalty_msat : 0 ,
108
- #[ cfg( not( feature = "no-std" ) ) ]
109
120
failure_penalty_half_life : Duration :: from_secs ( 0 ) ,
110
121
} )
111
122
}
123
+ }
112
124
113
- #[ cfg( not( feature = "no-std" ) ) ]
114
- fn decay_from ( & self , penalty_msat : u64 , last_failure : & Instant ) -> u64 {
115
- decay_from ( penalty_msat, last_failure, self . params . failure_penalty_half_life )
125
+ impl ChannelFailure {
126
+ fn new ( failure_penalty_msat : u64 ) -> Self {
127
+ Self {
128
+ undecayed_penalty_msat : failure_penalty_msat,
129
+ #[ cfg( not( feature = "no-std" ) ) ]
130
+ last_failed : Instant :: now ( ) ,
131
+ }
132
+ }
133
+
134
+ fn add_penalty ( & mut self , failure_penalty_msat : u64 , half_life : Duration ) {
135
+ self . undecayed_penalty_msat = self . decayed_penalty_msat ( half_life) + failure_penalty_msat;
136
+ #[ cfg( not( feature = "no-std" ) ) ]
137
+ {
138
+ self . last_failed = Instant :: now ( ) ;
139
+ }
140
+ }
141
+
142
+ fn decayed_penalty_msat ( & self , half_life : Duration ) -> u64 {
143
+ let decays = self . elapsed ( ) . as_secs ( ) . checked_div ( half_life. as_secs ( ) ) ;
144
+ match decays {
145
+ Some ( decays) => self . undecayed_penalty_msat >> decays,
146
+ None => 0 ,
147
+ }
148
+ }
149
+
150
+ fn elapsed ( & self ) -> Duration {
151
+ #[ cfg( not( feature = "no-std" ) ) ]
152
+ return self . last_failed . elapsed ( ) ;
153
+ #[ cfg( feature = "no-std" ) ]
154
+ return Duration :: from_secs ( 0 ) ;
116
155
}
117
156
}
118
157
@@ -127,7 +166,6 @@ impl Default for ScoringParameters {
127
166
Self {
128
167
base_penalty_msat : 500 ,
129
168
failure_penalty_msat : 1024 * 1000 ,
130
- #[ cfg( not( feature = "no-std" ) ) ]
131
169
failure_penalty_half_life : Duration :: from_secs ( 3600 ) ,
132
170
}
133
171
}
@@ -137,45 +175,19 @@ impl routing::Score for Scorer {
137
175
fn channel_penalty_msat (
138
176
& self , short_channel_id : u64 , _source : & NodeId , _target : & NodeId
139
177
) -> u64 {
140
- #[ cfg( not( feature = "no-std" ) ) ]
141
- let failure_penalty_msat = match self . channel_failures . get ( & short_channel_id) {
142
- Some ( ( penalty_msat, last_failure) ) => self . decay_from ( * penalty_msat, last_failure) ,
143
- None => 0 ,
144
- } ;
145
- #[ cfg( feature = "no-std" ) ]
146
- let failure_penalty_msat =
147
- self . channel_failures . get ( & short_channel_id) . copied ( ) . unwrap_or ( 0 ) ;
178
+ let failure_penalty_msat = self . channel_failures
179
+ . get ( & short_channel_id)
180
+ . map_or ( 0 , |value| value. decayed_penalty_msat ( self . params . failure_penalty_half_life ) ) ;
148
181
149
182
self . params . base_penalty_msat + failure_penalty_msat
150
183
}
151
184
152
185
fn payment_path_failed ( & mut self , _path : & Vec < RouteHop > , short_channel_id : u64 ) {
153
186
let failure_penalty_msat = self . params . failure_penalty_msat ;
154
- #[ cfg( not( feature = "no-std" ) ) ]
155
- {
156
- let half_life = self . params . failure_penalty_half_life ;
157
- self . channel_failures
158
- . entry ( short_channel_id)
159
- . and_modify ( |( penalty_msat, last_failure) | {
160
- let decayed_penalty = decay_from ( * penalty_msat, last_failure, half_life) ;
161
- * penalty_msat = decayed_penalty + failure_penalty_msat;
162
- * last_failure = Instant :: now ( ) ;
163
- } )
164
- . or_insert_with ( || ( failure_penalty_msat, Instant :: now ( ) ) ) ;
165
- }
166
- #[ cfg( feature = "no-std" ) ]
187
+ let half_life = self . params . failure_penalty_half_life ;
167
188
self . channel_failures
168
189
. entry ( short_channel_id)
169
- . and_modify ( |penalty_msat| * penalty_msat += failure_penalty_msat)
170
- . or_insert ( failure_penalty_msat) ;
171
- }
172
- }
173
-
174
- #[ cfg( not( feature = "no-std" ) ) ]
175
- fn decay_from ( penalty_msat : u64 , last_failure : & Instant , half_life : Duration ) -> u64 {
176
- let decays = last_failure. elapsed ( ) . as_secs ( ) . checked_div ( half_life. as_secs ( ) ) ;
177
- match decays {
178
- Some ( decays) => penalty_msat >> decays,
179
- None => 0 ,
190
+ . and_modify ( |failure| failure. add_penalty ( failure_penalty_msat, half_life) )
191
+ . or_insert_with ( || ChannelFailure :: new ( failure_penalty_msat) ) ;
180
192
}
181
193
}
0 commit comments