@@ -113,3 +113,172 @@ impl MiningRule for SyncRateRule {
113
113
}
114
114
}
115
115
}
116
+
117
+ #[ cfg( test) ]
118
+ mod tests {
119
+ use std:: sync:: { atomic:: AtomicBool , Arc } ;
120
+
121
+ use crate :: rules:: { mining_rule:: MiningRule , sync_rate_rule:: SYNC_RATE_WINDOW_MAX_SIZE , ExtraData } ;
122
+ use kaspa_consensus_core:: api:: counters:: ProcessingCountersSnapshot ;
123
+ use kaspa_core:: time:: unix_now;
124
+ use std:: sync:: atomic:: * ;
125
+
126
+ use super :: { SyncRateRule , SYNC_RATE_WINDOW_MIN_THRESHOLD } ;
127
+
128
+ fn create_rule ( ) -> ( Arc < AtomicBool > , SyncRateRule ) {
129
+ let use_sync_rate_rule = Arc :: new ( AtomicBool :: new ( false ) ) ;
130
+ let rule = SyncRateRule :: new ( use_sync_rate_rule. clone ( ) ) ;
131
+ ( use_sync_rate_rule, rule)
132
+ }
133
+
134
+ #[ test]
135
+ fn test_rule_end_to_end_flow ( ) {
136
+ let ( use_sync_rate_rule, rule) = create_rule ( ) ;
137
+
138
+ let good_snapshot =
139
+ ProcessingCountersSnapshot { blocks_submitted : 100 , header_counts : 100 , body_counts : 100 , ..Default :: default ( ) } ;
140
+
141
+ let bad_snapshot = ProcessingCountersSnapshot :: default ( ) ;
142
+
143
+ let extra_data = & ExtraData {
144
+ elapsed_time : std:: time:: Duration :: from_secs ( 10 ) ,
145
+ target_time_per_block : 100 , // 10bps value
146
+ finality_point_timestamp : unix_now ( ) ,
147
+ finality_duration : 1000 ,
148
+ has_sufficient_peer_connectivity : true ,
149
+ } ;
150
+
151
+ // Sync rate should be at 1.0
152
+ for _ in 0 ..10 {
153
+ rule. check_rule ( & good_snapshot, extra_data) ;
154
+ }
155
+
156
+ assert ! (
157
+ !use_sync_rate_rule. load( Ordering :: SeqCst ) ,
158
+ "Expected rule to not be triggered during normal operation. {} | {}" ,
159
+ rule. total_received_blocks. load( Ordering :: SeqCst ) ,
160
+ rule. total_expected_blocks. load( Ordering :: SeqCst )
161
+ ) ;
162
+
163
+ // Sync rate should be at 0.5
164
+ for _ in 0 ..11 {
165
+ rule. check_rule ( & bad_snapshot, extra_data) ;
166
+ }
167
+
168
+ assert ! (
169
+ use_sync_rate_rule. load( Ordering :: SeqCst ) ,
170
+ "Expected rule to trigger. {} | {}" ,
171
+ rule. total_received_blocks. load( Ordering :: SeqCst ) ,
172
+ rule. total_expected_blocks. load( Ordering :: SeqCst )
173
+ ) ;
174
+
175
+ for _ in 0 ..10 {
176
+ rule. check_rule ( & good_snapshot, extra_data) ;
177
+ }
178
+
179
+ assert ! (
180
+ !use_sync_rate_rule. load( Ordering :: SeqCst ) ,
181
+ "Expected rule to not be triggered during normal operation. {} | {}" ,
182
+ rule. total_received_blocks. load( Ordering :: SeqCst ) ,
183
+ rule. total_expected_blocks. load( Ordering :: SeqCst )
184
+ ) ;
185
+ }
186
+
187
+ #[ test]
188
+ fn test_rule_with_old_finality ( ) {
189
+ let ( use_sync_rate_rule, rule) = create_rule ( ) ;
190
+
191
+ let bad_snapshot = ProcessingCountersSnapshot :: default ( ) ;
192
+
193
+ let extra_data = & ExtraData {
194
+ elapsed_time : std:: time:: Duration :: from_secs ( 10 ) ,
195
+ target_time_per_block : 100 , // 10bps value
196
+ finality_point_timestamp : unix_now ( ) . saturating_sub ( 1000 * 3 ) - 1 , // the millisecond right before timestamp is "old enough"
197
+ finality_duration : 1000 ,
198
+ has_sufficient_peer_connectivity : true ,
199
+ } ;
200
+
201
+ for _ in 0 ..10 {
202
+ rule. check_rule ( & bad_snapshot, extra_data) ;
203
+ }
204
+
205
+ assert ! (
206
+ !use_sync_rate_rule. load( Ordering :: SeqCst ) ,
207
+ "Expected rule to trigger even with low sync rate if finality is old. {} | {}" ,
208
+ rule. total_received_blocks. load( Ordering :: SeqCst ) ,
209
+ rule. total_expected_blocks. load( Ordering :: SeqCst )
210
+ ) ;
211
+ }
212
+
213
+ #[ test]
214
+ fn test_sync_rate_window_updates ( ) {
215
+ let ( _, rule) = create_rule ( ) ;
216
+
217
+ let received_blocks = 123 ;
218
+ let expected_blocks = 456 ;
219
+
220
+ let old_received_total = rule. total_received_blocks . load ( Ordering :: SeqCst ) ;
221
+ let old_expected_total = rule. total_expected_blocks . load ( Ordering :: SeqCst ) ;
222
+
223
+ rule. update_sync_rate_window ( received_blocks, expected_blocks) ;
224
+
225
+ assert_eq ! ( rule. total_received_blocks. load( Ordering :: SeqCst ) , old_received_total + received_blocks) ;
226
+ assert_eq ! ( rule. total_expected_blocks. load( Ordering :: SeqCst ) , old_expected_total + expected_blocks) ;
227
+ }
228
+
229
+ #[ test]
230
+ fn test_sync_rate_window_update_result_sample_sizes ( ) {
231
+ let ( _, rule) = create_rule ( ) ;
232
+
233
+ for _ in 0 ..( SYNC_RATE_WINDOW_MIN_THRESHOLD - 1 ) {
234
+ assert ! ( !rule. update_sync_rate_window( 1 , 1 ) , "Expected false when window min size threshold is not filled but got true" ) ;
235
+ }
236
+
237
+ // sample is greater than threshold now
238
+ assert ! ( rule. update_sync_rate_window( 1 , 1 ) , "Expected true when window min size threshold is filled but got false" ) ;
239
+
240
+ for _ in 0 ..SYNC_RATE_WINDOW_MAX_SIZE {
241
+ // sample is greater than threshold now
242
+ assert ! ( rule. update_sync_rate_window( 1 , 1 ) , "Expected true when window min size threshold is filled but got false" ) ;
243
+ }
244
+
245
+ assert_eq ! (
246
+ rule. sync_rate_samples. read( ) . unwrap( ) . len( ) ,
247
+ SYNC_RATE_WINDOW_MAX_SIZE ,
248
+ "Expected window size to be at max after updating window it was already full"
249
+ ) ;
250
+ }
251
+
252
+ #[ test]
253
+ fn test_sync_rate_window_update_result_when_window_is_filled ( ) {
254
+ let ( _, rule) = create_rule ( ) ;
255
+
256
+ let received_blocks = 10 ;
257
+ let expected_blocks = 10 ;
258
+
259
+ // Fill the window
260
+ for _ in 0 ..SYNC_RATE_WINDOW_MAX_SIZE {
261
+ rule. update_sync_rate_window ( received_blocks, expected_blocks) ;
262
+ }
263
+
264
+ let total_received = rule. total_received_blocks . load ( Ordering :: SeqCst ) ;
265
+ let total_expected = rule. total_expected_blocks . load ( Ordering :: SeqCst ) ;
266
+
267
+ let new_received_block = received_blocks * 2 ;
268
+ let new_expected_block = expected_blocks * 2 ;
269
+ // Add one more sample
270
+ rule. update_sync_rate_window ( new_received_block, new_expected_block) ;
271
+
272
+ assert_eq ! (
273
+ rule. total_received_blocks. load( Ordering :: SeqCst ) ,
274
+ total_received + new_received_block - received_blocks,
275
+ "Expected total received blocks to be updated correctly"
276
+ ) ;
277
+
278
+ assert_eq ! (
279
+ rule. total_expected_blocks. load( Ordering :: SeqCst ) ,
280
+ total_expected + new_expected_block - expected_blocks,
281
+ "Expected total expected blocks to be updated correctly"
282
+ ) ;
283
+ }
284
+ }
0 commit comments