use async_std::sync::{Arc, Mutex};
use chrono::{NaiveDateTime, Utc};
use extended_primitives::Buffer;
use log::warn;
use uuid::Uuid;
use crate::connection::MinerOptions;
#[derive(Debug, Clone)]
pub struct Miner {
pub id: Uuid,
pub sid: Buffer,
pub client: Option<String>,
pub name: Option<String>,
pub stats: Arc<Mutex<MinerStats>>,
pub job_stats: Arc<Mutex<JobStats>>,
pub options: Arc<MinerOptions>,
pub needs_ban: Arc<Mutex<bool>>,
}
impl Miner {
pub fn new(
id: Uuid,
client: Option<String>,
name: Option<String>,
sid: Buffer,
options: Arc<MinerOptions>,
) -> Self {
Miner {
id,
sid,
client,
name,
stats: Arc::new(Mutex::new(MinerStats {
accepted_shares: 0,
rejected_shares: 0,
last_active: Utc::now().naive_utc(),
})),
job_stats: Arc::new(Mutex::new(Default::default())),
options,
needs_ban: Arc::new(Mutex::new(false)),
}
}
pub async fn ban(&self) {
*self.needs_ban.lock().await = true;
}
pub async fn consider_ban(&self) {
let accepted = self.stats.lock().await.accepted_shares;
let rejected = self.stats.lock().await.rejected_shares;
let total = accepted + rejected;
let check_threshold = 500;
let invalid_percent = 50.0;
if total >= check_threshold {
let percent_bad: f64 = (rejected as f64 / total as f64) * 100.0;
if percent_bad < invalid_percent {
} else {
warn!(
"Miner: {} banned. {} out of the last {} shares were invalid",
self.id, rejected, total
);
}
}
}
pub async fn valid_share(&self) {
let mut stats = self.stats.lock().await;
stats.accepted_shares += 1;
stats.last_active = Utc::now().naive_utc();
drop(stats);
self.retarget().await;
}
pub async fn invalid_share(&self) {
self.stats.lock().await.rejected_shares += 1;
}
async fn retarget(&self) {
let now = Utc::now().naive_utc().timestamp();
let mut job_stats = self.job_stats.lock().await;
let since_last = now - job_stats.last_timestamp;
job_stats.times.push(since_last);
job_stats.last_timestamp = now;
if now - job_stats.last_retarget < self.options.retarget_time {
return;
}
let variance = self.options.target_time * (self.options.variance_percent as f64 / 100.0);
let time_min = self.options.target_time - variance;
let time_max = self.options.target_time + variance;
job_stats.last_retarget = now;
let avg: i64 = job_stats.times.iter().sum::<i64>() / job_stats.times.len() as i64;
let mut d_dif = self.options.target_time / avg as f64;
if avg as f64 > time_max && job_stats.current_difficulty > self.options.min_diff {
if d_dif * job_stats.current_difficulty < self.options.min_diff {
d_dif = self.options.min_diff / job_stats.current_difficulty;
}
} else if (avg as f64) < time_min {
if d_dif * job_stats.current_difficulty > self.options.max_diff {
d_dif = self.options.max_diff / job_stats.current_difficulty;
}
} else {
return;
}
let new_diff = job_stats.current_difficulty * d_dif;
job_stats.times.clear();
}
}
#[derive(Debug, Clone)]
pub struct MinerStats {
accepted_shares: u64,
rejected_shares: u64,
last_active: NaiveDateTime,
}
#[derive(Debug, Default)]
pub struct JobStats {
last_timestamp: i64,
last_share_timestamp: i64,
last_retarget: i64,
times: Vec<i64>,
current_difficulty: f64,
job_difficulty: f64,
}