diff --git a/Cargo.toml b/Cargo.toml index 2634ea4..4574ee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ egui = "0.26.2" egui_extras = { version = "0.26.2", features = ["image"] } ## 2023-12-28: https://github.com/hinto-janai/gupax/issues/68 eframe = { version = "0.26.2", default-features = false, features = ["glow"] } - ## 2023-02-06: The below gets fixed by using the [wgpu] backend instead of [glow] ## It also fixes crashes on CPU-based graphics. Only used for Windows. ## Using [wgpu] actually crashes macOS (fixed in 0.20.x though). @@ -82,7 +81,7 @@ serde-this-or-that = "0.4.2" tar = "0.4.40" flate2 = "1.0" sudo = "0.6.0" - +readable = "0.16" # macOS [target.'cfg(target_os = "macos")'.dependencies] # On apple-darwin targets there is an issue with the native and rustls @@ -97,6 +96,8 @@ openssl = { version = "0.10", features = ["vendored"] } # We don't even use `xz` in `flate2` but this gets dynamically # linked as well which causes problems, so statically link it. lzma-sys = { version = "0.1.20", features = ["static"] } +[dev-dependencies] +egui = {version = "0.26.2", features=["callstack"]} [target.'cfg(not(target_os = "macos"))'.dependencies] tls-api-native-tls = "0.9.0" diff --git a/TODO_XMRvsBeast.md b/TODO_XMRvsBeast.md index da8dc16..ee55f9c 100644 --- a/TODO_XMRvsBeast.md +++ b/TODO_XMRvsBeast.md @@ -13,11 +13,11 @@ - [x] link and message hovering explaining registration and needs to read the rules. - [x] token input - [x] hero checkbox - - [ ] log section - - [ ] round type in - - [ ] win or loose + - [x] log section - [x] state of XvB process - [x] private stats + - [x] round type in + - [x] win or loose - [ ] new process for XvB - [x] status process XvB - [x] public information from [API](https://xmrvsbeast.com/p2pool/stats) diff --git a/src/app/panels/bottom.rs b/src/app/panels/bottom.rs index eb3d099..810085f 100644 --- a/src/app/panels/bottom.rs +++ b/src/app/panels/bottom.rs @@ -644,11 +644,11 @@ fn status_xvb(state: ProcessState, ui: &mut Ui, size: Vec2) { color = RED; XVB_FAILED } - NotMining => { + NotMining | Syncing => { color = ORANGE; XVB_PUBLIC_ONLY } - Middle | Waiting | Syncing => { + Middle | Waiting => { color = YELLOW; XVB_MIDDLE } diff --git a/src/app/panels/middle/status/benchmarks.rs b/src/app/panels/middle/status/benchmarks.rs index e423fad..058b717 100644 --- a/src/app/panels/middle/status/benchmarks.rs +++ b/src/app/panels/middle/status/benchmarks.rs @@ -1,9 +1,8 @@ use std::sync::{Arc, Mutex}; -use crate::{ - app::Benchmark, disk::state::Status, helper::xmrig::PubXmrigApi, utils::human::HumanNumber, -}; +use crate::{app::Benchmark, disk::state::Status, helper::xmrig::PubXmrigApi}; use egui::{Hyperlink, ProgressBar, Spinner, Vec2}; +use readable::num::{Float, Percent, Unsigned}; use crate::utils::macros::lock; @@ -67,7 +66,7 @@ impl Status { .on_hover_text(STATUS_SUBMENU_YOUR_HIGH); ui.add_sized( [width, text], - Label::new(format!("{} H/s", HumanNumber::from_f32(cpu.high))), + Label::new(format!("{} H/s", Float::from_0(cpu.high.into()))), ); ui.add_sized( [width, text], @@ -76,7 +75,7 @@ impl Status { .on_hover_text(STATUS_SUBMENU_YOUR_AVERAGE); ui.add_sized( [width, text], - Label::new(format!("{} H/s", HumanNumber::from_f32(cpu.average))), + Label::new(format!("{} H/s", Float::from_0(cpu.average.into()))), ); ui.add_sized( [width, text], @@ -85,7 +84,7 @@ impl Status { .on_hover_text(STATUS_SUBMENU_YOUR_LOW); ui.add_sized( [width, text], - Label::new(format!("{} H/s", HumanNumber::from_f32(cpu.low))), + Label::new(format!("{} H/s", Float::from_0(cpu.low.into()))), ); }) }) @@ -96,7 +95,7 @@ impl Status { if xmrig_alive { let api = lock!(xmrig_api); let percent = (api.hashrate_raw / cpu.high) * 100.0; - let human = HumanNumber::to_percent(percent); + let human = Percent::from(percent); if percent > 100.0 { ui.add_sized( [width, double], @@ -188,31 +187,34 @@ impl Status { ui.add_sized([cpu, text], Label::new(benchmark.cpu.as_str())); ui.separator(); ui.add_sized([bar, text], ProgressBar::new(benchmark.percent / 100.0)) - .on_hover_text(HumanNumber::to_percent(benchmark.percent).as_str()); + .on_hover_text(Percent::from(benchmark.percent).as_str()); ui.separator(); ui.add_sized( [high, text], - Label::new(HumanNumber::to_hashrate(benchmark.high).as_str()), + Label::new(format!("{} H/s", Float::from_0(benchmark.high.into()))), ); ui.separator(); ui.add_sized( [average, text], - Label::new(HumanNumber::to_hashrate(benchmark.average).as_str()), + Label::new(format!( + "{} H/s", + Float::from_0(benchmark.average.into()) + )), ); ui.separator(); ui.add_sized( [low, text], - Label::new(HumanNumber::to_hashrate(benchmark.low).as_str()), + Label::new(format!("{} H/s", Float::from_0(benchmark.low.into()))), ); ui.separator(); ui.add_sized( [rank, text], - Label::new(HumanNumber::from_u16(benchmark.rank).as_str()), + Label::new(Float::from(benchmark.low).as_str()), ); ui.separator(); ui.add_sized( [bench, text], - Label::new(HumanNumber::from_u16(benchmark.benchmarks).as_str()), + Label::new(Unsigned::from(benchmark.benchmarks).as_str()), ); }) }); diff --git a/src/app/panels/middle/status/p2pool.rs b/src/app/panels/middle/status/p2pool.rs index afc2f68..f318d8b 100644 --- a/src/app/panels/middle/status/p2pool.rs +++ b/src/app/panels/middle/status/p2pool.rs @@ -1,6 +1,7 @@ use std::sync::{Arc, Mutex}; use egui::{Label, RichText, SelectableLabel, Slider, TextEdit, Vec2}; +use readable::num::Unsigned; use crate::{ disk::{ @@ -9,7 +10,7 @@ use crate::{ status::{Hash, PayoutView}, }, helper::p2pool::PubP2poolApi, - utils::{constants::*, human::HumanNumber, macros::lock}, + utils::{constants::*, macros::lock}, }; impl Status { @@ -267,7 +268,7 @@ impl Status { ); ui.add_sized( [width, text], - Label::new(format!("{} H/s", HumanNumber::from_u64(hashrate))), + Label::new(format!("{} H/s", Unsigned::from(hashrate))), ); ui.add_sized( [width, text], diff --git a/src/app/panels/middle/status/processes.rs b/src/app/panels/middle/status/processes.rs index d0b6098..8433b41 100644 --- a/src/app/panels/middle/status/processes.rs +++ b/src/app/panels/middle/status/processes.rs @@ -1,4 +1,5 @@ use egui::{ScrollArea, Ui, Vec2}; +use readable::up::UptimeFull; use std::sync::{Arc, Mutex}; use crate::disk::state::Status; @@ -164,7 +165,17 @@ fn p2pool( Label::new(RichText::new("Shares Found").underline().color(BONE)), ) .on_hover_text(STATUS_P2POOL_SHARES); - ui.add_sized([width, height], Label::new(format!("{}", api.shares_found))); + ui.add_sized( + [width, height], + Label::new(format!( + "{}", + if let Some(s) = api.shares_found { + s.to_string() + } else { + UNKNOWN_DATA.to_string() + } + )), + ); ui.add_sized( [width, height], Label::new(RichText::new("Payouts").underline().color(BONE)), @@ -293,7 +304,10 @@ fn xmrig( Label::new(RichText::new("Uptime").underline().color(BONE)), ) .on_hover_text(STATUS_XMRIG_UPTIME); - ui.add_sized([width, height], Label::new(format!("{}", api.uptime))); + ui.add_sized( + [width, height], + Label::new(UptimeFull::from(api.uptime).as_str()), + ); ui.add_sized( [width, height], Label::new( diff --git a/src/app/panels/middle/xvb.rs b/src/app/panels/middle/xvb.rs index 727b25a..d6ef41a 100644 --- a/src/app/panels/middle/xvb.rs +++ b/src/app/panels/middle/xvb.rs @@ -7,7 +7,8 @@ use log::debug; use crate::helper::xvb::PubXvbApi; use crate::utils::constants::{ GREEN, LIGHT_GRAY, ORANGE, RED, XVB_DONATED_1H_FIELD, XVB_DONATED_24H_FIELD, XVB_FAILURE_FIELD, - XVB_HELP, XVB_HERO_SELECT, XVB_TOKEN_FIELD, XVB_TOKEN_LEN, XVB_URL_RULES, + XVB_HELP, XVB_HERO_SELECT, XVB_ROUND_TYPE_FIELD, XVB_TOKEN_FIELD, XVB_TOKEN_LEN, XVB_URL_RULES, + XVB_WINNER_FIELD, }; use crate::utils::macros::lock; use crate::utils::regex::Regexes; @@ -125,11 +126,15 @@ impl crate::disk::state::Xvb { // ui.vertical_centered(|ui| { ui.horizontal(|ui| { // widget takes a third less space for two separator. - let width_stat = - (ui.available_width() / 3.0) - (12.0 + ui.style().spacing.item_spacing.x) / 3.0; + let width_stat = (ui.available_width() / 5.0) + - ((24.0 + ui.style().spacing.item_spacing.x + SPACE) / 5.0); // 0.0 means minimum let height_stat = 0.0; let size_stat = vec2(width_stat, height_stat); + let round = match &priv_stats.round_participate { + Some(r) => r.to_string(), + None => "None".to_string(), + }; ui.add_sized(size_stat, |ui: &mut Ui| { ui.group(|ui| { let size_stat = vec2( @@ -159,12 +164,36 @@ impl crate::disk::state::Xvb { }) .response }); + ui.separator(); + ui.add_enabled_ui(priv_stats.round_participate.is_some(), |ui| { + ui.add_sized(size_stat, |ui: &mut Ui| { + ui.vertical_centered(|ui| { + ui.label(XVB_ROUND_TYPE_FIELD); + ui.label(round); + }) + .response + }) + .on_disabled_hover_text("You do not yet have a share in the PPLNS Window."); + }); + ui.separator(); + ui.add_sized(size_stat, |ui: &mut Ui| { + ui.vertical_centered(|ui| { + ui.label(XVB_WINNER_FIELD); + ui.label( + priv_stats + .win_current + .then(|| "You are Winning the round !") + .unwrap_or("You are not the winner"), + ); + }) + .response + }); }) .response }); }); // Rules link help - ui.add_space(ui.available_height() / 4.0); + ui.add_space(ui.available_height() / 2.0); ui.vertical_centered(|ui| { ui.hyperlink_to("Rules", XVB_URL_RULES) .on_hover_text("Click here to read the rules and understand how the raffle works."); diff --git a/src/helper/mod.rs b/src/helper/mod.rs index 7d329fa..af0fb44 100644 --- a/src/helper/mod.rs +++ b/src/helper/mod.rs @@ -742,7 +742,10 @@ mod test { assert_eq!(p.hashrate_15m.to_string(), "10,000"); assert_eq!(p.hashrate_1h.to_string(), "20,000"); assert_eq!(p.hashrate_24h.to_string(), "30,000"); - assert_eq!(p.shares_found.to_string(), "1,000"); + assert_eq!( + p.shares_found.expect("the value is set").to_string(), + "1,000" + ); assert_eq!(p.average_effort.to_string(), "100.00%"); assert_eq!(p.current_effort.to_string(), "200.00%"); assert_eq!(p.connections.to_string(), "1,234"); diff --git a/src/helper/p2pool.rs b/src/helper/p2pool.rs index 4855c3e..9ad7643 100644 --- a/src/helper/p2pool.rs +++ b/src/helper/p2pool.rs @@ -167,7 +167,7 @@ impl Helper { // Takes in a 95-char Monero address, returns the first and last // 6 characters separated with dots like so: [4abcde...abcdef] - fn head_tail_of_monero_address(address: &str) -> String { + pub fn head_tail_of_monero_address(address: &str) -> String { if address.len() < 95 { return "???".to_string(); } @@ -718,7 +718,7 @@ pub struct PubP2poolApi { pub hashrate_15m: HumanNumber, pub hashrate_1h: HumanNumber, pub hashrate_24h: HumanNumber, - pub shares_found: HumanNumber, + pub shares_found: Option, pub average_effort: HumanNumber, pub current_effort: HumanNumber, pub connections: HumanNumber, @@ -775,7 +775,7 @@ impl PubP2poolApi { hashrate_15m: HumanNumber::unknown(), hashrate_1h: HumanNumber::unknown(), hashrate_24h: HumanNumber::unknown(), - shares_found: HumanNumber::unknown(), + shares_found: None, average_effort: HumanNumber::unknown(), current_effort: HumanNumber::unknown(), connections: HumanNumber::unknown(), @@ -944,7 +944,7 @@ impl PubP2poolApi { hashrate_15m: HumanNumber::from_u64(local.hashrate_15m), hashrate_1h: HumanNumber::from_u64(local.hashrate_1h), hashrate_24h: HumanNumber::from_u64(local.hashrate_24h), - shares_found: HumanNumber::from_u64(local.shares_found), + shares_found: Some(local.shares_found), average_effort: HumanNumber::to_percent(local.average_effort), current_effort: HumanNumber::to_percent(local.current_effort), connections: HumanNumber::from_u32(local.connections), @@ -1114,6 +1114,45 @@ impl PubP2poolApi { _ => "[************************************************************]", } } + // function to know if a share is in current window. + // If new share found, an instant of when it was found. + pub(super) fn is_share_present_in_ppplns_window( + &self, + old_shares: &mut u64, + last_time_found: Option, + mini: bool, + ) -> (bool, Option) { + if let Some(v) = self.shares_found { + if v > *old_shares { + // found new share + *old_shares = v; + return (true, Some(Instant::now())); + } else { + // no new share found this last minute, check if last share is still valid + if let Some(n) = last_time_found { + // a share was found before, is it still valid ? + let time_window = if mini { + TIME_PPLNS_WINDOW_MINI + } else { + TIME_PPLNS_WINDOW_MAIN + }; + if n.elapsed() > time_window { + // if time is expired, no share is present in current PW + (false, None) + } else { + // if time is not expired, at least one is present in current PW + (true, None) + } + } else { + // no share found before and no new share found, so there is no share in current PW + (false, None) + } + } + } else { + // data from p2pool is not ready yet, so no share in pplns window. + return (false, None); + } + } } //---------------------------------------------------------------------------------------------------- Private P2Pool "Local" Api diff --git a/src/helper/xmrig.rs b/src/helper/xmrig.rs index 8f6896c..9f00239 100644 --- a/src/helper/xmrig.rs +++ b/src/helper/xmrig.rs @@ -1,8 +1,11 @@ use crate::helper::{ProcessName, ProcessSignal, ProcessState}; use crate::regex::XMRIG_REGEX; +use crate::utils::human::HumanNumber; use crate::utils::sudo::SudoState; -use crate::{constants::*, human::*, macros::*}; +use crate::{constants::*, macros::*}; use log::*; +use readable::num::Unsigned; +use readable::up::Uptime; use serde::{Deserialize, Serialize}; use std::path::Path; use std::{ @@ -405,7 +408,7 @@ impl Helper { "Failed" } }; - let uptime = HumanTime::into_human(start.elapsed()); + let uptime = Uptime::from(start.elapsed()); info!( "XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status @@ -469,7 +472,7 @@ impl Helper { "Unknown Error" } }; - let uptime = HumanTime::into_human(start.elapsed()); + let uptime = Uptime::from(start.elapsed()); info!( "XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status @@ -598,13 +601,13 @@ impl ImgXmrig { #[derive(Debug, Clone)] pub struct PubXmrigApi { pub output: String, - pub uptime: HumanTime, + pub uptime: Duration, pub worker_id: String, - pub resources: HumanNumber, - pub hashrate: HumanNumber, - pub diff: HumanNumber, - pub accepted: HumanNumber, - pub rejected: HumanNumber, + pub resources: String, + pub hashrate: String, + pub diff: String, + pub accepted: String, + pub rejected: String, pub hashrate_raw: f32, } @@ -619,13 +622,13 @@ impl PubXmrigApi { pub fn new() -> Self { Self { output: String::new(), - uptime: HumanTime::new(), - worker_id: "???".to_string(), - resources: HumanNumber::unknown(), - hashrate: HumanNumber::unknown(), - diff: HumanNumber::unknown(), - accepted: HumanNumber::unknown(), - rejected: HumanNumber::unknown(), + uptime: Duration::from_secs(0), + worker_id: UNKNOWN_DATA.to_string(), + resources: UNKNOWN_DATA.to_string(), + hashrate: UNKNOWN_DATA.to_string(), + diff: UNKNOWN_DATA.to_string(), + accepted: UNKNOWN_DATA.to_string(), + rejected: UNKNOWN_DATA.to_string(), hashrate_raw: 0.0, } } @@ -661,7 +664,7 @@ impl PubXmrigApi { public.output.push_str(&std::mem::take(&mut *output_pub)); } // Update uptime - public.uptime = HumanTime::into_human(elapsed); + public.uptime = elapsed; } // 2. Check for "new job"/"no active...". @@ -687,11 +690,11 @@ impl PubXmrigApi { *public = Self { worker_id: private.worker_id, - resources: HumanNumber::from_load(private.resources.load_average), - hashrate: HumanNumber::from_hashrate(private.hashrate.total), - diff: HumanNumber::from_u128(private.connection.diff), - accepted: HumanNumber::from_u128(private.connection.accepted), - rejected: HumanNumber::from_u128(private.connection.rejected), + resources: HumanNumber::from_load(private.resources.load_average).to_string(), + hashrate: HumanNumber::from_hashrate(private.hashrate.total).to_string(), + diff: Unsigned::from(private.connection.diff as usize).to_string(), + accepted: Unsigned::from(private.connection.accepted as usize).to_string(), + rejected: Unsigned::from(private.connection.rejected as usize).to_string(), hashrate_raw, ..std::mem::take(&mut *public) } diff --git a/src/helper/xvb.rs b/src/helper/xvb.rs index 6d2b918..9d59b4b 100644 --- a/src/helper/xvb.rs +++ b/src/helper/xvb.rs @@ -5,6 +5,7 @@ use hyper::client::HttpConnector; use hyper::StatusCode; use hyper_tls::HttpsConnector; use log::{debug, error, info, warn}; +use readable::up::Uptime; use serde::Deserialize; use std::fmt::Write; use std::{ @@ -13,16 +14,16 @@ use std::{ time::Instant, }; -use crate::utils::constants::XVB_URL; +use crate::utils::constants::{XVB_PUBLIC_ONLY, XVB_URL}; use crate::{ helper::{ProcessSignal, ProcessState}, utils::{ constants::{HORI_CONSOLE, XVB_URL_PUBLIC_API}, - human::HumanTime, macros::{lock, lock2, sleep}, }, }; +use super::p2pool::PubP2poolApi; use super::{Helper, Process}; impl Helper { @@ -31,11 +32,6 @@ impl Helper { info!("XvB | Attempting to stop..."); lock2!(helper, xvb).signal = ProcessSignal::Stop; lock2!(helper, xvb).state = ProcessState::Middle; - // Reset stats - let gui_api = Arc::clone(&lock!(helper).gui_api_xvb); - let pub_api = Arc::clone(&lock!(helper).pub_api_xvb); - *lock!(pub_api) = PubXvbApi::new(); - *lock!(gui_api) = PubXvbApi::new(); } pub fn restart_xvb( helper: &Arc>, @@ -72,9 +68,15 @@ impl Helper { let gui_api = Arc::clone(&lock!(helper).gui_api_xvb); let pub_api = Arc::clone(&lock!(helper).pub_api_xvb); let process = Arc::clone(&lock!(helper).xvb); + // needed to see if it is alive. For XvB process to function completely, p2pool node must be alive to check the shares in the pplns window. + let process_p2pool = Arc::clone(&lock!(helper).p2pool); + let gui_api_p2pool = Arc::clone(&lock!(helper).gui_api_p2pool); let state_xvb_check = state_xvb.clone(); let state_p2pool_check = state_p2pool.clone(); - + // Reset before printing to output. + // Need to reset because values of stats would stay otherwise which could bring confusion even if panel is with a disabled theme. + *lock!(pub_api) = PubXvbApi::new(); + *lock!(gui_api) = PubXvbApi::new(); // 2. Set process state debug!("XvB | Setting process state..."); { @@ -96,16 +98,40 @@ impl Helper { } Err(err) => { // send to console: token non existent for address on XvB server - warn!("Xvb | Start ... Failed because token and associated address are not existent on XvB server: {}", err); + warn!("Xvb | Start ... Partially failed because token and associated address are not existent on XvB server: {}\n", err); // output the error to console if let Err(e) = writeln!( lock!(gui_api).output, - "Failure to retrieve private stats from XvB server.\nError: {}", - err + "Failure to retrieve private stats from XvB server.\nError: {}\n{}\n", + err, + XVB_PUBLIC_ONLY ) { error!("XvB Watchdog | GUI status write failed: {}", e); } - lock2!(helper, xvb).state = ProcessState::NotMining; + lock!(process).state = ProcessState::NotMining; + } + } + if !lock!(process_p2pool).is_alive() { + // send to console: p2pool process is not running + warn!("Xvb | Start ... Partially failed because P2pool instance is not running."); + // output the error to console + if let Err(e) = writeln!( + lock!(gui_api).output, + "\nFailure to completely start XvB process because p2pool instance is not running.\n", + ) { + error!("XvB Watchdog | GUI status write failed: {}", e); + } + + lock!(process).state = ProcessState::Syncing; + } + if lock!(process).state != ProcessState::Alive { + if let Err(e) = writeln!(lock!(gui_api).output, "\n{}\n", XVB_PUBLIC_ONLY,) { + error!("XvB Watchdog | GUI status write failed: {}", e); + } + } else { + info!("XvB started"); + if let Err(e) = writeln!(lock!(gui_api).output, "\nXvB started\n") { + error!("XvB Watchdog | GUI status write failed: {}", e); } } let state_xvb_thread = state_xvb.clone(); @@ -118,6 +144,8 @@ impl Helper { process, &state_xvb_thread, &state_p2pool_thread, + gui_api_p2pool, + process_p2pool, ); }); } @@ -129,21 +157,57 @@ impl Helper { process: Arc>, state_xvb: &crate::disk::state::Xvb, state_p2pool: &crate::disk::state::P2pool, + gui_api_p2pool: Arc>, + process_p2pool: Arc>, ) { - info!("XvB started"); - - if let Err(e) = writeln!( - lock!(gui_api).output, - "{}\nXvb started\n{}\n\n", - HORI_CONSOLE, - HORI_CONSOLE - ) { - error!("XvB Watchdog | GUI status write failed: {}", e); - } + // see how many shares are found at p2pool node only if XvB is started successfully. If it wasn't, maybe P2pool is node not running. + let mut old_shares = if lock!(process).state == ProcessState::Alive { + // a loop until the value is some to let p2pool work and get first value. + loop { + if let Some(s) = lock!(gui_api_p2pool).shares_found { + break s; + } + std::thread::sleep(std::time::Duration::from_secs(1)); + } + } else { + // if Syncing state, this value is not needed + 0 + }; + // let mut old_shares = 0; + let mut time_last_share: Option = None; let start = lock!(process).start; info!("XvB | Entering watchdog mode... woof!"); loop { debug!("XvB Watchdog | ----------- Start of loop -----------"); + // verify if p2pool node is running with correct token. + if lock!(process).state != ProcessState::NotMining { + if lock!(process_p2pool).is_alive() { + // verify if state is to changed + if lock!(process).state == ProcessState::Syncing { + info!("XvB | started this time with p2pool"); + lock!(process).state = ProcessState::Alive; + if let Err(e) = writeln!( + lock!(gui_api).output, + "\nXvB is now started because p2pool node came online.\n", + ) { + error!("XvB Watchdog | GUI status write failed: {}", e); + } + } + } else { + // verify if the state is changing because p2pool is not alive anymore. + if lock!(process).state != ProcessState::Syncing { + info!("XvB | stop partially because p2pool is not alive anymore."); + lock!(process).state = ProcessState::Alive; + if let Err(e) = writeln!( + lock!(gui_api).output, + "\nXvB is now partially stopped because p2pool node came offline.\n", + ) { + error!("XvB Watchdog | GUI status write failed: {}", e); + } + lock!(process).state = ProcessState::Syncing + } + } + } // Set timer let now = Instant::now(); // check signal @@ -151,6 +215,7 @@ impl Helper { if signal_interrupt(process.clone(), start, gui_api.clone()) { break; } + // verify if // Send an HTTP API request only if one minute is passed since the last. // if since is 0, send request because it's the first time. let since = lock!(gui_api).tick; @@ -179,44 +244,86 @@ impl Helper { } } debug!("XvB Watchdog | Attempting HTTP private API request..."); - match XvbPrivStats::request_api(&state_p2pool.address, &state_xvb.token).await { - Ok(b) => { - debug!("XvB Watchdog | HTTP API request OK"); - let new_data = match serde_json::from_slice::(&b) { - Ok(data) => data, - Err(e) => { - warn!("XvB Watchdog | Data provided from private API is not deserializ-able.Error: {}", e); - // output the error to console - if let Err(e) = writeln!( + // only if private API is accessible, NotMining here means that the token and address is not registered on the XvB website. + if lock!(process).state == ProcessState::Alive { + // reload private stats + match XvbPrivStats::request_api(&state_p2pool.address, &state_xvb.token).await { + Ok(b) => { + debug!("XvB Watchdog | HTTP API request OK"); + let new_data = match serde_json::from_slice::(&b) { + Ok(data) => data, + Err(e) => { + warn!("XvB Watchdog | Data provided from private API is not deserializ-able.Error: {}", e); + // output the error to console + if let Err(e) = writeln!( lock!(gui_api).output, "XvB Watchdog | Data provided from private API is not deserializ-able.Error: {}", e ) { error!("XvB Watchdog | GUI status write failed: {}", e); } - break; - } - }; - lock!(&pub_api).stats_priv = new_data; - } - Err(err) => { - warn!( + break; + } + }; + lock!(&pub_api).stats_priv = new_data; + } + Err(err) => { + warn!( "XvB Watchdog | Could not send HTTP private API request to: {}\n:{}", XVB_URL, err ); - // output the error to console - if let Err(e) = writeln!( - lock!(gui_api).output, - "Failure to retrieve private stats from {}", - XVB_URL - ) { - error!("XvB Watchdog | GUI status write failed: {}", e); + // output the error to console + if let Err(e) = writeln!( + lock!(gui_api).output, + "Failure to retrieve private stats from {}", + XVB_URL + ) { + error!("XvB Watchdog | GUI status write failed: {}", e); + } + lock!(process).state = ProcessState::Failed; + break; } - lock!(process).state = ProcessState::Failed; - break; + } + // check if share is in pplns window + // p2pool local api show only found shares and not current shares. So we need to keep track of the time + // the height of p2pool would be nicer but the p2pool api doesn't show it. + let (share, new_time) = lock!(gui_api_p2pool) + .is_share_present_in_ppplns_window( + &mut old_shares, + time_last_share, + state_p2pool.mini, + ); + if let Some(n) = new_time { + time_last_share = Some(n); + } + + // // verify in which round type we are + let round = if share { + let stats_priv = &lock!(pub_api).stats_priv; + match ( + stats_priv.donor_1hr_avg / 1000.0, + stats_priv.donor_24hr_avg / 1000.0, + ) { + x if x.0 > 1000.0 && x.1 > 1000.0 => Some(XvbRound::DonorMega), + x if x.0 > 100.0 && x.1 > 100.0 => Some(XvbRound::DonorWhale), + x if x.0 > 10.0 && x.1 > 10.0 => Some(XvbRound::DonorVip), + x if x.0 > 1.0 && x.1 > 1.0 => Some(XvbRound::Donor), + (_, _) => Some(XvbRound::Vip), + } + } else { + None + }; + // refresh the round we participate + lock!(&pub_api).stats_priv.round_participate = round; + + // verify if we are the winner of the current round + if &lock!(pub_api).stats_pub.winner + == Helper::head_tail_of_monero_address(&state_p2pool.address).as_str() + { + lock!(pub_api).stats_priv.win_current = true } } - lock!(gui_api).tick += 0; + lock!(gui_api).tick = 0; } lock!(gui_api).tick += 1; @@ -240,7 +347,7 @@ use serde_this_or_that::as_u64; #[derive(Debug, Clone, Default)] pub struct PubXvbApi { pub output: String, - pub uptime: HumanTime, + pub uptime: u64, pub tick: u8, pub stats_pub: XvbPubStats, pub stats_priv: XvbPrivStats, @@ -323,6 +430,10 @@ pub struct XvbPrivStats { pub fails: u8, pub donor_1hr_avg: f32, pub donor_24hr_avg: f32, + #[serde(skip)] + pub win_current: bool, + #[serde(skip)] + pub round_participate: Option, } #[derive(Debug, Clone, Default, Display, Deserialize)] @@ -379,15 +490,18 @@ fn signal_interrupt( if lock!(process).signal == ProcessSignal::Stop { debug!("P2Pool Watchdog | Stop SIGNAL caught"); // Wait to get the exit status - let uptime = HumanTime::into_human(start.elapsed()); - info!("Xvb Watchdog | Stopped ... Uptime was: [{}]", uptime); + let uptime = start.elapsed(); + info!( + "Xvb Watchdog | Stopped ... Uptime was: [{}]", + Uptime::from(uptime) + ); // insert the signal into output of XvB // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. if let Err(e) = writeln!( lock!(gui_api).output, "{}\nXvb stopped | Uptime: [{}] | \n{}\n\n\n\n", HORI_CONSOLE, - uptime, + Uptime::from(uptime), HORI_CONSOLE ) { error!("XvB Watchdog | GUI Uptime/Exit status write failed: {}", e); @@ -399,7 +513,7 @@ fn signal_interrupt( // Check RESTART } else if lock!(process).signal == ProcessSignal::Restart { debug!("XvB Watchdog | Restart SIGNAL caught"); - let uptime = HumanTime::into_human(start.elapsed()); + let uptime = Uptime::from(start.elapsed()); info!("XvB Watchdog | Stopped ... Uptime was: [{}]", uptime); // no output to console because service will be started with fresh output. debug!("XvB Watchdog | Restart SIGNAL done, breaking"); diff --git a/src/utils/constants.rs b/src/utils/constants.rs index 894e5d1..0e5dc72 100644 --- a/src/utils/constants.rs +++ b/src/utils/constants.rs @@ -107,7 +107,7 @@ pub const XVB_DEAD: &str = "XvB process is offline"; pub const XVB_FAILED: &str = "XvB process is misconfigured or the XvB node is offline"; pub const XVB_MIDDLE: &str = "XvB is in the middle of (re)starting/stopping"; pub const XVB_NOT_CONFIGURED: &str = "You need to insert an existent token before starting XvB"; -pub const XVB_PUBLIC_ONLY: &str = "XvB process is started only to get public stats"; +pub const XVB_PUBLIC_ONLY: &str = "XvB process is started only to get public stats."; // This is the typical space added when using // [ui.separator()] or [ui.group()] @@ -420,6 +420,8 @@ pub const XVB_TOKEN_FIELD: &str = "Token"; pub const XVB_FAILURE_FIELD: &str = "Failures"; pub const XVB_DONATED_1H_FIELD: &str = "Donated last hour"; pub const XVB_DONATED_24H_FIELD: &str = "Donated last 24 hours"; +pub const XVB_ROUND_TYPE_FIELD: &str = "Round"; +pub const XVB_WINNER_FIELD: &str = "Win"; // CLI argument messages pub const ARG_HELP: &str = r#"USAGE: ./gupax [--flag] @@ -443,6 +445,15 @@ pub const ARG_COPYRIGHT: &str = r#"Gupax is licensed under GPLv3. For more information, see link below: "#; +// Unknown Data, replace HumanNumlber::unknown() +pub const UNKNOWN_DATA: &str = "???"; +// Time PPLNS WINDOW in seconds +// it is an estimation based on number of block in a pplns window and block time (10s). The difficulty of the network should adapt to get close to this value. +pub const TIME_PPLNS_WINDOW_MINI: Duration = Duration::from_secs(2160 * 10); +pub const TIME_PPLNS_WINDOW_MAIN: Duration = Duration::from_secs(363 * 10); + +use std::time::Duration; + //---------------------------------------------------------------------------------------------------- Visuals use egui::epaint::{Rounding, Shadow, Stroke};