- xdp: CString for if_nametoindex (was UB), real detach via prog fd (fabricated borrow_raw(-1) silently never detached), SAFETY comments, saturating expiry math; +5 unit tests - redis: real pubsub reconnect with exponential backoff (was sleep+return); KEYS -> SCAN in heartbeat sweep - ci: cargo test --all-features, repo-gates job — module size gate (scripts/check_module_size.sh, ratchet baseline) + default-secrets grep - refactor src/ to <=250 LOC/file, <=4 .rs/dir without behavior change; thin bins (rampart.rs 330 -> 6 LOC), new app/, subnet/, intel/, profile/, prefix/, challenge/, filter/, probe/, inventory/, metrics/, node/ - docs: TODO v5.0 (status refresh, new rules, findings backlog), README quickstart now matches real binaries - verify: fmt/clippy -D warnings/test --all-features (164 tests)/clang XDP green
88 lines
2.2 KiB
Rust
88 lines
2.2 KiB
Rust
use crate::metrics;
|
|
use std::collections::VecDeque;
|
|
use std::time::Instant;
|
|
|
|
/// Адаптирует сложность PoW к текущему темпу подключений.
|
|
pub struct DifficultyAdjuster {
|
|
window: VecDeque<Instant>,
|
|
min: u8,
|
|
max: u8,
|
|
current: u8,
|
|
}
|
|
|
|
impl Default for DifficultyAdjuster {
|
|
fn default() -> Self {
|
|
Self::new(4, 10)
|
|
}
|
|
}
|
|
|
|
impl DifficultyAdjuster {
|
|
#[must_use]
|
|
pub fn new(min: u8, max: u8) -> Self {
|
|
Self {
|
|
window: VecDeque::new(),
|
|
min: min.max(4),
|
|
max: max.min(10),
|
|
current: min.max(4),
|
|
}
|
|
}
|
|
|
|
pub fn record_connection(&mut self) {
|
|
let now = Instant::now();
|
|
self.window.push_back(now);
|
|
while let Some(&t) = self.window.front() {
|
|
if now.duration_since(t).as_secs() >= 1 {
|
|
self.window.pop_front();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
let new_diff = self.compute_difficulty();
|
|
if self.current != new_diff {
|
|
tracing::info!(
|
|
old = self.current,
|
|
new = new_diff,
|
|
window = self.window.len(),
|
|
"pow: difficulty adjusted"
|
|
);
|
|
self.current = new_diff;
|
|
metrics::POW_CURRENT_DIFFICULTY.set(self.current as i64);
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn current_difficulty(&self) -> u8 {
|
|
metrics::POW_CURRENT_DIFFICULTY.set(self.current as i64);
|
|
self.current
|
|
}
|
|
|
|
fn compute_difficulty(&self) -> u8 {
|
|
match self.window.len() {
|
|
cps if cps > 500 => self.max.max(self.min),
|
|
cps if cps > 200 => 8,
|
|
cps if cps > 50 => 6,
|
|
_ => self.min,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn adjuster_raises_difficulty_under_load() {
|
|
let mut adjuster = DifficultyAdjuster::new(4, 10);
|
|
for _ in 0..600 {
|
|
adjuster.record_connection();
|
|
}
|
|
assert_eq!(adjuster.current_difficulty(), 10);
|
|
}
|
|
|
|
#[test]
|
|
fn adjuster_stays_minimal_when_idle() {
|
|
let mut adjuster = DifficultyAdjuster::new(4, 10);
|
|
adjuster.record_connection();
|
|
assert_eq!(adjuster.current_difficulty(), 4);
|
|
}
|
|
}
|