Status Submenu: Add [Status/P2Pool] UI and data

This commit is contained in:
hinto-janaiyo 2022-12-30 19:22:43 -05:00
parent 08cda22e68
commit e8751842ce
No known key found for this signature in database
GPG key ID: B1C5A64B80691E45
7 changed files with 161 additions and 22 deletions

View file

@ -482,7 +482,7 @@ You need [`cargo`](https://www.rust-lang.org/learn/get-started), Rust's build to
The `--release` profile in Gupax is set to prefer code performance & small binary sizes over compilation speed (see [`Cargo.toml`](https://github.com/hinto-janaiyo/gupax/blob/main/Cargo.toml)). Gupax itself (with all dependencies already built) takes around 1m30s to build (vs 10s on a normal `--release`) with a Ryzen 5950x.
There are `37` unit tests throughout the codebase files, you should probably run:
There are `38` unit tests throughout the codebase files, you should probably run:
```
cargo test
```

View file

@ -176,11 +176,30 @@ pub const STATUS_SUBMENU_PROCESSES: &str = "View the status of process related d
pub const STATUS_SUBMENU_P2POOL: &str = "View P2Pool specific data";
pub const STATUS_SUBMENU_MONERO: &str = "View general Monero blockchain data";
pub const STATUS_SUBMENU_PAYOUT: &str = "The total amount of payouts received via P2Pool across all time";
//-- P2Pool
pub const STATUS_SUBMENU_XMR: &str = "The total of XMR mined via P2Pool across all time";
pub const STATUS_SUBMENU_LATEST: &str = "Sort the logs latest to oldest";
pub const STATUS_SUBMENU_OLDEST: &str = "Sort the logs oldest to latest";
pub const STATUS_SUBMENU_BIGGEST: &str = "Sort the logs by the biggest payouts first";
pub const STATUS_SUBMENU_SMALLEST: &str = "Sort the logs by the smallest payouts first";
pub const STATUS_SUBMENU_AUTOMATIC: &str = "Automatically calculate share/block time with your current P2Pool 1 hour average hashrate";
pub const STATUS_SUBMENU_MANUAL: &str = "Manually input a hashrate to calculate share/block time with current P2Pool/Monero network stats";
pub const STATUS_SUBMENU_HASH: &str = "Use [Hash] as the hashrate metric";
pub const STATUS_SUBMENU_KILO: &str = "Use [Kilo] as the hashrate metric (1,000x hash)";
pub const STATUS_SUBMENU_MEGA: &str = "Use [Mega] as the hashrate metric (1,000,000x hash)";
pub const STATUS_SUBMENU_GIGA: &str = "Use [Giga] as the hashrate metric (1,000,000,000x hash)";
pub const STATUS_SUBMENU_P2POOL_BLOCK_MEAN: &str = "The average time it takes for P2Pool to find a block";
pub const STATUS_SUBMENU_YOUR_P2POOL_HASHRATE: &str = "Your 1 hour average hashrate on P2Pool";
pub const STATUS_SUBMENU_P2POOL_SHARE_MEAN: &str = "The average time it takes for your hashrate to find a share on P2Pool";
pub const STATUS_SUBMENU_SOLO_BLOCK_MEAN: &str = "The average time it would take for your hashrate to find a block solo mining Monero";
pub const STATUS_SUBMENU_MONERO_DIFFICULTY: &str = "The current Monero network's difficulty (how many hashes it will take on average to find a block)";
pub const STATUS_SUBMENU_MONERO_HASHRATE: &str = "The current Monero network's hashrate";
pub const STATUS_SUBMENU_P2POOL_DIFFICULTY: &str = "The current P2Pool network's difficulty (how many hashes it will take on average to find a share)";
pub const STATUS_SUBMENU_P2POOL_HASHRATE: &str = "The current P2Pool network's hashrate";
pub const STATUS_SUBMENU_P2POOL_MINERS: &str = "The current amount of miners on P2Pool";
pub const STATUS_SUBMENU_P2POOL_DOMINANCE: &str = "The percent of hashrate P2Pool accounts for in the entire Monero network";
pub const STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE: &str = "The percent of hashrate you account for in P2Pool";
pub const STATUS_SUBMENU_YOUR_MONERO_DOMINANCE: &str = "The percent of hashrate you account for in the entire Monero network";
// Gupax
pub const GUPAX_UPDATE: &str = "Check for updates on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically";

View file

@ -239,6 +239,12 @@ impl State {
}
}
pub fn update_absolute_path(&mut self) -> Result<(), TomlError> {
self.gupax.absolute_p2pool_path = into_absolute_path(self.gupax.p2pool_path.clone())?;
self.gupax.absolute_xmrig_path = into_absolute_path(self.gupax.xmrig_path.clone())?;
Ok(())
}
// Convert [&str] to [State]
pub fn from_str(string: &str) -> Result<Self, TomlError> {
match toml::de::from_str(string) {
@ -869,6 +875,15 @@ impl Default for Hash {
}
impl Hash {
pub fn convert_to_hash(f: f64, from: Self) -> f64 {
match from {
Self::Hash => f,
Self::Kilo => f * 1_000.0,
Self::Mega => f * 1_000_000.0,
Self::Giga => f * 1_000_000_000.0,
}
}
pub fn convert(f: f64, og: Self, new: Self) -> f64 {
match og {
Self::Hash => {
@ -1034,7 +1049,7 @@ impl Default for Status {
payout_view: PayoutView::default(),
monero_enabled: false,
manual_hash: false,
hashrate: 0.0,
hashrate: 1.0,
hash_metric: Hash::default(),
}
}
@ -1064,6 +1079,7 @@ impl Default for Gupax {
}
}
}
impl Default for P2pool {
fn default() -> Self {
Self {

View file

@ -1239,17 +1239,18 @@ pub struct PubP2poolApi {
pub average_effort: HumanNumber,
pub current_effort: HumanNumber,
pub connections: HumanNumber,
// The API below needs a raw int [hashrate] to go off of and
// The API needs a raw ints to go off of and
// there's not a good way to access it without doing weird
// [Arc<Mutex>] shenanigans, so the raw [hashrate_1h] is
// copied here instead.
pub hashrate: u64,
// [Arc<Mutex>] shenanigans, so some raw ints are stored here.
pub user_p2pool_hashrate_u64: u64,
pub p2pool_difficulty_u64: u64,
pub monero_difficulty_u64: u64,
// Network API
pub monero_difficulty: HumanNumber, // e.g: [15,000,000]
pub monero_hashrate: HumanNumber, // e.g: [1.000 GH/s]
pub hash: String,
pub hash: String, // Current block hash
pub height: HumanNumber,
pub reward: u64, // Atomic units
pub reward: AtomicUnit,
// Pool API
pub p2pool_difficulty: HumanNumber,
pub p2pool_hashrate: HumanNumber,
@ -1290,12 +1291,14 @@ impl PubP2poolApi {
average_effort: HumanNumber::unknown(),
current_effort: HumanNumber::unknown(),
connections: HumanNumber::unknown(),
hashrate: 0,
user_p2pool_hashrate_u64: 0,
p2pool_difficulty_u64: 0,
monero_difficulty_u64: 0,
monero_difficulty: HumanNumber::unknown(),
monero_hashrate: HumanNumber::unknown(),
hash: String::from("???"),
height: HumanNumber::unknown(),
reward: 0,
reward: AtomicUnit::new(),
p2pool_difficulty: HumanNumber::unknown(),
p2pool_hashrate: HumanNumber::unknown(),
miners: HumanNumber::unknown(),
@ -1407,14 +1410,14 @@ impl PubP2poolApi {
average_effort: HumanNumber::to_percent(local.average_effort),
current_effort: HumanNumber::to_percent(local.current_effort),
connections: HumanNumber::from_u16(local.connections),
hashrate: local.hashrate_1h,
user_p2pool_hashrate_u64: local.hashrate_1h,
..std::mem::take(&mut *public)
};
}
// Mutate [PubP2poolApi] with data from a [PrivP2pool(Network|Pool)Api].
fn update_from_network_pool(public: &Arc<Mutex<Self>>, net: PrivP2poolNetworkApi, pool: PrivP2poolPoolApi) {
let user_hashrate = lock!(public).hashrate; // The user's total P2Pool hashrate
let user_hashrate = lock!(public).user_p2pool_hashrate_u64; // The user's total P2Pool hashrate
let monero_difficulty = net.difficulty;
let monero_hashrate = monero_difficulty / MONERO_BLOCK_TIME_IN_SECONDS;
let p2pool_hashrate = pool.pool_statistics.hashRate;
@ -1452,11 +1455,13 @@ impl PubP2poolApi {
}
let mut public = lock!(public);
*public = Self {
p2pool_difficulty_u64: p2pool_difficulty,
monero_difficulty_u64: monero_difficulty,
monero_difficulty: HumanNumber::from_u64(monero_difficulty),
monero_hashrate: HumanNumber::from_u64_to_gigahash_3_point(monero_hashrate),
hash: net.hash,
height: HumanNumber::from_u32(net.height),
reward: net.reward,
reward: AtomicUnit::from_u64(net.reward),
p2pool_difficulty: HumanNumber::from_u64(p2pool_difficulty),
p2pool_hashrate: HumanNumber::from_u64_to_megahash_3_point(p2pool_hashrate),
miners: HumanNumber::from_u32(pool.pool_statistics.miners),
@ -1469,6 +1474,14 @@ impl PubP2poolApi {
..std::mem::take(&mut *public)
};
}
pub fn calculate_share_or_block_time(hashrate: u64, difficulty: u64) -> HumanTime {
if hashrate == 0 {
HumanTime::new()
} else {
HumanTime::from_u64(difficulty / hashrate)
}
}
}
//---------------------------------------------------------------------------------------------------- Private P2Pool "Local" Api
@ -1889,7 +1902,7 @@ mod test {
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");
assert_eq!(p.hashrate, 20000);
assert_eq!(p.user_p2pool_hashrate_u64, 20000);
drop(p);
// Update Network + Pool
PubP2poolApi::update_from_network_pool(&public, network, pool);
@ -1899,7 +1912,7 @@ mod test {
assert_eq!(p.monero_hashrate.to_string(), "2.500 GH/s");
assert_eq!(p.hash.to_string(), "asdf");
assert_eq!(p.height.to_string(), "1,234");
assert_eq!(p.reward, 2345);
assert_eq!(p.reward.to_u64(), 2345);
assert_eq!(p.p2pool_difficulty.to_string(), "10,000,000");
assert_eq!(p.p2pool_hashrate.to_string(), "1.000 MH/s");
assert_eq!(p.miners.to_string(), "1,000");

View file

@ -44,6 +44,10 @@ impl HumanTime {
HumanTime(d)
}
pub const fn from_u64(u: u64) -> HumanTime {
HumanTime(Duration::from_secs(u))
}
fn plural(f: &mut std::fmt::Formatter, started: &mut bool, name: &str, value: u64) -> std::fmt::Result {
if value > 0 {
if *started {
@ -216,6 +220,9 @@ impl HumanNumber {
let f = format!("{}", f);
Self(f)
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
//---------------------------------------------------------------------------------------------------- TESTS

View file

@ -1520,6 +1520,8 @@ impl eframe::App for App {
});
} else if p2pool_is_alive {
if key.is_up() && !wants_input || ui.add_sized([width, height], Button::new("")).on_hover_text("Restart P2Pool").clicked() {
lock!(self.og).update_absolute_path();
self.state.update_absolute_path();
Helper::restart_p2pool(&self.helper, &self.state.p2pool, &self.state.gupax.absolute_p2pool_path);
}
if key.is_down() && !wants_input || ui.add_sized([width, height], Button::new("")).on_hover_text("Stop P2Pool").clicked() {
@ -1549,6 +1551,8 @@ impl eframe::App for App {
ui.set_enabled(ui_enabled);
let color = if ui_enabled { GREEN } else { RED };
if (ui_enabled && key.is_up() && !wants_input) || ui.add_sized([width, height], Button::new(RichText::new("").color(color))).on_hover_text("Start P2Pool").on_disabled_hover_text(text).clicked() {
lock!(self.og).update_absolute_path();
self.state.update_absolute_path();
Helper::start_p2pool(&self.helper, &self.state.p2pool, &self.state.gupax.absolute_p2pool_path);
}
}
@ -1575,6 +1579,8 @@ impl eframe::App for App {
});
} else if xmrig_is_alive {
if key.is_up() && !wants_input || ui.add_sized([width, height], Button::new("")).on_hover_text("Restart XMRig").clicked() {
lock!(self.og).update_absolute_path();
self.state.update_absolute_path();
if cfg!(windows) {
Helper::restart_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path, Arc::clone(&self.sudo));
} else {
@ -1609,16 +1615,17 @@ impl eframe::App for App {
}
ui.set_enabled(ui_enabled);
let color = if ui_enabled { GREEN } else { RED };
#[cfg(target_os = "windows")]
if (ui_enabled && key.is_up() && !wants_input) || ui.add_sized([width, height], Button::new(RichText::new("").color(color))).on_hover_text("Start XMRig").on_disabled_hover_text(text).clicked() {
lock!(self.og).update_absolute_path();
self.state.update_absolute_path();
if cfg!(windows) {
Helper::start_xmrig(&self.helper, &self.state.xmrig, &self.state.gupax.absolute_xmrig_path, Arc::clone(&self.sudo));
}
#[cfg(target_family = "unix")]
if (ui_enabled && key.is_up() && !wants_input) || ui.add_sized([width, height], Button::new(RichText::new("").color(color))).on_hover_text("Start XMRig").on_disabled_hover_text(text).clicked() {
} else if cfg!(unix) {
lock!(self.sudo).signal = ProcessSignal::Start;
self.error_state.ask_sudo(&self.sudo);
}
}
}
});
},
_ => (),

View file

@ -27,6 +27,7 @@ use crate::{
macros::*,
GupaxP2poolApi,
PayoutView,
human::HumanNumber,
};
use std::sync::{Arc,Mutex};
use log::*;
@ -36,6 +37,7 @@ use egui::{
TextStyle::Name,
TextEdit,
SelectableLabel,
Slider,
};
impl crate::disk::Status {
@ -172,6 +174,81 @@ pub fn show(&mut self, sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolAp
});
});
drop(api);
// Payout/Share Calculator
let button = (width/20.0)-(SPACE*1.666);
ui.group(|ui| { ui.horizontal(|ui| {
ui.set_min_width(width-SPACE);
if ui.add_sized([button*2.0, text], SelectableLabel::new(self.manual_hash == false, "Automatic")).on_hover_text(STATUS_SUBMENU_AUTOMATIC).clicked() {self.manual_hash = false; }
ui.separator();
if ui.add_sized([button*2.0, text], SelectableLabel::new(self.manual_hash == true, "Manual")).on_hover_text(STATUS_SUBMENU_MANUAL).clicked() { self.manual_hash = true; }
ui.separator();
ui.set_enabled(self.manual_hash);
if ui.add_sized([button, text], SelectableLabel::new(self.hash_metric == Hash::Hash, "Hash")).on_hover_text(STATUS_SUBMENU_HASH).clicked() { self.hash_metric = Hash::Hash; }
ui.separator();
if ui.add_sized([button, text], SelectableLabel::new(self.hash_metric == Hash::Kilo, "Kilo")).on_hover_text(STATUS_SUBMENU_KILO).clicked() { self.hash_metric = Hash::Kilo; }
ui.separator();
if ui.add_sized([button, text], SelectableLabel::new(self.hash_metric == Hash::Mega, "Mega")).on_hover_text(STATUS_SUBMENU_MEGA).clicked() { self.hash_metric = Hash::Mega; }
ui.separator();
if ui.add_sized([button, text], SelectableLabel::new(self.hash_metric == Hash::Giga, "Giga")).on_hover_text(STATUS_SUBMENU_GIGA).clicked() { self.hash_metric = Hash::Giga; }
ui.separator();
ui.spacing_mut().slider_width = button*12.5;
ui.add_sized([button*14.0, text], Slider::new(&mut self.hashrate, 1.0..=1_000.0));
})});
let api = lock!(p2pool_api);
ui.set_enabled(p2pool_alive);
let text = height / 25.0;
let width = (width/3.0)-(SPACE*1.666);
let min_height = ui.available_height()/1.25;
ui.horizontal(|ui| {
ui.group(|ui| { ui.vertical(|ui| {
ui.set_min_height(min_height);
if self.manual_hash {
let hashrate = Hash::convert_to_hash(self.hashrate, self.hash_metric) as u64;
let p2pool_share_mean = PubP2poolApi::calculate_share_or_block_time(hashrate, api.p2pool_difficulty_u64);
let solo_block_mean = PubP2poolApi::calculate_share_or_block_time(hashrate, api.monero_difficulty_u64);
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_block_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Hashrate").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE);
ui.add_sized([width, text], Label::new(format!("{} H/s", HumanNumber::from_u64(hashrate))));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Share Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
ui.add_sized([width, text], Label::new(p2pool_share_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your Solo Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(solo_block_mean.to_string()));
} else {
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_block_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Hashrate").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE);
ui.add_sized([width, text], Label::new(format!("{} H/s", api.hashrate_1h)));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Share Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_share_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your Solo Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(api.solo_block_mean.to_string()));
}
})});
ui.group(|ui| { ui.vertical(|ui| {
ui.set_min_height(min_height);
ui.add_sized([width, text], Label::new(RichText::new("Monero Difficulty").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_MONERO_DIFFICULTY);
ui.add_sized([width, text], Label::new(api.monero_difficulty.as_str()));
ui.add_sized([width, text], Label::new(RichText::new("Monero Hashrate").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_MONERO_HASHRATE);
ui.add_sized([width, text], Label::new(api.monero_hashrate.as_str()));
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Difficulty").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_DIFFICULTY);
ui.add_sized([width, text], Label::new(api.p2pool_difficulty.as_str()));
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Hashrate").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_HASHRATE);
ui.add_sized([width, text], Label::new(api.p2pool_hashrate.as_str()));
})});
ui.group(|ui| { ui.vertical(|ui| {
ui.set_min_height(min_height);
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Miners").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_MINERS);
ui.add_sized([width, text], Label::new(api.miners.as_str()));
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Dominance").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE);
ui.add_sized([width, text], Label::new(api.p2pool_percent.as_str()));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Dominance").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE);
ui.add_sized([width, text], Label::new(api.user_p2pool_percent.as_str()));
ui.add_sized([width, text], Label::new(RichText::new("Your Monero Dominance").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE);
ui.add_sized([width, text], Label::new(api.user_monero_percent.as_str()));
})});
});
drop(api);
//---------------------------------------------------------------------------------------------------- [Monero]
} else if self.submenu == Submenu::Monero {
}