From efc156149ba275884b0c172a110f1bb165009d41 Mon Sep 17 00:00:00 2001 From: mostafaei2002 Date: Sun, 16 Jun 2024 18:59:00 +0330 Subject: [PATCH] refactor: turn algorithm.rs structure into a struct with implementations --- src/helper/xvb/algorithm.rs | 704 +++++++++++++++++------------------ src/helper/xvb/priv_stats.rs | 16 +- 2 files changed, 349 insertions(+), 371 deletions(-) diff --git a/src/helper/xvb/algorithm.rs b/src/helper/xvb/algorithm.rs index eb6455d..f2c9e1c 100644 --- a/src/helper/xvb/algorithm.rs +++ b/src/helper/xvb/algorithm.rs @@ -4,7 +4,6 @@ use std::{ }; use log::{debug, info, warn}; -use readable::num::Float; use reqwest::Client; use tokio::time::sleep; @@ -24,288 +23,6 @@ use crate::{ use super::{priv_stats::RuntimeDonationLevel, PubXvbApi, SamplesAverageHour}; -pub(crate) fn calcul_donated_time( - lhr: f32, - gui_api_p2pool: &Arc>, - gui_api_xvb: &Arc>, - state_p2pool: &crate::disk::state::P2pool, -) -> u32 { - let p2pool_ehr = lock!(gui_api_p2pool).sidechain_ehr; - // what if ehr stay still for the next ten minutes ? mHR will augment every ten minutes because it thinks that oHR is decreasing. - // - let avg_hr = calc_last_hour_avg_hash_rate(&lock!(gui_api_xvb).p2pool_sent_last_hour_samples); - let mut p2pool_ohr = p2pool_ehr - avg_hr; - if p2pool_ohr < 0.0 { - p2pool_ohr = 0.0; - } - info!("XvB Process | p2pool sidechain HR - last hour average HR = estimated outside HR\n{p2pool_ehr} - {avg_hr} = {p2pool_ohr}"); - let mut min_hr = minimum_hashrate_share( - lock!(gui_api_p2pool).p2pool_difficulty_u64, - state_p2pool.mini, - p2pool_ohr, - ); - if min_hr.is_sign_negative() { - info!("XvB Process | if minimum HR is negative, it is 0."); - min_hr = 0.0; - } - info!("Xvb Process | hr {}, min_hr: {} ", lhr, min_hr); - // numbers are divided by a thousands to print kH/s and not H/s - let msg_lhr = format!( - "{} kH/s local HR from Xmrig", - Float::from_3((lhr / 1000.0).into()) - ); - let msg_mhr = format!( - "{} kH/s minimum required local HR to keep a share in PPLNS window", - Float::from_3((min_hr / 1000.0).into()) - ); - let msg_ehr = format!( - "{} kH/s estimated sent the last hour for your address on p2pool, including this instance", - Float::from_3((p2pool_ehr / 1000.0).into()) - ); - output_console(gui_api_xvb, &msg_lhr); - output_console(gui_api_xvb, &msg_mhr); - output_console(gui_api_xvb, &msg_ehr); - - let xvb_chr = lock!(gui_api_xvb).stats_priv.donor_1hr_avg * 1000.0; - info!("current HR on XvB (last hour): {xvb_chr}"); - let shr = calc_last_hour_avg_hash_rate(&lock!(gui_api_xvb).xvb_sent_last_hour_samples); - - // calculate how much time can be spared - let mode = lock!(gui_api_xvb).stats_priv.runtime_mode.clone(); - - let default_spared_time = time_that_could_be_spared(lhr, min_hr); - let mut spared_time = match mode { - RuntimeMode::Auto => { - info!("RuntimeMode::Auto - calculating spared_time"); - // calculate how much time needed to be spared to be in most round type minimum HR + buffer - minimum_time_for_highest_accessible_round(default_spared_time, lhr, xvb_chr, shr) - } - RuntimeMode::Hero => { - info!("RuntimeMode::Hero - calculating spared_time lhr:{lhr} min_hr:{min_hr}"); - output_console(gui_api_xvb, "Hero mode is enabled for this decision"); - default_spared_time - } - RuntimeMode::ManualXvb => { - let donate_hr = lock!(gui_api_xvb).stats_priv.runtime_manual_amount; - info!("RuntimeMode::ManualXvb - lhr:{lhr} donate_hr:{donate_hr}"); - - XVB_TIME_ALGO * (donate_hr as u32) / (lhr as u32) - } - RuntimeMode::ManualP2pool => { - let keep_hr = lock!(gui_api_xvb).stats_priv.runtime_manual_amount; - info!("RuntimeMode::ManualXvb - lhr:{lhr} keep_hr:{keep_hr}"); - - XVB_TIME_ALGO - (XVB_TIME_ALGO * (keep_hr as u32) / (lhr as u32)) - } - RuntimeMode::ManualDonationLevel => { - let donation_level = lock!(gui_api_xvb) - .stats_priv - .runtime_manual_donation_level - .clone(); - info!("RuntimeMode::ManualDonationLevel"); - - minimum_time_for_manual_round(donation_level, default_spared_time, lhr, xvb_chr, shr) - } - }; - - info!("Final spared_time is {spared_time}"); - if spared_time > XVB_TIME_ALGO { - spared_time = XVB_TIME_ALGO; - } - - spared_time -} -fn minimum_hashrate_share(difficulty: u64, mini: bool, ohr: f32) -> f32 { - let pws = if mini { - BLOCK_PPLNS_WINDOW_MINI - } else { - BLOCK_PPLNS_WINDOW_MAIN - }; - let minimum_hr = ((difficulty / (pws * SECOND_PER_BLOCK_P2POOL)) as f32 * XVB_BUFFER) - ohr; - info!("XvB Process | (difficulty / (window pplns blocks * seconds per p2pool block) * BUFFER) - outside HR = minimum HR to keep a share\n({difficulty} / ({pws} * {SECOND_PER_BLOCK_P2POOL}) * {XVB_BUFFER}) - {ohr} = {minimum_hr}"); - minimum_hr -} -fn time_that_could_be_spared(hr: f32, min_hr: f32) -> u32 { - // percent of time minimum - let minimum_time_required_on_p2pool = XVB_TIME_ALGO as f32 / (hr / min_hr); - info!("XvB Process | Time of algo / local hashrate / minimum hashrate = minimum time required on p2pool\n{XVB_TIME_ALGO} / ({hr} / {min_hr}) = {minimum_time_required_on_p2pool}"); - let spared_time = XVB_TIME_ALGO as f32 - minimum_time_required_on_p2pool; - info!("XvB Process | Time of algo - minimum time required on p2pool = time that can be spared.\n{XVB_TIME_ALGO} - {minimum_time_required_on_p2pool} = {spared_time}"); - // if less than 6 seconds, XMRig could hardly have the time to mine anything. - if spared_time >= 6.0 { - return spared_time as u32; - } - info!( - "XvB Process | sparted time is equal or less than 6 seconds, so everything goes to p2pool." - ); - 0 -} - -// spared time, local hr, current 1h average hr already mining on XvB, 1h average local HR sent on XvB. -fn minimum_time_for_highest_accessible_round(st: u32, lhr: f32, chr: f32, shr: f32) -> u32 { - // we remove one second that could possibly be sent, because if the time needed is a float, it will be rounded up. - // this subtraction can not fail because mnimum spared time is >= 6. - let hr_for_xvb = ((st - 1) as f32 / XVB_TIME_ALGO as f32) * lhr; - info!( - "hr for xvb is: ({st} / {}) * {lhr} = {hr_for_xvb}H/s", - XVB_TIME_ALGO - ); - let ohr = chr - shr; - info!("ohr is: {chr} - {shr} = {ohr}H/s"); - let min_mega = XVB_ROUND_DONOR_MEGA_MIN_HR as f32 - ohr; - info!( - "minimum required HR for mega round is: {} - {ohr} = {min_mega}H/s", - XVB_ROUND_DONOR_MEGA_MIN_HR - ); - let min_whale = XVB_ROUND_DONOR_WHALE_MIN_HR as f32 - ohr; - info!( - "minimum required HR for whale round is: {} - {ohr} = {min_whale}H/s", - XVB_ROUND_DONOR_WHALE_MIN_HR - ); - let min_donorvip = XVB_ROUND_DONOR_VIP_MIN_HR as f32 - ohr; - info!( - "minimum required HR for donor vip round is: {} - {ohr} = {min_donorvip}H/s", - XVB_ROUND_DONOR_VIP_MIN_HR - ); - let min_donor = XVB_ROUND_DONOR_MIN_HR as f32 - ohr; - info!( - "minimum required HR for donor round is: {} - {ohr} = {min_donor}H/s", - XVB_ROUND_DONOR_MIN_HR - ); - let min = match hr_for_xvb { - x if x > min_mega => { - info!("trying to get Mega round"); - info!( - "minimum second to send = ((({x} - ({x} - {min_mega})) / {lhr}) * {}) ", - XVB_TIME_ALGO - ); - min_mega - } - x if x > min_whale => { - info!("trying to get Whale round"); - info!( - "minimum second to send = ((({x} - ({x} - {min_whale})) / {lhr}) * {}) ", - XVB_TIME_ALGO - ); - min_whale - } - x if x > min_donorvip => { - info!("trying to get Vip Donor round"); - info!( - "minimum second to send = ((({x} - ({x} - {min_donorvip})) / {lhr}) * {}) ", - XVB_TIME_ALGO - ); - min_donorvip - } - x if x > min_donor => { - info!("trying to get Donor round"); - info!( - "minimum second to send = ((({x} - ({x} - {min_donor})) / {lhr}) * {}) ", - XVB_TIME_ALGO - ); - min_donor - } - _ => return 0, - }; - - (((hr_for_xvb - (hr_for_xvb - min)) / lhr) * XVB_TIME_ALGO as f32).ceil() as u32 -} - -fn minimum_time_for_manual_round( - level: RuntimeDonationLevel, - st: u32, - lhr: f32, - chr: f32, - shr: f32, -) -> u32 { - let hr_for_xvb = ((st - 1) as f32 / XVB_TIME_ALGO as f32) * lhr; - info!( - "hr for xvb is: ({st} / {}) * {lhr} = {hr_for_xvb}H/s", - XVB_TIME_ALGO - ); - let ohr = chr - shr; - - let min = match level { - RuntimeDonationLevel::Donor => { - info!("RuntimeDonationLevel::Donor"); - XVB_ROUND_DONOR_MIN_HR as f32 - ohr - } - RuntimeDonationLevel::DonorVIP => { - info!("RuntimeDonationLevel::DonorVIP"); - XVB_ROUND_DONOR_VIP_MIN_HR as f32 - ohr - } - RuntimeDonationLevel::DonorWhale => { - info!("RuntimeDonationLevel::DonorWhale"); - XVB_ROUND_DONOR_WHALE_MIN_HR as f32 - ohr - } - RuntimeDonationLevel::DonorMega => { - info!("RuntimeDonationLevel::DonorMega"); - XVB_ROUND_DONOR_MEGA_MIN_HR as f32 - ohr - } - }; - - (((hr_for_xvb - (hr_for_xvb - min)) / lhr) * XVB_TIME_ALGO as f32).ceil() as u32 -} - -#[allow(clippy::too_many_arguments)] -async fn sleep_then_update_node_xmrig( - spared_time: u32, - client: &Client, - api_uri: &str, - token_xmrig: &str, - address: &str, - gui_api_xvb: &Arc>, - gui_api_xmrig: &Arc>, - rig: &str, -) { - let node = lock!(gui_api_xvb).stats_priv.node; - debug!( - "Xvb Process | algo sleep for {} while mining on P2pool", - XVB_TIME_ALGO - spared_time - ); - sleep(Duration::from_secs((XVB_TIME_ALGO - spared_time).into())).await; - // only update xmrig config if it is actually mining. - if spared_time > 0 { - debug!("Xvb Process | request xmrig to mine on XvB"); - if lock!(gui_api_xvb).current_node.is_none() - || lock!(gui_api_xvb) - .current_node - .as_ref() - .is_some_and(|n| n == &XvbNode::P2pool) - { - if let Err(err) = PrivXmrigApi::update_xmrig_config( - client, - api_uri, - token_xmrig, - &node, - address, - gui_api_xmrig, - rig, - ) - .await - { - // show to console error about updating xmrig config - warn!("Xvb Process | Failed request HTTP API Xmrig"); - output_console( - gui_api_xvb, - &format!( - "Failure to update xmrig config with HTTP API.\nError: {}", - err - ), - ); - } else { - debug!("Xvb Process | mining on XvB pool"); - } - } - // will not quit the process until it is really done. - // xvb process watch this algo handle to see if process is finished or not. - sleep(Duration::from_secs(spared_time.into())).await; - } -} -// push new value into samples before executing this calcul -fn calc_last_hour_avg_hash_rate(samples: &SamplesAverageHour) -> f32 { - samples.0.iter().sum::() / samples.0.len() as f32 -} #[allow(clippy::too_many_arguments)] pub(crate) async fn algorithm( client: &Client, @@ -318,24 +35,63 @@ pub(crate) async fn algorithm( time_donated: &Arc>, rig: &str, ) { - debug!("Xvb Process | Algorithm is started"); - output_console( + let mut algorithm = Algorithm::new( + client, gui_api_xvb, - "Algorithm of distribution HR started for the next ten minutes.", + gui_api_xmrig, + gui_api_p2pool, + token_xmrig, + state_p2pool, + share, + time_donated, + rig, ); - // the time that takes the algorithm do decide the next ten minutes could means less p2pool mining. It is solved by the buffer and spawning requests. - let address = &state_p2pool.address; - // request XMrig to mine on P2pool - // if share is in PW, - if share > 0 { - debug!("Xvb Process | Algorithm share is in current window"); - // calcul minimum HR + algorithm.run().await; +} - output_console( - gui_api_xvb, - "At least one share is in current PPLNS window.", - ); +struct Algorithm<'a> { + client: &'a Client, + gui_api_xvb: &'a Arc>, + gui_api_xmrig: &'a Arc>, + gui_api_p2pool: &'a Arc>, + token_xmrig: &'a str, + state_p2pool: &'a crate::disk::state::P2pool, + time_donated: &'a Arc>, + rig: &'a str, + stats: Stats, +} +#[derive(Debug)] +struct Stats { + share: u32, + hashrate_xmrig: f32, + target_donation_hashrate: f32, + xvb_24h_avg: f32, + xvb_1h_avg: f32, + address: String, + runtime_mode: RuntimeMode, + runtime_donation_level: RuntimeDonationLevel, + runtime_amount: f64, + p2pool_total_hashrate: f32, + avg_last_hour_hashrate: f32, + p2pool_external_hashrate: f32, + share_min_hashrate: f32, + spareable_hashrate: f32, + spared_time: u32, +} + +impl<'a> Algorithm<'a> { + fn new( + client: &'a Client, + gui_api_xvb: &'a Arc>, + gui_api_xmrig: &'a Arc>, + gui_api_p2pool: &'a Arc>, + token_xmrig: &'a str, + state_p2pool: &'a crate::disk::state::P2pool, + share: u32, + time_donated: &'a Arc>, + rig: &'a str, + ) -> Self { let hashrate_xmrig = { if lock!(gui_api_xmrig).hashrate_raw_15m > 0.0 { lock!(gui_api_xmrig).hashrate_raw_15m @@ -346,87 +102,116 @@ pub(crate) async fn algorithm( } }; - *lock!(time_donated) = - calcul_donated_time(hashrate_xmrig, gui_api_p2pool, gui_api_xvb, state_p2pool); - let time_donated = *lock!(time_donated); - debug!("Xvb Process | Donated time {} ", time_donated); - output_console( - gui_api_xvb, - &format!( - "Mining on P2pool node for {} seconds then on XvB for {} seconds.", - XVB_TIME_ALGO - time_donated, - time_donated - ), - ); + let address = state_p2pool.address.clone(); - // p2pool need to be mined if donated time is not equal to xvb_time_algo - if time_donated != XVB_TIME_ALGO && lock!(gui_api_xvb).current_node != Some(XvbNode::P2pool) - { - debug!("Xvb Process | request xmrig to mine on p2pool"); - if let Err(err) = PrivXmrigApi::update_xmrig_config( - client, - XMRIG_CONFIG_URI, - token_xmrig, - &XvbNode::P2pool, - address, - gui_api_xmrig, - rig, - ) - .await - { - warn!("Xvb Process | Failed request HTTP API Xmrig"); - output_console( - gui_api_xvb, - &format!( - "Failure to update xmrig config with HTTP API.\nError: {}", - err - ), - ); - } + let xvb_24h_avg = lock!(gui_api_xvb).stats_priv.donor_24hr_avg; + let xvb_1h_avg = lock!(gui_api_xvb).stats_priv.donor_1hr_avg; + + let runtime_mode = lock!(gui_api_xvb).stats_priv.runtime_mode.clone(); + let runtime_donation_level = lock!(gui_api_xvb) + .stats_priv + .runtime_manual_donation_level + .clone(); + let runtime_amount = lock!(gui_api_xvb).stats_priv.runtime_manual_amount.clone(); + + let p2pool_total_hashrate = lock!(gui_api_p2pool).sidechain_ehr; + + let avg_last_hour_hashrate = + Self::calc_last_hour_avg_hash_rate(&lock!(gui_api_xvb).p2pool_sent_last_hour_samples); + let mut p2pool_external_hashrate = p2pool_total_hashrate - avg_last_hour_hashrate; + if p2pool_external_hashrate < 0.0 { + p2pool_external_hashrate = 0.0; } - // sleep 10m less spared time then request XMrig to mine on XvB - sleep_then_update_node_xmrig( - time_donated, - client, - XMRIG_CONFIG_URI, - token_xmrig, + let mut share_min_hashrate = Self::minimum_hashrate_share( + lock!(gui_api_p2pool).p2pool_difficulty_u64, + state_p2pool.mini, + p2pool_external_hashrate, + ); + if share_min_hashrate.is_sign_negative() { + info!("XvB Process | if minimum HR is negative, it is 0."); + share_min_hashrate = 0.0; + } + + let spareable_hashrate = hashrate_xmrig - share_min_hashrate; + + // TODO consider printing algorithm stats instead of spreadout print statements + let stats = Stats { + share, + hashrate_xmrig, + xvb_24h_avg, + xvb_1h_avg, address, + target_donation_hashrate: f32::default(), + runtime_mode, + runtime_donation_level, + runtime_amount, + p2pool_total_hashrate, + avg_last_hour_hashrate, + p2pool_external_hashrate, + share_min_hashrate, + spareable_hashrate, + spared_time: u32::default(), + }; + + let mut new_instace = Self { + client, gui_api_xvb, gui_api_xmrig, - "", - ) - .await; - lock!(gui_api_xvb) - .p2pool_sent_last_hour_samples - .0 - .push_back( - hashrate_xmrig - * ((XVB_TIME_ALGO as f32 - time_donated as f32) / XVB_TIME_ALGO as f32), - ); - lock!(gui_api_xvb) - .xvb_sent_last_hour_samples - .0 - .push_back(hashrate_xmrig * (time_donated as f32 / XVB_TIME_ALGO as f32)); - } else { - // no share, so we mine on p2pool. We update xmrig only if it was still mining on XvB. - if lock!(gui_api_xvb).current_node != Some(XvbNode::P2pool) { + gui_api_p2pool, + token_xmrig, + state_p2pool, + time_donated, + rig, + stats, + }; + + new_instace.stats.target_donation_hashrate = new_instace.get_target_donation_hashrate(); + new_instace.stats.spared_time = ((new_instace.stats.target_donation_hashrate as u32) + / (new_instace.stats.hashrate_xmrig as u32)) + * XVB_TIME_ALGO; + + info!("XvB Process | Starting Algorithm - Algorithm State:",); + info!("{:#?}", new_instace.stats); + + new_instace + } + + fn is_share_fulfilled(&self) -> bool { + self.stats.share > 0 + } + + fn is_xvb_24h_fulfilled(&self) -> bool { + self.stats.xvb_24h_avg > self.stats.target_donation_hashrate + } + + fn xvb_1h_fulfilled(&self) -> bool { + self.stats.xvb_1h_avg > self.stats.target_donation_hashrate + } + + async fn fulfill_xvb_1h(&self) { + self.mine_p2pool().await; + self.sleep_then_update_node_xmrig().await; + } + + async fn mine_p2pool(&self) { + if lock!(self.gui_api_xvb).current_node != Some(XvbNode::P2pool) { info!("Xvb Process | request xmrig to mine on p2pool"); if let Err(err) = PrivXmrigApi::update_xmrig_config( - client, + self.client, XMRIG_CONFIG_URI, - token_xmrig, + self.token_xmrig, &XvbNode::P2pool, - address, - gui_api_xmrig, - rig, + &self.stats.address, + self.gui_api_xmrig, + self.rig, ) .await { warn!("Xvb Process | Failed request HTTP API Xmrig"); output_console( - gui_api_xvb, + self.gui_api_xvb, &format!( "Failure to update xmrig config with HTTP API.\nError: {}", err @@ -434,19 +219,198 @@ pub(crate) async fn algorithm( ); } } - output_console(gui_api_xvb, "No share in the current PPLNS Window !"); - output_console(gui_api_xvb, "Mining on P2pool for the next ten minutes."); + output_console(self.gui_api_xvb, "No share in the current PPLNS Window !"); + output_console( + self.gui_api_xvb, + "Mining on P2pool for the next ten minutes.", + ); sleep(Duration::from_secs(XVB_TIME_ALGO.into())).await; - lock!(gui_api_xvb) + lock!(self.gui_api_xvb) .p2pool_sent_last_hour_samples .0 - .push_back(lock!(gui_api_xmrig).hashrate_raw_15m); - lock!(gui_api_xvb) + .push_back(lock!(self.gui_api_xmrig).hashrate_raw_15m); + lock!(self.gui_api_xvb) .xvb_sent_last_hour_samples .0 .push_back(0.0); } - // algorithm has run, so do not retry but run normally - // put a space to mark the difference with the next run. - output_console_without_time(gui_api_xvb, ""); + + async fn mine_xvb(&self) { + let node = lock!(self.gui_api_xvb).stats_priv.node; + + debug!("Xvb Process | request xmrig to mine on XvB"); + if lock!(self.gui_api_xvb).current_node.is_none() + || lock!(self.gui_api_xvb) + .current_node + .as_ref() + .is_some_and(|n| n == &XvbNode::P2pool) + { + if let Err(err) = PrivXmrigApi::update_xmrig_config( + self.client, + XMRIG_CONFIG_URI, + self.token_xmrig, + &node, + &self.stats.address, + self.gui_api_xmrig, + self.rig, + ) + .await + { + // show to console error about updating xmrig config + warn!("Xvb Process | Failed request HTTP API Xmrig"); + output_console( + self.gui_api_xvb, + &format!( + "Failure to update xmrig config with HTTP API.\nError: {}", + err + ), + ); + } else { + debug!("Xvb Process | mining on XvB pool"); + } + } + sleep(Duration::from_secs(XVB_TIME_ALGO.into())).await; + + lock!(self.gui_api_xvb) + .p2pool_sent_last_hour_samples + .0 + .push_back(lock!(self.gui_api_xmrig).hashrate_raw_15m); + lock!(self.gui_api_xvb) + .xvb_sent_last_hour_samples + .0 + .push_back(0.0); + } + + async fn sleep_then_update_node_xmrig(&self) { + let node = lock!(self.gui_api_xvb).stats_priv.node; + debug!( + "Xvb Process | algo sleep for {} while mining on P2pool", + XVB_TIME_ALGO - self.stats.spared_time + ); + sleep(Duration::from_secs( + (XVB_TIME_ALGO - self.stats.spared_time).into(), + )) + .await; + + // only update xmrig config if it is actually mining. + debug!("Xvb Process | request xmrig to mine on XvB"); + if lock!(self.gui_api_xvb).current_node.is_none() + || lock!(self.gui_api_xvb) + .current_node + .as_ref() + .is_some_and(|n| n == &XvbNode::P2pool) + { + if let Err(err) = PrivXmrigApi::update_xmrig_config( + self.client, + XMRIG_CONFIG_URI, + self.token_xmrig, + &node, + &self.stats.address, + self.gui_api_xmrig, + self.rig, + ) + .await + { + // show to console error about updating xmrig config + warn!("Xvb Process | Failed request HTTP API Xmrig"); + output_console( + self.gui_api_xvb, + &format!( + "Failure to update xmrig config with HTTP API.\nError: {}", + err + ), + ); + } else { + debug!("Xvb Process | mining on XvB pool"); + } + } + // will not quit the process until it is really done. + // xvb process watch this algo handle to see if process is finished or not. + sleep(Duration::from_secs(self.stats.spared_time.into())).await; + } + + fn get_target_donation_hashrate(&self) -> f32 { + match self.stats.runtime_mode { + RuntimeMode::Auto => self.get_auto_mode_target_donation_hashrate(), + RuntimeMode::Hero => self.get_hero_mode_target_donation_hashrate(), + RuntimeMode::ManualXvb => self.stats.runtime_amount as f32, + RuntimeMode::ManualP2pool => { + (XVB_TIME_ALGO as f32) - (self.stats.runtime_amount as f32) + } + RuntimeMode::ManualDonationLevel => self.stats.runtime_donation_level.get_hashrate(), + } + } + + fn get_auto_mode_target_donation_hashrate(&self) -> f32 { + // TODO fix wrong target hashrate being selected + // TODO consider using xvb_24h_avg for calculations + // TODO consider using dynamic buffer size buffer gets smaller as gupaxx runs for longer to provide more stability + + let donation_level = match self.stats.spareable_hashrate { + x if x > (XVB_ROUND_DONOR_MIN_HR as f32) => Some(RuntimeDonationLevel::Donor), + x if x > (XVB_ROUND_DONOR_VIP_MIN_HR as f32) => Some(RuntimeDonationLevel::DonorVIP), + x if x > (XVB_ROUND_DONOR_WHALE_MIN_HR as f32) => { + Some(RuntimeDonationLevel::DonorWhale) + } + x if x > (XVB_ROUND_DONOR_MEGA_MIN_HR as f32) => Some(RuntimeDonationLevel::DonorMega), + _ => None, + }; + + if let Some(level) = donation_level { + level.get_hashrate() + } else { + 0.0 + } + } + + fn get_hero_mode_target_donation_hashrate(&self) -> f32 { + // TODO improve selection method + // TODO consider using a large buffer size + // TODO consider manually setting the share count to aim for on hero mode + + self.stats.spareable_hashrate + } + + // push new value into samples before executing this calcul + fn calc_last_hour_avg_hash_rate(samples: &SamplesAverageHour) -> f32 { + samples.0.iter().sum::() / samples.0.len() as f32 + } + + fn minimum_hashrate_share(difficulty: u64, mini: bool, p2pool_external_hashrate: f32) -> f32 { + let pws = if mini { + BLOCK_PPLNS_WINDOW_MINI + } else { + BLOCK_PPLNS_WINDOW_MAIN + }; + let minimum_hr = ((difficulty / (pws * SECOND_PER_BLOCK_P2POOL)) as f32 * XVB_BUFFER) + - p2pool_external_hashrate; + info!("XvB Process | (difficulty / (window pplns blocks * seconds per p2pool block) * BUFFER) - outside HR = minimum HR to keep a share\n({difficulty} / ({pws} * {SECOND_PER_BLOCK_P2POOL}) * {XVB_BUFFER}) - {p2pool_external_hashrate} = {minimum_hr}"); + minimum_hr + } + + async fn run(&mut self) { + // TODO add console output for each step + + if self.is_share_fulfilled() && self.is_xvb_24h_fulfilled() { + output_console( + self.gui_api_xvb, + "There is a share in p2pool and 24H avg XvB is achieved.", + ); + output_console(self.gui_api_xvb, "Calculating donation time for XvB..."); + + self.fulfill_xvb_1h().await + } else if self.is_share_fulfilled() { + output_console(self.gui_api_xvb, "24H avg XvB target not achieved."); + output_console(self.gui_api_xvb, "Sending all hashrate to XvB"); + + self.mine_xvb().await + } else { + output_console(self.gui_api_xvb, "There are no shares in p2pool"); + output_console(self.gui_api_xvb, "Sending all hashrate to p2pool"); + + self.mine_p2pool().await + } + + output_console(self.gui_api_xvb, "") + } } diff --git a/src/helper/xvb/priv_stats.rs b/src/helper/xvb/priv_stats.rs index 3bc9097..209ef5b 100644 --- a/src/helper/xvb/priv_stats.rs +++ b/src/helper/xvb/priv_stats.rs @@ -9,13 +9,16 @@ use reqwest::{Client, StatusCode}; use serde::Deserialize; use tokio::time::sleep; -use crate::disk::state::XvbMode; use crate::{ disk::state::ManualDonationLevel, helper::{xvb::output_console, Process, ProcessState}, macros::lock, XVB_URL, }; +use crate::{ + disk::state::XvbMode, XVB_ROUND_DONOR_MEGA_MIN_HR, XVB_ROUND_DONOR_MIN_HR, + XVB_ROUND_DONOR_VIP_MIN_HR, XVB_ROUND_DONOR_WHALE_MIN_HR, +}; use super::{nodes::XvbNode, rounds::XvbRound, PubXvbApi}; @@ -38,6 +41,17 @@ pub enum RuntimeDonationLevel { DonorMega, } +impl RuntimeDonationLevel { + pub fn get_hashrate(&self) -> f32 { + match &self { + Self::Donor => XVB_ROUND_DONOR_MIN_HR as f32, + Self::DonorVIP => XVB_ROUND_DONOR_VIP_MIN_HR as f32, + Self::DonorWhale => XVB_ROUND_DONOR_WHALE_MIN_HR as f32, + Self::DonorMega => XVB_ROUND_DONOR_MEGA_MIN_HR as f32, + } + } +} + #[derive(Debug, Clone, Default, Deserialize)] pub struct XvbPrivStats { pub fails: u8,