Skip to content

Commit 7aef507

Browse files
committed
add unit tests for sync rate rule
1 parent 4fa00a6 commit 7aef507

File tree

2 files changed

+170
-1
lines changed

2 files changed

+170
-1
lines changed

consensus/core/src/api/counters.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl ProcessingCounters {
2929
}
3030
}
3131

32-
#[derive(Debug, PartialEq, Eq)]
32+
#[derive(Default, Debug, PartialEq, Eq)]
3333
pub struct ProcessingCountersSnapshot {
3434
pub blocks_submitted: u64,
3535
pub header_counts: u64,

protocol/mining/src/rules/sync_rate_rule.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,172 @@ impl MiningRule for SyncRateRule {
113113
}
114114
}
115115
}
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

Comments
 (0)