mirror of
https://github.com/hinto-janai/gupax.git
synced 2024-12-22 10:59:21 +00:00
cargo fmt
This commit is contained in:
parent
1810e135a2
commit
abaaf72e4d
19 changed files with 10362 additions and 7852 deletions
6
build.rs
6
build.rs
|
@ -14,7 +14,8 @@ fn main() -> std::io::Result<()> {
|
||||||
// This sets the [Run as Administrator] metadata flag for Windows.
|
// This sets the [Run as Administrator] metadata flag for Windows.
|
||||||
// Why do I do this?: [https://github.com/hinto-janai/gupax/tree/main/src#why-does-gupax-need-to-be-admin-on-windows]
|
// Why do I do this?: [https://github.com/hinto-janai/gupax/tree/main/src#why-does-gupax-need-to-be-admin-on-windows]
|
||||||
// TL;DR: Because Windows.
|
// TL;DR: Because Windows.
|
||||||
res.set_manifest(r#"
|
res.set_manifest(
|
||||||
|
r#"
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
<security>
|
<security>
|
||||||
|
@ -24,7 +25,8 @@ fn main() -> std::io::Result<()> {
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
</assembly>
|
</assembly>
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
res.compile()
|
res.compile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
168
src/constants.rs
168
src/constants.rs
|
@ -19,8 +19,8 @@ pub const GUPAX_VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); // e.g
|
||||||
pub const P2POOL_VERSION: &str = "v3.10";
|
pub const P2POOL_VERSION: &str = "v3.10";
|
||||||
pub const XMRIG_VERSION: &str = "v6.21.1";
|
pub const XMRIG_VERSION: &str = "v6.21.1";
|
||||||
pub const COMMIT: &str = env!("COMMIT"); // set in build.rs
|
pub const COMMIT: &str = env!("COMMIT"); // set in build.rs
|
||||||
// e.g: Gupax_v1_0_0
|
// e.g: Gupax_v1_0_0
|
||||||
// Would have been [Gupax_v1.0.0] but P2Pool truncates everything after [.]
|
// Would have been [Gupax_v1.0.0] but P2Pool truncates everything after [.]
|
||||||
pub const GUPAX_VERSION_UNDERSCORE: &str = concat!(
|
pub const GUPAX_VERSION_UNDERSCORE: &str = concat!(
|
||||||
"Gupax_v",
|
"Gupax_v",
|
||||||
env!("CARGO_PKG_VERSION_MAJOR"),
|
env!("CARGO_PKG_VERSION_MAJOR"),
|
||||||
|
@ -45,8 +45,7 @@ pub const APP_DEFAULT_SCALE: f32 = 1.0;
|
||||||
|
|
||||||
// Constants specific for Linux distro packaging of Gupax
|
// Constants specific for Linux distro packaging of Gupax
|
||||||
#[cfg(feature = "distro")]
|
#[cfg(feature = "distro")]
|
||||||
pub const DISTRO_NO_UPDATE: &str =
|
pub const DISTRO_NO_UPDATE: &str = r#"This [Gupax] was compiled for use as a Linux distro package. Built-in updates are disabled. The below settings [Update-via-Tor] & [Auto-Update] will not do anything. Please use your package manager to update [Gupax/P2Pool/XMRig]."#;
|
||||||
r#"This [Gupax] was compiled for use as a Linux distro package. Built-in updates are disabled. The below settings [Update-via-Tor] & [Auto-Update] will not do anything. Please use your package manager to update [Gupax/P2Pool/XMRig]."#;
|
|
||||||
|
|
||||||
// Use macOS shaped icon for macOS
|
// Use macOS shaped icon for macOS
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
@ -58,8 +57,7 @@ pub const HORIZONTAL: &str = "--------------------------------------------";
|
||||||
pub const HORI_CONSOLE: &str = "---------------------------------------------------------------------------------------------------------------------------";
|
pub const HORI_CONSOLE: &str = "---------------------------------------------------------------------------------------------------------------------------";
|
||||||
|
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
pub const KEYBOARD_SHORTCUTS: &str =
|
pub const KEYBOARD_SHORTCUTS: &str = r#"*---------------------------------------*
|
||||||
r#"*---------------------------------------*
|
|
||||||
| Key shortcuts |
|
| Key shortcuts |
|
||||||
|---------------------------------------|
|
|---------------------------------------|
|
||||||
| F11 | Fullscreen |
|
| F11 | Fullscreen |
|
||||||
|
@ -93,7 +91,8 @@ pub const P2POOL_ALIVE: &str = "P2Pool is online and fully synchronized";
|
||||||
pub const P2POOL_DEAD: &str = "P2Pool is offline";
|
pub const P2POOL_DEAD: &str = "P2Pool is offline";
|
||||||
pub const P2POOL_FAILED: &str = "P2Pool is offline and failed when exiting";
|
pub const P2POOL_FAILED: &str = "P2Pool is offline and failed when exiting";
|
||||||
pub const P2POOL_MIDDLE: &str = "P2Pool is in the middle of (re)starting/stopping";
|
pub const P2POOL_MIDDLE: &str = "P2Pool is in the middle of (re)starting/stopping";
|
||||||
pub const P2POOL_SYNCING: &str = "P2Pool is still syncing. This indicator will turn GREEN when P2Pool is ready";
|
pub const P2POOL_SYNCING: &str =
|
||||||
|
"P2Pool is still syncing. This indicator will turn GREEN when P2Pool is ready";
|
||||||
|
|
||||||
pub const XMRIG_ALIVE: &str = "XMRig is online and mining";
|
pub const XMRIG_ALIVE: &str = "XMRig is online and mining";
|
||||||
pub const XMRIG_DEAD: &str = "XMRig is offline";
|
pub const XMRIG_DEAD: &str = "XMRig is offline";
|
||||||
|
@ -125,15 +124,13 @@ pub const DARK_GRAY: egui::Color32 = egui::Color32::from_gray(13);
|
||||||
pub const SECOND: std::time::Duration = std::time::Duration::from_secs(1);
|
pub const SECOND: std::time::Duration = std::time::Duration::from_secs(1);
|
||||||
|
|
||||||
// The explanation given to the user on why XMRig needs sudo.
|
// The explanation given to the user on why XMRig needs sudo.
|
||||||
pub const XMRIG_ADMIN_REASON: &str =
|
pub const XMRIG_ADMIN_REASON: &str = r#"The large hashrate difference between XMRig and other miners like Monero and P2Pool's built-in miners is mostly due to XMRig configuring CPU MSRs and setting up hugepages. Other miners like Monero or P2Pool's built-in miner do not do this. It can be done manually but it isn't recommended since XMRig does this for you automatically, but only if it has the proper admin privileges."#;
|
||||||
r#"The large hashrate difference between XMRig and other miners like Monero and P2Pool's built-in miners is mostly due to XMRig configuring CPU MSRs and setting up hugepages. Other miners like Monero or P2Pool's built-in miner do not do this. It can be done manually but it isn't recommended since XMRig does this for you automatically, but only if it has the proper admin privileges."#;
|
|
||||||
// Password buttons
|
// Password buttons
|
||||||
pub const PASSWORD_TEXT: &str = "Enter sudo/admin password...";
|
pub const PASSWORD_TEXT: &str = "Enter sudo/admin password...";
|
||||||
pub const PASSWORD_LEAVE: &str = "Return to the previous screen";
|
pub const PASSWORD_LEAVE: &str = "Return to the previous screen";
|
||||||
pub const PASSWORD_ENTER: &str = "Attempt with the current password";
|
pub const PASSWORD_ENTER: &str = "Attempt with the current password";
|
||||||
pub const PASSWORD_HIDE: &str = "Toggle hiding/showing the password";
|
pub const PASSWORD_HIDE: &str = "Toggle hiding/showing the password";
|
||||||
|
|
||||||
|
|
||||||
// OS specific
|
// OS specific
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub const OS: &str = " Windows";
|
pub const OS: &str = " Windows";
|
||||||
|
@ -155,18 +152,22 @@ pub const OS_NAME: &str = "Linux";
|
||||||
// Tooltips
|
// Tooltips
|
||||||
// Status
|
// Status
|
||||||
pub const STATUS_GUPAX_UPTIME: &str = "How long Gupax has been online";
|
pub const STATUS_GUPAX_UPTIME: &str = "How long Gupax has been online";
|
||||||
pub const STATUS_GUPAX_CPU_USAGE: &str = "How much CPU Gupax is currently using. This accounts for all your threads (it is out of 100%)";
|
pub const STATUS_GUPAX_CPU_USAGE: &str =
|
||||||
|
"How much CPU Gupax is currently using. This accounts for all your threads (it is out of 100%)";
|
||||||
pub const STATUS_GUPAX_MEMORY_USAGE: &str = "How much memory Gupax is currently using in Megabytes";
|
pub const STATUS_GUPAX_MEMORY_USAGE: &str = "How much memory Gupax is currently using in Megabytes";
|
||||||
pub const STATUS_GUPAX_SYSTEM_CPU_USAGE: &str = "How much CPU your entire system is currently using. This accounts for all your threads (it is out of 100%)";
|
pub const STATUS_GUPAX_SYSTEM_CPU_USAGE: &str = "How much CPU your entire system is currently using. This accounts for all your threads (it is out of 100%)";
|
||||||
pub const STATUS_GUPAX_SYSTEM_MEMORY: &str = "How much memory your entire system has (including swap) and is currently using in Gigabytes";
|
pub const STATUS_GUPAX_SYSTEM_MEMORY: &str =
|
||||||
pub const STATUS_GUPAX_SYSTEM_CPU_MODEL: &str = "The detected model of your system's CPU and its current frequency";
|
"How much memory your entire system has (including swap) and is currently using in Gigabytes";
|
||||||
|
pub const STATUS_GUPAX_SYSTEM_CPU_MODEL: &str =
|
||||||
|
"The detected model of your system's CPU and its current frequency";
|
||||||
//--
|
//--
|
||||||
pub const STATUS_P2POOL_UPTIME: &str = "How long P2Pool has been online";
|
pub const STATUS_P2POOL_UPTIME: &str = "How long P2Pool has been online";
|
||||||
pub const STATUS_P2POOL_PAYOUTS: &str = "The total amount of payouts received in this instance of P2Pool and an extrapolated estimate of how many you will receive. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
pub const STATUS_P2POOL_PAYOUTS: &str = "The total amount of payouts received in this instance of P2Pool and an extrapolated estimate of how many you will receive. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
||||||
pub const STATUS_P2POOL_XMR: &str = "The total amount of XMR mined in this instance of P2Pool and an extrapolated estimate of how many you will mine in the future. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
pub const STATUS_P2POOL_XMR: &str = "The total amount of XMR mined in this instance of P2Pool and an extrapolated estimate of how many you will mine in the future. Warning: these stats will be quite inaccurate if your P2Pool hasn't been running for a long time!";
|
||||||
pub const STATUS_P2POOL_HASHRATE: &str = "The total amount of hashrate your P2Pool has pointed at it in 15 minute, 1 hour, and 24 hour averages";
|
pub const STATUS_P2POOL_HASHRATE: &str = "The total amount of hashrate your P2Pool has pointed at it in 15 minute, 1 hour, and 24 hour averages";
|
||||||
pub const STATUS_P2POOL_SHARES: &str = "The total amount of shares found on P2Pool";
|
pub const STATUS_P2POOL_SHARES: &str = "The total amount of shares found on P2Pool";
|
||||||
pub const STATUS_P2POOL_EFFORT: &str = "The average amount of effort needed to find a share, and the current effort";
|
pub const STATUS_P2POOL_EFFORT: &str =
|
||||||
|
"The average amount of effort needed to find a share, and the current effort";
|
||||||
pub const STATUS_P2POOL_CONNECTIONS: &str = "The total amount of miner connections on this P2Pool";
|
pub const STATUS_P2POOL_CONNECTIONS: &str = "The total amount of miner connections on this P2Pool";
|
||||||
pub const STATUS_P2POOL_MONERO_NODE: &str = "The Monero node being used by P2Pool";
|
pub const STATUS_P2POOL_MONERO_NODE: &str = "The Monero node being used by P2Pool";
|
||||||
pub const STATUS_P2POOL_POOL: &str = "The P2Pool sidechain you're currently connected to";
|
pub const STATUS_P2POOL_POOL: &str = "The P2Pool sidechain you're currently connected to";
|
||||||
|
@ -180,7 +181,8 @@ pub const STATUS_XMRIG_SHARES: &str = "The amount of accepted and rejected
|
||||||
pub const STATUS_XMRIG_POOL: &str = "The pool XMRig is currently mining to";
|
pub const STATUS_XMRIG_POOL: &str = "The pool XMRig is currently mining to";
|
||||||
pub const STATUS_XMRIG_THREADS: &str = "The amount of threads XMRig is currently using";
|
pub const STATUS_XMRIG_THREADS: &str = "The amount of threads XMRig is currently using";
|
||||||
// Status Submenus
|
// Status Submenus
|
||||||
pub const STATUS_SUBMENU_PROCESSES: &str = "View the status of process related data for [Gupax|P2Pool|XMRig]";
|
pub const STATUS_SUBMENU_PROCESSES: &str =
|
||||||
|
"View the status of process related data for [Gupax|P2Pool|XMRig]";
|
||||||
pub const STATUS_SUBMENU_P2POOL: &str = "View P2Pool specific data";
|
pub const STATUS_SUBMENU_P2POOL: &str = "View P2Pool specific data";
|
||||||
pub const STATUS_SUBMENU_HASHRATE: &str = "Compare your CPU hashrate with others";
|
pub const STATUS_SUBMENU_HASHRATE: &str = "Compare your CPU hashrate with others";
|
||||||
//-- P2Pool
|
//-- P2Pool
|
||||||
|
@ -190,32 +192,44 @@ pub const STATUS_SUBMENU_LATEST: &str = "Sort the payouts from latest to olde
|
||||||
pub const STATUS_SUBMENU_OLDEST: &str = "Sort the payouts from oldest to latest";
|
pub const STATUS_SUBMENU_OLDEST: &str = "Sort the payouts from oldest to latest";
|
||||||
pub const STATUS_SUBMENU_BIGGEST: &str = "Sort the payouts from biggest to smallest";
|
pub const STATUS_SUBMENU_BIGGEST: &str = "Sort the payouts from biggest to smallest";
|
||||||
pub const STATUS_SUBMENU_SMALLEST: &str = "Sort the payouts from smallest to biggest";
|
pub const STATUS_SUBMENU_SMALLEST: &str = "Sort the payouts from smallest to biggest";
|
||||||
pub const STATUS_SUBMENU_AUTOMATIC: &str = "Automatically calculate share/block time with your current P2Pool 1 hour average hashrate";
|
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_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_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_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_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_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_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_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_P2POOL_SHARE_MEAN: &str =
|
||||||
pub const STATUS_SUBMENU_SOLO_BLOCK_MEAN: &str = "The average time it would take for your hashrate to find a block solo mining Monero";
|
"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_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_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_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_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_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_P2POOL_DOMINANCE: &str =
|
||||||
pub const STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE: &str = "The percent of hashrate you account for in P2Pool";
|
"The percent of hashrate P2Pool accounts for in the entire Monero network";
|
||||||
pub const STATUS_SUBMENU_YOUR_MONERO_DOMINANCE: &str = "The percent of hashrate you account 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";
|
||||||
pub const STATUS_SUBMENU_PROGRESS_BAR: &str = "The next time Gupax will update P2Pool stats. Each [*] is 900ms (updates roughly every 54 seconds)";
|
pub const STATUS_SUBMENU_PROGRESS_BAR: &str = "The next time Gupax will update P2Pool stats. Each [*] is 900ms (updates roughly every 54 seconds)";
|
||||||
//-- Benchmarks
|
//-- Benchmarks
|
||||||
pub const STATUS_SUBMENU_YOUR_CPU: &str = "The CPU detected by Gupax";
|
pub const STATUS_SUBMENU_YOUR_CPU: &str = "The CPU detected by Gupax";
|
||||||
pub const STATUS_SUBMENU_YOUR_BENCHMARKS: &str = "How many benchmarks your CPU has had uploaded to [https://xmrig.com/benchmark] ";
|
pub const STATUS_SUBMENU_YOUR_BENCHMARKS: &str =
|
||||||
pub const STATUS_SUBMENU_YOUR_RANK: &str = "Your CPU's rank out of all CPUs listed on [https://xmrig.com/benchmark] (higher is better)";
|
"How many benchmarks your CPU has had uploaded to [https://xmrig.com/benchmark] ";
|
||||||
pub const STATUS_SUBMENU_YOUR_HIGH: &str = "The highest hashrate recorded for your CPU on [https://xmrig.com/benchmark]";
|
pub const STATUS_SUBMENU_YOUR_RANK: &str =
|
||||||
pub const STATUS_SUBMENU_YOUR_AVERAGE: &str = "The average hashrate of your CPU based off the data at [https://xmrig.com/benchmark]";
|
"Your CPU's rank out of all CPUs listed on [https://xmrig.com/benchmark] (higher is better)";
|
||||||
pub const STATUS_SUBMENU_YOUR_LOW: &str = "The lowest hashrate recorded for your CPU on [https://xmrig.com/benchmark]";
|
pub const STATUS_SUBMENU_YOUR_HIGH: &str =
|
||||||
|
"The highest hashrate recorded for your CPU on [https://xmrig.com/benchmark]";
|
||||||
|
pub const STATUS_SUBMENU_YOUR_AVERAGE: &str =
|
||||||
|
"The average hashrate of your CPU based off the data at [https://xmrig.com/benchmark]";
|
||||||
|
pub const STATUS_SUBMENU_YOUR_LOW: &str =
|
||||||
|
"The lowest hashrate recorded for your CPU on [https://xmrig.com/benchmark]";
|
||||||
pub const STATUS_SUBMENU_OTHER_CPUS: &str = "A list of ALL the recorded CPU benchmarks. The CPUs most similar to yours are listed first. All this data is taken from [https://xmrig.com/benchmark].";
|
pub const STATUS_SUBMENU_OTHER_CPUS: &str = "A list of ALL the recorded CPU benchmarks. The CPUs most similar to yours are listed first. All this data is taken from [https://xmrig.com/benchmark].";
|
||||||
pub const STATUS_SUBMENU_OTHER_CPU: &str = "The CPU name";
|
pub const STATUS_SUBMENU_OTHER_CPU: &str = "The CPU name";
|
||||||
pub const STATUS_SUBMENU_OTHER_RELATIVE: &str = "The relative hashrate power compared to the fastest recorded CPU, which is current: [AMD EPYC 7T83 64-Core Processor]";
|
pub const STATUS_SUBMENU_OTHER_RELATIVE: &str = "The relative hashrate power compared to the fastest recorded CPU, which is current: [AMD EPYC 7T83 64-Core Processor]";
|
||||||
|
@ -223,12 +237,15 @@ pub const STATUS_SUBMENU_OTHER_HIGH: &str = "Highest hashrate record";
|
||||||
pub const STATUS_SUBMENU_OTHER_AVERAGE: &str = "Average hashrate";
|
pub const STATUS_SUBMENU_OTHER_AVERAGE: &str = "Average hashrate";
|
||||||
pub const STATUS_SUBMENU_OTHER_LOW: &str = "Lowest hashrate record";
|
pub const STATUS_SUBMENU_OTHER_LOW: &str = "Lowest hashrate record";
|
||||||
pub const STATUS_SUBMENU_OTHER_RANK: &str = "The rank of this CPU out of [1567] (lower is better)";
|
pub const STATUS_SUBMENU_OTHER_RANK: &str = "The rank of this CPU out of [1567] (lower is better)";
|
||||||
pub const STATUS_SUBMENU_OTHER_BENCHMARKS: &str = "How many benchmarks this CPU has had posted to [https://xmrig.com/benchmark]";
|
pub const STATUS_SUBMENU_OTHER_BENCHMARKS: &str =
|
||||||
|
"How many benchmarks this CPU has had posted to [https://xmrig.com/benchmark]";
|
||||||
|
|
||||||
// Gupax
|
// Gupax
|
||||||
pub const GUPAX_UPDATE: &str = "Check for updates on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically";
|
pub const GUPAX_UPDATE: &str =
|
||||||
|
"Check for updates on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically";
|
||||||
pub const GUPAX_AUTO_UPDATE: &str = "Automatically check for updates at startup";
|
pub const GUPAX_AUTO_UPDATE: &str = "Automatically check for updates at startup";
|
||||||
pub const GUPAX_SHOULD_RESTART: &str = "Gupax was updated. A restart is recommended but not required";
|
pub const GUPAX_SHOULD_RESTART: &str =
|
||||||
|
"Gupax was updated. A restart is recommended but not required";
|
||||||
pub const GUPAX_UP_TO_DATE: &str = "Gupax is up-to-date";
|
pub const GUPAX_UP_TO_DATE: &str = "Gupax is up-to-date";
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub const GUPAX_UPDATE_VIA_TOR: &str = "Update through the Tor network. Tor is embedded within Gupax; a Tor system proxy is not required";
|
pub const GUPAX_UPDATE_VIA_TOR: &str = "Update through the Tor network. Tor is embedded within Gupax; a Tor system proxy is not required";
|
||||||
|
@ -241,9 +258,12 @@ pub const GUPAX_AUTO_XMRIG: &str = "Automatically start XMRig on Gupax sta
|
||||||
pub const GUPAX_ADJUST: &str = "Adjust and set the width/height of the Gupax window";
|
pub const GUPAX_ADJUST: &str = "Adjust and set the width/height of the Gupax window";
|
||||||
pub const GUPAX_WIDTH: &str = "Set the width of the Gupax window";
|
pub const GUPAX_WIDTH: &str = "Set the width of the Gupax window";
|
||||||
pub const GUPAX_HEIGHT: &str = "Set the height of the Gupax window";
|
pub const GUPAX_HEIGHT: &str = "Set the height of the Gupax window";
|
||||||
pub const GUPAX_SCALE: &str = "Set the resolution scaling of the Gupax window (resize window to re-apply scaling)";
|
pub const GUPAX_SCALE: &str =
|
||||||
pub const GUPAX_LOCK_WIDTH: &str = "Automatically match the HEIGHT against the WIDTH in a 4:3 ratio";
|
"Set the resolution scaling of the Gupax window (resize window to re-apply scaling)";
|
||||||
pub const GUPAX_LOCK_HEIGHT: &str = "Automatically match the WIDTH against the HEIGHT in a 4:3 ratio";
|
pub const GUPAX_LOCK_WIDTH: &str =
|
||||||
|
"Automatically match the HEIGHT against the WIDTH in a 4:3 ratio";
|
||||||
|
pub const GUPAX_LOCK_HEIGHT: &str =
|
||||||
|
"Automatically match the WIDTH against the HEIGHT in a 4:3 ratio";
|
||||||
pub const GUPAX_NO_LOCK: &str = "Allow individual selection of width and height";
|
pub const GUPAX_NO_LOCK: &str = "Allow individual selection of width and height";
|
||||||
pub const GUPAX_SET: &str = "Set the width/height of the Gupax window to the current values";
|
pub const GUPAX_SET: &str = "Set the width/height of the Gupax window to the current values";
|
||||||
pub const GUPAX_TAB: &str = "Set the default tab Gupax starts on";
|
pub const GUPAX_TAB: &str = "Set the default tab Gupax starts on";
|
||||||
|
@ -253,12 +273,10 @@ pub const GUPAX_TAB_GUPAX: &str = "Set the tab Gupax starts on to: Gupax"
|
||||||
pub const GUPAX_TAB_P2POOL: &str = "Set the tab Gupax starts on to: P2Pool";
|
pub const GUPAX_TAB_P2POOL: &str = "Set the tab Gupax starts on to: P2Pool";
|
||||||
pub const GUPAX_TAB_XMRIG: &str = "Set the tab Gupax starts on to: XMRig";
|
pub const GUPAX_TAB_XMRIG: &str = "Set the tab Gupax starts on to: XMRig";
|
||||||
|
|
||||||
pub const GUPAX_SIMPLE: &str =
|
pub const GUPAX_SIMPLE: &str = r#"Use simple Gupax settings:
|
||||||
r#"Use simple Gupax settings:
|
|
||||||
- Update button
|
- Update button
|
||||||
- Basic toggles"#;
|
- Basic toggles"#;
|
||||||
pub const GUPAX_ADVANCED: &str =
|
pub const GUPAX_ADVANCED: &str = r#"Use advanced Gupax settings:
|
||||||
r#"Use advanced Gupax settings:
|
|
||||||
- Update button
|
- Update button
|
||||||
- Basic toggles
|
- Basic toggles
|
||||||
- P2Pool/XMRig binary path selector
|
- P2Pool/XMRig binary path selector
|
||||||
|
@ -276,20 +294,20 @@ pub const P2POOL_OUT: &str = "How many out-bound peers to con
|
||||||
pub const P2POOL_IN: &str = "How many in-bound peers to allow? (others connecting to you)";
|
pub const P2POOL_IN: &str = "How many in-bound peers to allow? (others connecting to you)";
|
||||||
pub const P2POOL_LOG: &str = "Verbosity of the console log";
|
pub const P2POOL_LOG: &str = "Verbosity of the console log";
|
||||||
pub const P2POOL_AUTO_NODE: &str = "Automatically ping the remote Monero nodes at Gupax startup";
|
pub const P2POOL_AUTO_NODE: &str = "Automatically ping the remote Monero nodes at Gupax startup";
|
||||||
pub const P2POOL_AUTO_SELECT: &str = "Automatically select the fastest remote Monero node after pinging";
|
pub const P2POOL_AUTO_SELECT: &str =
|
||||||
pub const P2POOL_BACKUP_HOST_SIMPLE: &str =
|
"Automatically select the fastest remote Monero node after pinging";
|
||||||
r#"Automatically switch to the other nodes listed if the current one is down.
|
pub const P2POOL_BACKUP_HOST_SIMPLE: &str = r#"Automatically switch to the other nodes listed if the current one is down.
|
||||||
|
|
||||||
Note: you must ping the remote nodes or this feature will default to only using the currently selected node."#;
|
Note: you must ping the remote nodes or this feature will default to only using the currently selected node."#;
|
||||||
pub const P2POOL_BACKUP_HOST_ADVANCED: &str = "Automatically switch to the other nodes in your list if the current one is down.";
|
pub const P2POOL_BACKUP_HOST_ADVANCED: &str =
|
||||||
|
"Automatically switch to the other nodes in your list if the current one is down.";
|
||||||
pub const P2POOL_SELECT_FASTEST: &str = "Select the fastest remote Monero node";
|
pub const P2POOL_SELECT_FASTEST: &str = "Select the fastest remote Monero node";
|
||||||
pub const P2POOL_SELECT_RANDOM: &str = "Select a random remote Monero node";
|
pub const P2POOL_SELECT_RANDOM: &str = "Select a random remote Monero node";
|
||||||
pub const P2POOL_SELECT_LAST: &str = "Select the previous remote Monero node";
|
pub const P2POOL_SELECT_LAST: &str = "Select the previous remote Monero node";
|
||||||
pub const P2POOL_SELECT_NEXT: &str = "Select the next remote Monero node";
|
pub const P2POOL_SELECT_NEXT: &str = "Select the next remote Monero node";
|
||||||
pub const P2POOL_PING: &str = "Ping the built-in remote Monero nodes";
|
pub const P2POOL_PING: &str = "Ping the built-in remote Monero nodes";
|
||||||
pub const P2POOL_ADDRESS: &str = "You must use a primary Monero address to mine on P2Pool (starts with a 4). It is highly recommended to create a new wallet since addresses are public on P2Pool!";
|
pub const P2POOL_ADDRESS: &str = "You must use a primary Monero address to mine on P2Pool (starts with a 4). It is highly recommended to create a new wallet since addresses are public on P2Pool!";
|
||||||
pub const P2POOL_COMMUNITY_NODE_WARNING: &str =
|
pub const P2POOL_COMMUNITY_NODE_WARNING: &str = r#"TL;DR: Run & use your own Monero Node.
|
||||||
r#"TL;DR: Run & use your own Monero Node.
|
|
||||||
|
|
||||||
Using a Remote Monero Node is convenient but comes at the cost of privacy and reliability.
|
Using a Remote Monero Node is convenient but comes at the cost of privacy and reliability.
|
||||||
|
|
||||||
|
@ -300,17 +318,14 @@ Running and using your own local Monero node improves privacy and ensures your c
|
||||||
For a simple guide, see the [Running a Local Monero Node] section on Gupax's GitHub by clicking this message."#;
|
For a simple guide, see the [Running a Local Monero Node] section on Gupax's GitHub by clicking this message."#;
|
||||||
|
|
||||||
pub const P2POOL_INPUT: &str = "Send a command to P2Pool";
|
pub const P2POOL_INPUT: &str = "Send a command to P2Pool";
|
||||||
pub const P2POOL_ARGUMENTS: &str =
|
pub const P2POOL_ARGUMENTS: &str = r#"WARNING: Use [--no-color] and make sure to set [--data-api <PATH>] & [--local-api] so that the [Status] tab can work!
|
||||||
r#"WARNING: Use [--no-color] and make sure to set [--data-api <PATH>] & [--local-api] so that the [Status] tab can work!
|
|
||||||
|
|
||||||
Start P2Pool with these arguments and override all below settings"#;
|
Start P2Pool with these arguments and override all below settings"#;
|
||||||
pub const P2POOL_SIMPLE: &str =
|
pub const P2POOL_SIMPLE: &str = r#"Use simple P2Pool settings:
|
||||||
r#"Use simple P2Pool settings:
|
|
||||||
- Remote remote Monero node
|
- Remote remote Monero node
|
||||||
- Default P2Pool settings + Mini
|
- Default P2Pool settings + Mini
|
||||||
- Backup host setting"#;
|
- Backup host setting"#;
|
||||||
pub const P2POOL_ADVANCED: &str =
|
pub const P2POOL_ADVANCED: &str = r#"Use advanced P2Pool settings:
|
||||||
r#"Use advanced P2Pool settings:
|
|
||||||
- Terminal input
|
- Terminal input
|
||||||
- Overriding command arguments
|
- Overriding command arguments
|
||||||
- Manual node list
|
- Manual node list
|
||||||
|
@ -334,13 +349,11 @@ pub const LIST_DELETE: &str = "Delete the currently selected entry";
|
||||||
pub const LIST_CLEAR: &str = "Clear all current values";
|
pub const LIST_CLEAR: &str = "Clear all current values";
|
||||||
|
|
||||||
// XMRig
|
// XMRig
|
||||||
pub const XMRIG_SIMPLE: &str =
|
pub const XMRIG_SIMPLE: &str = r#"Use simple XMRig settings:
|
||||||
r#"Use simple XMRig settings:
|
|
||||||
- Mine to local P2Pool (localhost:3333)
|
- Mine to local P2Pool (localhost:3333)
|
||||||
- CPU thread slider
|
- CPU thread slider
|
||||||
- HTTP API @ localhost:18088"#;
|
- HTTP API @ localhost:18088"#;
|
||||||
pub const XMRIG_ADVANCED: &str =
|
pub const XMRIG_ADVANCED: &str = r#"Use advanced XMRig settings:
|
||||||
r#"Use advanced XMRig settings:
|
|
||||||
- Terminal input
|
- Terminal input
|
||||||
- Overriding command arguments
|
- Overriding command arguments
|
||||||
- Custom payout address
|
- Custom payout address
|
||||||
|
@ -350,8 +363,7 @@ r#"Use advanced XMRig settings:
|
||||||
- TLS setting
|
- TLS setting
|
||||||
- Keepalive setting"#;
|
- Keepalive setting"#;
|
||||||
pub const XMRIG_INPUT: &str = "Send a command to XMRig";
|
pub const XMRIG_INPUT: &str = "Send a command to XMRig";
|
||||||
pub const XMRIG_ARGUMENTS: &str =
|
pub const XMRIG_ARGUMENTS: &str = r#"WARNING: Use [--no-color] and make sure to set [--http-host <IP>] & [--http-port <PORT>] so that the [Status] tab can work!
|
||||||
r#"WARNING: Use [--no-color] and make sure to set [--http-host <IP>] & [--http-port <PORT>] so that the [Status] tab can work!
|
|
||||||
|
|
||||||
Start XMRig with these arguments and override all below settings"#;
|
Start XMRig with these arguments and override all below settings"#;
|
||||||
pub const XMRIG_ADDRESS: &str = "Specify which Monero address to payout to. This does nothing if mining to P2Pool since the address being paid out to will be the one P2Pool started with. This doubles as a rig identifier for P2Pool and some pools.";
|
pub const XMRIG_ADDRESS: &str = "Specify which Monero address to payout to. This does nothing if mining to P2Pool since the address being paid out to will be the one P2Pool started with. This doubles as a rig identifier for P2Pool and some pools.";
|
||||||
|
@ -360,9 +372,12 @@ pub const XMRIG_IP: &str = "Specify the pool IP to connect to with X
|
||||||
pub const XMRIG_PORT: &str = "Specify the port of the pool; [1-65535]";
|
pub const XMRIG_PORT: &str = "Specify the port of the pool; [1-65535]";
|
||||||
pub const XMRIG_RIG: &str = "Add an optional rig ID. This will be the name shown on the pool; Only [A-Za-z0-9-_] and spaces allowed; Max length = 30 characters";
|
pub const XMRIG_RIG: &str = "Add an optional rig ID. This will be the name shown on the pool; Only [A-Za-z0-9-_] and spaces allowed; Max length = 30 characters";
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
pub const XMRIG_PAUSE: &str = "THIS SETTING IS DISABLED IF SET TO [0]. Pause mining if user is active, resume after";
|
pub const XMRIG_PAUSE: &str =
|
||||||
pub const XMRIG_API_IP: &str = "Specify which IP to bind to for XMRig's HTTP API; If empty: [localhost/127.0.0.1]";
|
"THIS SETTING IS DISABLED IF SET TO [0]. Pause mining if user is active, resume after";
|
||||||
pub const XMRIG_API_PORT: &str = "Specify which port to bind to for XMRig's HTTP API; If empty: [18088]";
|
pub const XMRIG_API_IP: &str =
|
||||||
|
"Specify which IP to bind to for XMRig's HTTP API; If empty: [localhost/127.0.0.1]";
|
||||||
|
pub const XMRIG_API_PORT: &str =
|
||||||
|
"Specify which port to bind to for XMRig's HTTP API; If empty: [18088]";
|
||||||
pub const XMRIG_TLS: &str = "Enable SSL/TLS connections (needs pool support)";
|
pub const XMRIG_TLS: &str = "Enable SSL/TLS connections (needs pool support)";
|
||||||
pub const XMRIG_KEEPALIVE: &str = "Send keepalive packets to prevent timeout (needs pool support)";
|
pub const XMRIG_KEEPALIVE: &str = "Send keepalive packets to prevent timeout (needs pool support)";
|
||||||
pub const XMRIG_THREADS: &str = "Number of CPU threads to use for mining";
|
pub const XMRIG_THREADS: &str = "Number of CPU threads to use for mining";
|
||||||
|
@ -372,8 +387,7 @@ pub const XMRIG_PATH_OK: &str = "XMRig was found at the given PATH";
|
||||||
pub const XMRIG_PATH_EMPTY: &str = "XMRig PATH is empty! To fix: goto the [Gupax Advanced] tab, select [Open] and specify where XMRig is located.";
|
pub const XMRIG_PATH_EMPTY: &str = "XMRig PATH is empty! To fix: goto the [Gupax Advanced] tab, select [Open] and specify where XMRig is located.";
|
||||||
|
|
||||||
// CLI argument messages
|
// CLI argument messages
|
||||||
pub const ARG_HELP: &str =
|
pub const ARG_HELP: &str = r#"USAGE: ./gupax [--flag]
|
||||||
r#"USAGE: ./gupax [--flag]
|
|
||||||
|
|
||||||
--help Print this help message
|
--help Print this help message
|
||||||
--version Print version and build info
|
--version Print version and build info
|
||||||
|
@ -390,29 +404,16 @@ r#"USAGE: ./gupax [--flag]
|
||||||
To view more detailed console debug information, start Gupax with
|
To view more detailed console debug information, start Gupax with
|
||||||
the environment variable [RUST_LOG] set to a log level like so:
|
the environment variable [RUST_LOG] set to a log level like so:
|
||||||
RUST_LOG=(trace|debug|info|warn|error) ./gupax"#;
|
RUST_LOG=(trace|debug|info|warn|error) ./gupax"#;
|
||||||
pub const ARG_COPYRIGHT: &str =
|
pub const ARG_COPYRIGHT: &str = r#"Gupax is licensed under GPLv3.
|
||||||
r#"Gupax is licensed under GPLv3.
|
|
||||||
For more information, see link below:
|
For more information, see link below:
|
||||||
<https://github.com/hinto-janai/gupax>"#;
|
<https://github.com/hinto-janai/gupax>"#;
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Visuals
|
//---------------------------------------------------------------------------------------------------- Visuals
|
||||||
use egui::epaint::{
|
use egui::epaint::{Rounding, Shadow, Stroke};
|
||||||
Rounding,
|
|
||||||
Shadow,
|
|
||||||
Stroke
|
|
||||||
};
|
|
||||||
|
|
||||||
use egui::{
|
use egui::{style::Spacing, Color32, Visuals};
|
||||||
Color32,
|
|
||||||
Visuals,
|
|
||||||
style::Spacing,
|
|
||||||
};
|
|
||||||
|
|
||||||
use egui::style::{
|
use egui::style::{Selection, WidgetVisuals, Widgets};
|
||||||
Selection,
|
|
||||||
Widgets,
|
|
||||||
WidgetVisuals,
|
|
||||||
};
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
pub const ACCENT_COLOR: Color32 = Color32::from_rgb(200, 100, 100);
|
pub const ACCENT_COLOR: Color32 = Color32::from_rgb(200, 100, 100);
|
||||||
|
@ -497,8 +498,17 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_app_ratio_is_4_by_3() {
|
fn default_app_ratio_is_4_by_3() {
|
||||||
assert_eq!(format!("{:.3}", crate::APP_MIN_WIDTH/crate::APP_MIN_HEIGHT), "1.333");
|
assert_eq!(
|
||||||
assert_eq!(format!("{:.3}", crate::APP_DEFAULT_WIDTH/crate::APP_DEFAULT_HEIGHT), "1.333");
|
format!("{:.3}", crate::APP_MIN_WIDTH / crate::APP_MIN_HEIGHT),
|
||||||
|
"1.333"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format!(
|
||||||
|
"{:.3}",
|
||||||
|
crate::APP_DEFAULT_WIDTH / crate::APP_DEFAULT_HEIGHT
|
||||||
|
),
|
||||||
|
"1.333"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
457
src/disk.rs
457
src/disk.rs
|
@ -30,28 +30,21 @@
|
||||||
// ├─ Version/
|
// ├─ Version/
|
||||||
// ├─ ...
|
// ├─ ...
|
||||||
|
|
||||||
use std::{
|
use crate::{constants::*, gupax::Ratio, human::*, macros::*, xmr::*, Tab};
|
||||||
fs,
|
use figment::providers::{Format, Toml};
|
||||||
fmt::Display,
|
|
||||||
path::PathBuf,
|
|
||||||
result::Result,
|
|
||||||
sync::{Arc,Mutex},
|
|
||||||
fmt::Write,
|
|
||||||
};
|
|
||||||
use serde::{Serialize,Deserialize};
|
|
||||||
use figment::Figment;
|
use figment::Figment;
|
||||||
use figment::providers::{Format,Toml};
|
|
||||||
use crate::{
|
|
||||||
human::*,
|
|
||||||
constants::*,
|
|
||||||
gupax::Ratio,
|
|
||||||
Tab,
|
|
||||||
xmr::*,
|
|
||||||
macros::*,
|
|
||||||
};
|
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
fmt::Write,
|
||||||
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
result::Result,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Const
|
//---------------------------------------------------------------------------------------------------- Const
|
||||||
// State file
|
// State file
|
||||||
|
@ -132,8 +125,11 @@ pub fn get_gupax_data_path() -> Result<PathBuf, TomlError> {
|
||||||
gupax_p2pool_dir.push(GUPAX_P2POOL_API_DIRECTORY);
|
gupax_p2pool_dir.push(GUPAX_P2POOL_API_DIRECTORY);
|
||||||
create_gupax_p2pool_dir(&gupax_p2pool_dir)?;
|
create_gupax_p2pool_dir(&gupax_p2pool_dir)?;
|
||||||
Ok(path)
|
Ok(path)
|
||||||
},
|
}
|
||||||
None => { error!("OS | Data path ... FAIL"); Err(TomlError::Path(PATH_ERROR.to_string())) },
|
None => {
|
||||||
|
error!("OS | Data path ... FAIL");
|
||||||
|
Err(TomlError::Path(PATH_ERROR.to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,8 +138,21 @@ pub fn set_unix_750_perms(path: &PathBuf) -> Result<(), TomlError> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
match fs::set_permissions(path, fs::Permissions::from_mode(0o750)) {
|
match fs::set_permissions(path, fs::Permissions::from_mode(0o750)) {
|
||||||
Ok(_) => { info!("OS | Unix 750 permissions on path [{}] ... OK", path.display()); Ok(()) },
|
Ok(_) => {
|
||||||
Err(e) => { error!("OS | Unix 750 permissions on path [{}] ... FAIL ... {}", path.display(), e); Err(TomlError::Io(e)) },
|
info!(
|
||||||
|
"OS | Unix 750 permissions on path [{}] ... OK",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"OS | Unix 750 permissions on path [{}] ... FAIL ... {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
Err(TomlError::Io(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,8 +161,21 @@ pub fn set_unix_660_perms(path: &PathBuf) -> Result<(), TomlError> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
match fs::set_permissions(path, fs::Permissions::from_mode(0o660)) {
|
match fs::set_permissions(path, fs::Permissions::from_mode(0o660)) {
|
||||||
Ok(_) => { info!("OS | Unix 660 permissions on path [{}] ... OK", path.display()); Ok(()) },
|
Ok(_) => {
|
||||||
Err(e) => { error!("OS | Unix 660 permissions on path [{}] ... FAIL ... {}", path.display(), e); Err(TomlError::Io(e)) },
|
info!(
|
||||||
|
"OS | Unix 660 permissions on path [{}] ... OK",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"OS | Unix 660 permissions on path [{}] ... FAIL ... {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
Err(TomlError::Io(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +189,10 @@ pub fn create_gupax_dir(path: &PathBuf) -> Result<(), TomlError> {
|
||||||
// Create Gupax directory
|
// Create Gupax directory
|
||||||
match fs::create_dir_all(path) {
|
match fs::create_dir_all(path) {
|
||||||
Ok(_) => info!("OS | Create data path ... OK"),
|
Ok(_) => info!("OS | Create data path ... OK"),
|
||||||
Err(e) => { error!("OS | Create data path ... FAIL ... {}", e); return Err(TomlError::Io(e)) },
|
Err(e) => {
|
||||||
|
error!("OS | Create data path ... FAIL ... {}", e);
|
||||||
|
return Err(TomlError::Io(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set_unix_750_perms(path)
|
set_unix_750_perms(path)
|
||||||
}
|
}
|
||||||
|
@ -175,8 +200,21 @@ pub fn create_gupax_dir(path: &PathBuf) -> Result<(), TomlError> {
|
||||||
pub fn create_gupax_p2pool_dir(path: &PathBuf) -> Result<(), TomlError> {
|
pub fn create_gupax_p2pool_dir(path: &PathBuf) -> Result<(), TomlError> {
|
||||||
// Create Gupax directory
|
// Create Gupax directory
|
||||||
match fs::create_dir_all(path) {
|
match fs::create_dir_all(path) {
|
||||||
Ok(_) => { info!("OS | Create Gupax-P2Pool API path [{}] ... OK", path.display()); Ok(()) },
|
Ok(_) => {
|
||||||
Err(e) => { error!("OS | Create Gupax-P2Pool API path [{}] ... FAIL ... {}", path.display(), e); Err(TomlError::Io(e)) },
|
info!(
|
||||||
|
"OS | Create Gupax-P2Pool API path [{}] ... OK",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"OS | Create Gupax-P2Pool API path [{}] ... FAIL ... {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
Err(TomlError::Io(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,18 +224,20 @@ pub fn read_to_string(file: File, path: &PathBuf) -> Result<String, TomlError> {
|
||||||
Ok(string) => {
|
Ok(string) => {
|
||||||
info!("{:?} | Read ... OK", file);
|
info!("{:?} | Read ... OK", file);
|
||||||
Ok(string)
|
Ok(string)
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("{:?} | Read ... FAIL", file);
|
warn!("{:?} | Read ... FAIL", file);
|
||||||
Err(TomlError::Io(err))
|
Err(TomlError::Io(err))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write str to console with [info!] surrounded by "---"
|
// Write str to console with [info!] surrounded by "---"
|
||||||
pub fn print_dash(toml: &str) {
|
pub fn print_dash(toml: &str) {
|
||||||
info!("{}", HORIZONTAL);
|
info!("{}", HORIZONTAL);
|
||||||
for i in toml.lines() { info!("{}", i); }
|
for i in toml.lines() {
|
||||||
|
info!("{}", i);
|
||||||
|
}
|
||||||
info!("{}", HORIZONTAL);
|
info!("{}", HORIZONTAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +291,7 @@ impl State {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("State | String -> State ... FAIL ... {}", err);
|
warn!("State | String -> State ... FAIL ... {}", err);
|
||||||
Err(TomlError::Deserialize(err))
|
Err(TomlError::Deserialize(err))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +299,10 @@ impl State {
|
||||||
pub fn to_string(&self) -> Result<String, TomlError> {
|
pub fn to_string(&self) -> Result<String, TomlError> {
|
||||||
match toml::ser::to_string(self) {
|
match toml::ser::to_string(self) {
|
||||||
Ok(s) => Ok(s),
|
Ok(s) => Ok(s),
|
||||||
Err(e) => { error!("State | Couldn't serialize default file: {}", e); Err(TomlError::Serialize(e)) },
|
Err(e) => {
|
||||||
|
error!("State | Couldn't serialize default file: {}", e);
|
||||||
|
Err(TomlError::Serialize(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +323,7 @@ impl State {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
// Deserialize, attempt merge if failed
|
// Deserialize, attempt merge if failed
|
||||||
match Self::from_str(&string) {
|
match Self::from_str(&string) {
|
||||||
|
@ -288,10 +331,13 @@ impl State {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
warn!("State | Attempting merge...");
|
warn!("State | Attempting merge...");
|
||||||
match Self::merge(&string) {
|
match Self::merge(&string) {
|
||||||
Ok(mut new) => { Self::save(&mut new, path)?; Ok(new) },
|
Ok(mut new) => {
|
||||||
|
Self::save(&mut new, path)?;
|
||||||
|
Ok(new)
|
||||||
|
}
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,12 +363,21 @@ impl State {
|
||||||
info!("State | Parse ... OK");
|
info!("State | Parse ... OK");
|
||||||
print_dash(&string);
|
print_dash(&string);
|
||||||
string
|
string
|
||||||
},
|
}
|
||||||
Err(err) => { error!("State | Couldn't parse TOML into string ... FAIL"); return Err(TomlError::Serialize(err)) },
|
Err(err) => {
|
||||||
|
error!("State | Couldn't parse TOML into string ... FAIL");
|
||||||
|
return Err(TomlError::Serialize(err));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match fs::write(path, string) {
|
match fs::write(path, string) {
|
||||||
Ok(_) => { info!("State | Save ... OK"); Ok(()) },
|
Ok(_) => {
|
||||||
Err(err) => { error!("State | Couldn't overwrite TOML file ... FAIL"); Err(TomlError::Io(err)) },
|
info!("State | Save ... OK");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("State | Couldn't overwrite TOML file ... FAIL");
|
||||||
|
Err(TomlError::Io(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,9 +385,18 @@ impl State {
|
||||||
// leaving behind old keys+values and updating [default] with old valid ones.
|
// leaving behind old keys+values and updating [default] with old valid ones.
|
||||||
pub fn merge(old: &str) -> Result<Self, TomlError> {
|
pub fn merge(old: &str) -> Result<Self, TomlError> {
|
||||||
let default = toml::ser::to_string(&Self::new()).unwrap();
|
let default = toml::ser::to_string(&Self::new()).unwrap();
|
||||||
let new: Self = match Figment::from(Toml::string(&default)).merge(Toml::string(old)).extract() {
|
let new: Self = match Figment::from(Toml::string(&default))
|
||||||
Ok(new) => { info!("State | TOML merge ... OK"); new },
|
.merge(Toml::string(old))
|
||||||
Err(err) => { error!("State | Couldn't merge default + old TOML"); return Err(TomlError::Merge(err)) },
|
.extract()
|
||||||
|
{
|
||||||
|
Ok(new) => {
|
||||||
|
info!("State | TOML merge ... OK");
|
||||||
|
new
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("State | Couldn't merge default + old TOML");
|
||||||
|
return Err(TomlError::Merge(err));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(new)
|
Ok(new)
|
||||||
}
|
}
|
||||||
|
@ -365,8 +429,8 @@ impl Node {
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Node | String parse ... FAIL ... {}", err);
|
error!("Node | String parse ... FAIL ... {}", err);
|
||||||
return Err(TomlError::Deserialize(err))
|
return Err(TomlError::Deserialize(err));
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
let size = nodes.keys().len();
|
let size = nodes.keys().len();
|
||||||
let mut vec = Vec::with_capacity(size);
|
let mut vec = Vec::with_capacity(size);
|
||||||
|
@ -374,29 +438,43 @@ impl Node {
|
||||||
let ip = match values.get("ip") {
|
let ip = match values.get("ip") {
|
||||||
Some(ip) => match ip.as_str() {
|
Some(ip) => match ip.as_str() {
|
||||||
Some(ip) => ip.to_string(),
|
Some(ip) => ip.to_string(),
|
||||||
None => { error!("Node | [None] at [ip] parse"); return Err(TomlError::Parse("[None] at [ip] parse")) },
|
None => {
|
||||||
|
error!("Node | [None] at [ip] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [ip] parse"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => { error!("Node | [None] at [ip] parse"); return Err(TomlError::Parse("[None] at [ip] parse")) },
|
None => {
|
||||||
|
error!("Node | [None] at [ip] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [ip] parse"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let rpc = match values.get("rpc") {
|
let rpc = match values.get("rpc") {
|
||||||
Some(rpc) => match rpc.as_str() {
|
Some(rpc) => match rpc.as_str() {
|
||||||
Some(rpc) => rpc.to_string(),
|
Some(rpc) => rpc.to_string(),
|
||||||
None => { error!("Node | [None] at [rpc] parse"); return Err(TomlError::Parse("[None] at [rpc] parse")) },
|
None => {
|
||||||
|
error!("Node | [None] at [rpc] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [rpc] parse"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => { error!("Node | [None] at [rpc] parse"); return Err(TomlError::Parse("[None] at [rpc] parse")) },
|
None => {
|
||||||
|
error!("Node | [None] at [rpc] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [rpc] parse"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let zmq = match values.get("zmq") {
|
let zmq = match values.get("zmq") {
|
||||||
Some(zmq) => match zmq.as_str() {
|
Some(zmq) => match zmq.as_str() {
|
||||||
Some(zmq) => zmq.to_string(),
|
Some(zmq) => zmq.to_string(),
|
||||||
None => { error!("Node | [None] at [zmq] parse"); return Err(TomlError::Parse("[None] at [zmq] parse")) },
|
None => {
|
||||||
|
error!("Node | [None] at [zmq] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [zmq] parse"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => { error!("Node | [None] at [zmq] parse"); return Err(TomlError::Parse("[None] at [zmq] parse")) },
|
None => {
|
||||||
};
|
error!("Node | [None] at [zmq] parse");
|
||||||
let node = Node {
|
return Err(TomlError::Parse("[None] at [zmq] parse"));
|
||||||
ip,
|
}
|
||||||
rpc,
|
|
||||||
zmq,
|
|
||||||
};
|
};
|
||||||
|
let node = Node { ip, rpc, zmq };
|
||||||
vec.push((key.clone(), node));
|
vec.push((key.clone(), node));
|
||||||
}
|
}
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
|
@ -410,10 +488,7 @@ impl Node {
|
||||||
write!(
|
write!(
|
||||||
toml,
|
toml,
|
||||||
"[\'{}\']\nip = {:#?}\nrpc = {:#?}\nzmq = {:#?}\n\n",
|
"[\'{}\']\nip = {:#?}\nrpc = {:#?}\nzmq = {:#?}\n\n",
|
||||||
key,
|
key, value.ip, value.rpc, value.zmq,
|
||||||
value.ip,
|
|
||||||
value.rpc,
|
|
||||||
value.zmq,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(toml)
|
Ok(toml)
|
||||||
|
@ -433,7 +508,7 @@ impl Node {
|
||||||
_ => {
|
_ => {
|
||||||
Self::create_new(path)?;
|
Self::create_new(path)?;
|
||||||
read_to_string(file, path)?
|
read_to_string(file, path)?
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
// Deserialize, attempt merge if failed
|
// Deserialize, attempt merge if failed
|
||||||
Self::from_str_to_vec(&string)
|
Self::from_str_to_vec(&string)
|
||||||
|
@ -455,25 +530,31 @@ impl Node {
|
||||||
info!("Node | Saving to disk ... [{}]", path.display());
|
info!("Node | Saving to disk ... [{}]", path.display());
|
||||||
let string = Self::to_string(vec)?;
|
let string = Self::to_string(vec)?;
|
||||||
match fs::write(path, string) {
|
match fs::write(path, string) {
|
||||||
Ok(_) => { info!("Node | Save ... OK"); Ok(()) },
|
Ok(_) => {
|
||||||
Err(err) => { error!("Node | Couldn't overwrite file"); Err(TomlError::Io(err)) },
|
info!("Node | Save ... OK");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Node | Couldn't overwrite file");
|
||||||
|
Err(TomlError::Io(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn merge(old: &String) -> Result<Self, TomlError> {
|
// pub fn merge(old: &String) -> Result<Self, TomlError> {
|
||||||
// info!("Node | Starting TOML merge...");
|
// info!("Node | Starting TOML merge...");
|
||||||
// let default = match toml::ser::to_string(&Self::new()) {
|
// let default = match toml::ser::to_string(&Self::new()) {
|
||||||
// Ok(string) => { info!("Node | Default TOML parse ... OK"); string },
|
// Ok(string) => { info!("Node | Default TOML parse ... OK"); string },
|
||||||
// Err(err) => { error!("Node | Couldn't parse default TOML into string"); return Err(TomlError::Serialize(err)) },
|
// Err(err) => { error!("Node | Couldn't parse default TOML into string"); return Err(TomlError::Serialize(err)) },
|
||||||
// };
|
// };
|
||||||
// let mut new: Self = match Figment::new().merge(Toml::string(&old)).merge(Toml::string(&default)).extract() {
|
// let mut new: Self = match Figment::new().merge(Toml::string(&old)).merge(Toml::string(&default)).extract() {
|
||||||
// Ok(new) => { info!("Node | TOML merge ... OK"); new },
|
// Ok(new) => { info!("Node | TOML merge ... OK"); new },
|
||||||
// Err(err) => { error!("Node | Couldn't merge default + old TOML"); return Err(TomlError::Merge(err)) },
|
// Err(err) => { error!("Node | Couldn't merge default + old TOML"); return Err(TomlError::Merge(err)) },
|
||||||
// };
|
// };
|
||||||
// // Attempt save
|
// // Attempt save
|
||||||
// Self::save(&mut new)?;
|
// Self::save(&mut new)?;
|
||||||
// Ok(new)
|
// Ok(new)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [Pool] impl
|
//---------------------------------------------------------------------------------------------------- [Pool] impl
|
||||||
|
@ -502,8 +583,8 @@ impl Pool {
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Pool | String parse ... FAIL ... {}", err);
|
error!("Pool | String parse ... FAIL ... {}", err);
|
||||||
return Err(TomlError::Deserialize(err))
|
return Err(TomlError::Deserialize(err));
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
let size = pools.keys().len();
|
let size = pools.keys().len();
|
||||||
let mut vec = Vec::with_capacity(size);
|
let mut vec = Vec::with_capacity(size);
|
||||||
|
@ -512,29 +593,43 @@ impl Pool {
|
||||||
let rig = match values.get("rig") {
|
let rig = match values.get("rig") {
|
||||||
Some(rig) => match rig.as_str() {
|
Some(rig) => match rig.as_str() {
|
||||||
Some(rig) => rig.to_string(),
|
Some(rig) => rig.to_string(),
|
||||||
None => { error!("Pool | [None] at [rig] parse"); return Err(TomlError::Parse("[None] at [rig] parse")) },
|
None => {
|
||||||
|
error!("Pool | [None] at [rig] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [rig] parse"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => { error!("Pool | [None] at [rig] parse"); return Err(TomlError::Parse("[None] at [rig] parse")) },
|
None => {
|
||||||
|
error!("Pool | [None] at [rig] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [rig] parse"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let ip = match values.get("ip") {
|
let ip = match values.get("ip") {
|
||||||
Some(ip) => match ip.as_str() {
|
Some(ip) => match ip.as_str() {
|
||||||
Some(ip) => ip.to_string(),
|
Some(ip) => ip.to_string(),
|
||||||
None => { error!("Pool | [None] at [ip] parse"); return Err(TomlError::Parse("[None] at [ip] parse")) },
|
None => {
|
||||||
|
error!("Pool | [None] at [ip] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [ip] parse"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => { error!("Pool | [None] at [ip] parse"); return Err(TomlError::Parse("[None] at [ip] parse")) },
|
None => {
|
||||||
|
error!("Pool | [None] at [ip] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [ip] parse"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let port = match values.get("port") {
|
let port = match values.get("port") {
|
||||||
Some(port) => match port.as_str() {
|
Some(port) => match port.as_str() {
|
||||||
Some(port) => port.to_string(),
|
Some(port) => port.to_string(),
|
||||||
None => { error!("Pool | [None] at [port] parse"); return Err(TomlError::Parse("[None] at [port] parse")) },
|
None => {
|
||||||
|
error!("Pool | [None] at [port] parse");
|
||||||
|
return Err(TomlError::Parse("[None] at [port] parse"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => { error!("Pool | [None] at [port] parse"); return Err(TomlError::Parse("[None] at [port] parse")) },
|
None => {
|
||||||
};
|
error!("Pool | [None] at [port] parse");
|
||||||
let pool = Pool {
|
return Err(TomlError::Parse("[None] at [port] parse"));
|
||||||
rig,
|
}
|
||||||
ip,
|
|
||||||
port,
|
|
||||||
};
|
};
|
||||||
|
let pool = Pool { rig, ip, port };
|
||||||
vec.push((key.clone(), pool));
|
vec.push((key.clone(), pool));
|
||||||
}
|
}
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
|
@ -546,10 +641,7 @@ impl Pool {
|
||||||
write!(
|
write!(
|
||||||
toml,
|
toml,
|
||||||
"[\'{}\']\nrig = {:#?}\nip = {:#?}\nport = {:#?}\n\n",
|
"[\'{}\']\nrig = {:#?}\nip = {:#?}\nport = {:#?}\n\n",
|
||||||
key,
|
key, value.rig, value.ip, value.port,
|
||||||
value.rig,
|
|
||||||
value.ip,
|
|
||||||
value.port,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(toml)
|
Ok(toml)
|
||||||
|
@ -564,7 +656,7 @@ impl Pool {
|
||||||
_ => {
|
_ => {
|
||||||
Self::create_new(path)?;
|
Self::create_new(path)?;
|
||||||
read_to_string(file, path)?
|
read_to_string(file, path)?
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
// Deserialize
|
// Deserialize
|
||||||
Self::from_str_to_vec(&string)
|
Self::from_str_to_vec(&string)
|
||||||
|
@ -583,14 +675,20 @@ impl Pool {
|
||||||
info!("Pool | Saving to disk ... [{}]", path.display());
|
info!("Pool | Saving to disk ... [{}]", path.display());
|
||||||
let string = Self::to_string(vec)?;
|
let string = Self::to_string(vec)?;
|
||||||
match fs::write(path, string) {
|
match fs::write(path, string) {
|
||||||
Ok(_) => { info!("Pool | Save ... OK"); Ok(()) },
|
Ok(_) => {
|
||||||
Err(err) => { error!("Pool | Couldn't overwrite file"); Err(TomlError::Io(err)) },
|
info!("Pool | Save ... OK");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Pool | Couldn't overwrite file");
|
||||||
|
Err(TomlError::Io(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Gupax-P2Pool API
|
//---------------------------------------------------------------------------------------------------- Gupax-P2Pool API
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GupaxP2poolApi {
|
pub struct GupaxP2poolApi {
|
||||||
pub log: String, // Log file only containing full payout lines
|
pub log: String, // Log file only containing full payout lines
|
||||||
pub log_rev: String, // Same as above but reversed based off lines
|
pub log_rev: String, // Same as above but reversed based off lines
|
||||||
|
@ -605,7 +703,11 @@ pub struct GupaxP2poolApi {
|
||||||
pub path_xmr: PathBuf, // Path to [xmr]
|
pub path_xmr: PathBuf, // Path to [xmr]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GupaxP2poolApi { fn default() -> Self { Self::new() } }
|
impl Default for GupaxP2poolApi {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GupaxP2poolApi {
|
impl GupaxP2poolApi {
|
||||||
//---------------------------------------------------------------------------------------------------- Init, these pretty much only get called once
|
//---------------------------------------------------------------------------------------------------- Init, these pretty much only get called once
|
||||||
|
@ -646,31 +748,53 @@ impl GupaxP2poolApi {
|
||||||
let mut path = gupax_p2pool_dir.clone();
|
let mut path = gupax_p2pool_dir.clone();
|
||||||
path.push(file);
|
path.push(file);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
info!("GupaxP2poolApi | [{}] already exists, skipping...", path.display());
|
info!(
|
||||||
continue
|
"GupaxP2poolApi | [{}] already exists, skipping...",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
match std::fs::File::create(&path) {
|
match std::fs::File::create(&path) {
|
||||||
Ok(mut f) => {
|
Ok(mut f) => {
|
||||||
match file {
|
match file {
|
||||||
GUPAX_P2POOL_API_PAYOUT|GUPAX_P2POOL_API_XMR => writeln!(f, "0")?,
|
GUPAX_P2POOL_API_PAYOUT | GUPAX_P2POOL_API_XMR => writeln!(f, "0")?,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
info!("GupaxP2poolApi | [{}] create ... OK", path.display());
|
info!("GupaxP2poolApi | [{}] create ... OK", path.display());
|
||||||
},
|
}
|
||||||
Err(e) => { warn!("GupaxP2poolApi | [{}] create ... FAIL: {}", path.display(), e); return Err(TomlError::Io(e)) },
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"GupaxP2poolApi | [{}] create ... FAIL: {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return Err(TomlError::Io(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_all_files_and_update(&mut self) -> Result<(), TomlError> {
|
pub fn read_all_files_and_update(&mut self) -> Result<(), TomlError> {
|
||||||
let payout_u64 = match read_to_string(File::Payout, &self.path_payout)?.trim().parse::<u64>() {
|
let payout_u64 = match read_to_string(File::Payout, &self.path_payout)?
|
||||||
|
.trim()
|
||||||
|
.parse::<u64>()
|
||||||
|
{
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
Err(e) => { warn!("GupaxP2poolApi | [payout] parse error: {}", e); return Err(TomlError::Parse("payout")) }
|
Err(e) => {
|
||||||
|
warn!("GupaxP2poolApi | [payout] parse error: {}", e);
|
||||||
|
return Err(TomlError::Parse("payout"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let xmr = match read_to_string(File::Xmr, &self.path_xmr)?.trim().parse::<u64>() {
|
let xmr = match read_to_string(File::Xmr, &self.path_xmr)?
|
||||||
|
.trim()
|
||||||
|
.parse::<u64>()
|
||||||
|
{
|
||||||
Ok(o) => AtomicUnit::from_u64(o),
|
Ok(o) => AtomicUnit::from_u64(o),
|
||||||
Err(e) => { warn!("GupaxP2poolApi | [xmr] parse error: {}", e); return Err(TomlError::Parse("xmr")) }
|
Err(e) => {
|
||||||
|
warn!("GupaxP2poolApi | [xmr] parse error: {}", e);
|
||||||
|
return Err(TomlError::Parse("xmr"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let payout = HumanNumber::from_u64(payout_u64);
|
let payout = HumanNumber::from_u64(payout_u64);
|
||||||
let log = read_to_string(File::Log, &self.path_log)?;
|
let log = read_to_string(File::Log, &self.path_log)?;
|
||||||
|
@ -689,9 +813,15 @@ impl GupaxP2poolApi {
|
||||||
|
|
||||||
// Completely delete the [p2pool] folder and create defaults.
|
// Completely delete the [p2pool] folder and create defaults.
|
||||||
pub fn create_new(path: &PathBuf) -> Result<(), TomlError> {
|
pub fn create_new(path: &PathBuf) -> Result<(), TomlError> {
|
||||||
info!("GupaxP2poolApi | Deleting old folder at [{}]...", path.display());
|
info!(
|
||||||
|
"GupaxP2poolApi | Deleting old folder at [{}]...",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
std::fs::remove_dir_all(&path)?;
|
std::fs::remove_dir_all(&path)?;
|
||||||
info!("GupaxP2poolApi | Creating new default folder at [{}]...", path.display());
|
info!(
|
||||||
|
"GupaxP2poolApi | Creating new default folder at [{}]...",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
create_gupax_p2pool_dir(&path)?;
|
create_gupax_p2pool_dir(&path)?;
|
||||||
Self::create_all_files(&path)?;
|
Self::create_all_files(&path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -736,7 +866,13 @@ impl GupaxP2poolApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes the (date, atomic_unit, block) and updates [self] and the [PayoutOrd]
|
// Takes the (date, atomic_unit, block) and updates [self] and the [PayoutOrd]
|
||||||
pub fn add_payout(&mut self, formatted_log_line: &str, date: String, atomic_unit: AtomicUnit, block: HumanNumber) {
|
pub fn add_payout(
|
||||||
|
&mut self,
|
||||||
|
formatted_log_line: &str,
|
||||||
|
date: String,
|
||||||
|
atomic_unit: AtomicUnit,
|
||||||
|
block: HumanNumber,
|
||||||
|
) {
|
||||||
self.append_log(formatted_log_line);
|
self.append_log(formatted_log_line);
|
||||||
self.append_head_log_rev(formatted_log_line);
|
self.append_head_log_rev(formatted_log_line);
|
||||||
self.payout_u64 += 1;
|
self.payout_u64 += 1;
|
||||||
|
@ -757,23 +893,62 @@ impl GupaxP2poolApi {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
let mut file = match fs::OpenOptions::new().append(true).create(true).open(path) {
|
let mut file = match fs::OpenOptions::new().append(true).create(true).open(path) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => { error!("GupaxP2poolApi | Append [{}] ... FAIL: {}", path.display(), e); return Err(TomlError::Io(e)) },
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"GupaxP2poolApi | Append [{}] ... FAIL: {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return Err(TomlError::Io(e));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match writeln!(file, "{}", formatted_log_line) {
|
match writeln!(file, "{}", formatted_log_line) {
|
||||||
Ok(_) => { debug!("GupaxP2poolApi | Append [{}] ... OK", path.display()); Ok(()) },
|
Ok(_) => {
|
||||||
Err(e) => { error!("GupaxP2poolApi | Append [{}] ... FAIL: {}", path.display(), e); Err(TomlError::Io(e)) },
|
debug!("GupaxP2poolApi | Append [{}] ... OK", path.display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"GupaxP2poolApi | Append [{}] ... FAIL: {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
Err(TomlError::Io(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disk_overwrite(string: &str, path: &PathBuf) -> Result<(), TomlError> {
|
pub fn disk_overwrite(string: &str, path: &PathBuf) -> Result<(), TomlError> {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
let mut file = match fs::OpenOptions::new().write(true).truncate(true).create(true).open(path) {
|
let mut file = match fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.create(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => { error!("GupaxP2poolApi | Overwrite [{}] ... FAIL: {}", path.display(), e); return Err(TomlError::Io(e)) },
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"GupaxP2poolApi | Overwrite [{}] ... FAIL: {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return Err(TomlError::Io(e));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match writeln!(file, "{}", string) {
|
match writeln!(file, "{}", string) {
|
||||||
Ok(_) => { debug!("GupaxP2poolApi | Overwrite [{}] ... OK", path.display()); Ok(()) },
|
Ok(_) => {
|
||||||
Err(e) => { error!("GupaxP2poolApi | Overwrite [{}] ... FAIL: {}", path.display(), e); Err(TomlError::Io(e)) },
|
debug!("GupaxP2poolApi | Overwrite [{}] ... OK", path.display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"GupaxP2poolApi | Overwrite [{}] ... FAIL: {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
Err(TomlError::Io(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -818,7 +993,7 @@ impl From<std::fmt::Error> for TomlError {
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [File] Enum (for matching which file)
|
//---------------------------------------------------------------------------------------------------- [File] Enum (for matching which file)
|
||||||
#[derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub enum File {
|
pub enum File {
|
||||||
// State files
|
// State files
|
||||||
State, // state.toml | Gupax state
|
State, // state.toml | Gupax state
|
||||||
|
@ -832,7 +1007,7 @@ pub enum File {
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [Submenu] enum for [Status] tab
|
//---------------------------------------------------------------------------------------------------- [Submenu] enum for [Status] tab
|
||||||
#[derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub enum Submenu {
|
pub enum Submenu {
|
||||||
Processes,
|
Processes,
|
||||||
P2pool,
|
P2pool,
|
||||||
|
@ -857,7 +1032,7 @@ impl Display for Submenu {
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [PayoutView] enum for [Status/P2Pool] tab
|
//---------------------------------------------------------------------------------------------------- [PayoutView] enum for [Status/P2Pool] tab
|
||||||
// The enum buttons for selecting which "view" to sort the payout log in.
|
// The enum buttons for selecting which "view" to sort the payout log in.
|
||||||
#[derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub enum PayoutView {
|
pub enum PayoutView {
|
||||||
Latest, // Shows the most recent logs first
|
Latest, // Shows the most recent logs first
|
||||||
Oldest, // Shows the oldest logs first
|
Oldest, // Shows the oldest logs first
|
||||||
|
@ -884,7 +1059,7 @@ impl Display for PayoutView {
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [Hash] enum for [Status/P2Pool]
|
//---------------------------------------------------------------------------------------------------- [Hash] enum for [Status/P2Pool]
|
||||||
#[derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub enum Hash {
|
pub enum Hash {
|
||||||
Hash,
|
Hash,
|
||||||
Kilo,
|
Kilo,
|
||||||
|
@ -910,37 +1085,29 @@ impl Hash {
|
||||||
|
|
||||||
pub fn convert(f: f64, og: Self, new: Self) -> f64 {
|
pub fn convert(f: f64, og: Self, new: Self) -> f64 {
|
||||||
match og {
|
match og {
|
||||||
Self::Hash => {
|
Self::Hash => match new {
|
||||||
match new {
|
|
||||||
Self::Hash => f,
|
Self::Hash => f,
|
||||||
Self::Kilo => f / 1_000.0,
|
Self::Kilo => f / 1_000.0,
|
||||||
Self::Mega => f / 1_000_000.0,
|
Self::Mega => f / 1_000_000.0,
|
||||||
Self::Giga => f / 1_000_000_000.0,
|
Self::Giga => f / 1_000_000_000.0,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Self::Kilo => {
|
Self::Kilo => match new {
|
||||||
match new {
|
|
||||||
Self::Hash => f * 1_000.0,
|
Self::Hash => f * 1_000.0,
|
||||||
Self::Kilo => f,
|
Self::Kilo => f,
|
||||||
Self::Mega => f / 1_000.0,
|
Self::Mega => f / 1_000.0,
|
||||||
Self::Giga => f / 1_000_000.0,
|
Self::Giga => f / 1_000_000.0,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Self::Mega => {
|
Self::Mega => match new {
|
||||||
match new {
|
|
||||||
Self::Hash => f * 1_000_000.0,
|
Self::Hash => f * 1_000_000.0,
|
||||||
Self::Kilo => f * 1_000.0,
|
Self::Kilo => f * 1_000.0,
|
||||||
Self::Mega => f,
|
Self::Mega => f,
|
||||||
Self::Giga => f / 1_000.0,
|
Self::Giga => f / 1_000.0,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Self::Giga => {
|
Self::Giga => match new {
|
||||||
match new {
|
|
||||||
Self::Hash => f * 1_000_000_000.0,
|
Self::Hash => f * 1_000_000_000.0,
|
||||||
Self::Kilo => f * 1_000_000.0,
|
Self::Kilo => f * 1_000_000.0,
|
||||||
Self::Mega => f * 1_000.0,
|
Self::Mega => f * 1_000.0,
|
||||||
Self::Giga => f,
|
Self::Giga => f,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -956,7 +1123,7 @@ impl Display for Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [Node] Struct
|
//---------------------------------------------------------------------------------------------------- [Node] Struct
|
||||||
#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub ip: String,
|
pub ip: String,
|
||||||
pub rpc: String,
|
pub rpc: String,
|
||||||
|
@ -964,7 +1131,7 @@ pub struct Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [Pool] Struct
|
//---------------------------------------------------------------------------------------------------- [Pool] Struct
|
||||||
#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct Pool {
|
pub struct Pool {
|
||||||
pub rig: String,
|
pub rig: String,
|
||||||
pub ip: String,
|
pub ip: String,
|
||||||
|
@ -972,7 +1139,7 @@ pub struct Pool {
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [State] Struct
|
//---------------------------------------------------------------------------------------------------- [State] Struct
|
||||||
#[derive(Clone,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
pub gupax: Gupax,
|
pub gupax: Gupax,
|
||||||
|
@ -981,7 +1148,7 @@ pub struct State {
|
||||||
pub version: Arc<Mutex<Version>>,
|
pub version: Arc<Mutex<Version>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub submenu: Submenu,
|
pub submenu: Submenu,
|
||||||
pub payout_view: PayoutView,
|
pub payout_view: PayoutView,
|
||||||
|
@ -991,13 +1158,13 @@ pub struct Status {
|
||||||
pub hash_metric: Hash,
|
pub hash_metric: Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct Gupax {
|
pub struct Gupax {
|
||||||
pub simple: bool,
|
pub simple: bool,
|
||||||
pub auto_update: bool,
|
pub auto_update: bool,
|
||||||
pub auto_p2pool: bool,
|
pub auto_p2pool: bool,
|
||||||
pub auto_xmrig: bool,
|
pub auto_xmrig: bool,
|
||||||
// pub auto_monero: bool,
|
// pub auto_monero: bool,
|
||||||
pub ask_before_quit: bool,
|
pub ask_before_quit: bool,
|
||||||
pub save_before_quit: bool,
|
pub save_before_quit: bool,
|
||||||
pub update_via_tor: bool,
|
pub update_via_tor: bool,
|
||||||
|
@ -1012,7 +1179,7 @@ pub struct Gupax {
|
||||||
pub ratio: Ratio,
|
pub ratio: Ratio,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct P2pool {
|
pub struct P2pool {
|
||||||
pub simple: bool,
|
pub simple: bool,
|
||||||
pub mini: bool,
|
pub mini: bool,
|
||||||
|
@ -1036,7 +1203,7 @@ pub struct P2pool {
|
||||||
pub selected_zmq: String,
|
pub selected_zmq: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct Xmrig {
|
pub struct Xmrig {
|
||||||
pub simple: bool,
|
pub simple: bool,
|
||||||
pub pause: u8,
|
pub pause: u8,
|
||||||
|
@ -1060,7 +1227,7 @@ pub struct Xmrig {
|
||||||
pub selected_port: String,
|
pub selected_port: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Version {
|
pub struct Version {
|
||||||
pub gupax: String,
|
pub gupax: String,
|
||||||
pub p2pool: String,
|
pub p2pool: String,
|
||||||
|
@ -1412,8 +1579,8 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn create_and_serde_gupax_p2pool_api() {
|
fn create_and_serde_gupax_p2pool_api() {
|
||||||
use crate::disk::GupaxP2poolApi;
|
use crate::disk::GupaxP2poolApi;
|
||||||
use crate::xmr::PayoutOrd;
|
|
||||||
use crate::xmr::AtomicUnit;
|
use crate::xmr::AtomicUnit;
|
||||||
|
use crate::xmr::PayoutOrd;
|
||||||
|
|
||||||
// Get API dir, fill paths.
|
// Get API dir, fill paths.
|
||||||
let mut api = GupaxP2poolApi::new();
|
let mut api = GupaxP2poolApi::new();
|
||||||
|
@ -1440,7 +1607,9 @@ mod test {
|
||||||
assert_eq!(api.payout_u64, 1);
|
assert_eq!(api.payout_u64, 1);
|
||||||
assert_eq!(api.xmr.to_u64(), 2);
|
assert_eq!(api.xmr.to_u64(), 2);
|
||||||
assert!(!api.payout_ord.is_empty());
|
assert!(!api.payout_ord.is_empty());
|
||||||
assert!(api.log.contains("2022-01-27 01:30:23.1377 | 0.000000000001 XMR | Block 2,642,816"));
|
assert!(api
|
||||||
|
.log
|
||||||
|
.contains("2022-01-27 01:30:23.1377 | 0.000000000001 XMR | Block 2,642,816"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
File diff suppressed because one or more lines are too long
410
src/gupax.rs
410
src/gupax.rs
|
@ -16,30 +16,18 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::State;
|
use crate::State;
|
||||||
|
use crate::{constants::*, macros::*, update::*, ErrorState, Restart, Tab};
|
||||||
use egui::{
|
use egui::{
|
||||||
TextEdit,
|
Button, Checkbox, Label, ProgressBar, RichText, SelectableLabel, Slider, Spinner, TextEdit,
|
||||||
TextStyle,
|
TextStyle, TextStyle::Monospace, Vec2,
|
||||||
TextStyle::Monospace,
|
|
||||||
Checkbox,ProgressBar,Spinner,Button,Label,Slider,
|
|
||||||
SelectableLabel,
|
|
||||||
RichText,
|
|
||||||
Vec2,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
constants::*,
|
|
||||||
update::*,
|
|
||||||
ErrorState,
|
|
||||||
Restart,
|
|
||||||
Tab,
|
|
||||||
macros::*,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
thread,
|
|
||||||
sync::{Arc,Mutex},
|
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use serde::{Serialize,Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
path::Path,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- FileWindow
|
//---------------------------------------------------------------------------------------------------- FileWindow
|
||||||
// Struct for writing/reading the path state.
|
// Struct for writing/reading the path state.
|
||||||
|
@ -65,7 +53,7 @@ impl FileWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
P2pool,
|
P2pool,
|
||||||
Xmrig,
|
Xmrig,
|
||||||
|
@ -73,7 +61,7 @@ pub enum FileType {
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Ratio Lock
|
//---------------------------------------------------------------------------------------------------- Ratio Lock
|
||||||
// Enum for the lock ratio in the advanced tab.
|
// Enum for the lock ratio in the advanced tab.
|
||||||
#[derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub enum Ratio {
|
pub enum Ratio {
|
||||||
Width,
|
Width,
|
||||||
Height,
|
Height,
|
||||||
|
@ -95,169 +83,361 @@ impl crate::disk::Gupax {
|
||||||
height: f32,
|
height: f32,
|
||||||
frame: &mut eframe::Frame,
|
frame: &mut eframe::Frame,
|
||||||
_ctx: &egui::Context,
|
_ctx: &egui::Context,
|
||||||
ui: &mut egui::Ui
|
ui: &mut egui::Ui,
|
||||||
) {
|
) {
|
||||||
// Update button + Progress bar
|
// Update button + Progress bar
|
||||||
debug!("Gupax Tab | Rendering [Update] button + progress bar");
|
debug!("Gupax Tab | Rendering [Update] button + progress bar");
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
let button = if self.simple { height/5.0 } else { height/15.0 };
|
let button = if self.simple {
|
||||||
let height = if self.simple { height/5.0 } else { height/10.0 };
|
height / 5.0
|
||||||
|
} else {
|
||||||
|
height / 15.0
|
||||||
|
};
|
||||||
|
let height = if self.simple {
|
||||||
|
height / 5.0
|
||||||
|
} else {
|
||||||
|
height / 10.0
|
||||||
|
};
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
let updating = *lock2!(update,updating);
|
let updating = *lock2!(update, updating);
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
// If [Gupax] is being built for a Linux distro,
|
// If [Gupax] is being built for a Linux distro,
|
||||||
// disable built-in updating completely.
|
// disable built-in updating completely.
|
||||||
#[cfg(feature = "distro")]
|
#[cfg(feature = "distro")]
|
||||||
ui.set_enabled(false);
|
ui.set_enabled(false);
|
||||||
#[cfg(feature = "distro")]
|
#[cfg(feature = "distro")]
|
||||||
ui.add_sized([width, button], Button::new("Updates are disabled")).on_disabled_hover_text(DISTRO_NO_UPDATE);
|
ui.add_sized([width, button], Button::new("Updates are disabled"))
|
||||||
|
.on_disabled_hover_text(DISTRO_NO_UPDATE);
|
||||||
#[cfg(not(feature = "distro"))]
|
#[cfg(not(feature = "distro"))]
|
||||||
ui.set_enabled(!updating);
|
ui.set_enabled(!updating);
|
||||||
#[cfg(not(feature = "distro"))]
|
#[cfg(not(feature = "distro"))]
|
||||||
if ui.add_sized([width, button], Button::new("Check for updates")).on_hover_text(GUPAX_UPDATE).clicked() {
|
if ui
|
||||||
|
.add_sized([width, button], Button::new("Check for updates"))
|
||||||
|
.on_hover_text(GUPAX_UPDATE)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
Update::spawn_thread(og, self, state_path, update, error_state, restart);
|
Update::spawn_thread(og, self, state_path, update, error_state, restart);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.set_enabled(updating);
|
ui.set_enabled(updating);
|
||||||
let prog = *lock2!(update,prog);
|
let prog = *lock2!(update, prog);
|
||||||
let msg = format!("{}\n{}{}", *lock2!(update,msg), prog, "%");
|
let msg = format!("{}\n{}{}", *lock2!(update, msg), prog, "%");
|
||||||
ui.add_sized([width, height*1.4], Label::new(RichText::new(msg)));
|
ui.add_sized([width, height * 1.4], Label::new(RichText::new(msg)));
|
||||||
let height = height/2.0;
|
let height = height / 2.0;
|
||||||
if updating {
|
if updating {
|
||||||
ui.add_sized([width, height], Spinner::new().size(height));
|
ui.add_sized([width, height], Spinner::new().size(height));
|
||||||
} else {
|
} else {
|
||||||
ui.add_sized([width, height], Label::new("..."));
|
ui.add_sized([width, height], Label::new("..."));
|
||||||
}
|
}
|
||||||
ui.add_sized([width, height], ProgressBar::new(lock2!(update,prog).round() / 100.0));
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
ProgressBar::new(lock2!(update, prog).round() / 100.0),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!("Gupax Tab | Rendering bool buttons");
|
debug!("Gupax Tab | Rendering bool buttons");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
let width = (width - SPACE*12.0)/6.0;
|
let width = (width - SPACE * 12.0) / 6.0;
|
||||||
let height = if self.simple { height/10.0 } else { height/15.0 };
|
let height = if self.simple {
|
||||||
|
height / 10.0
|
||||||
|
} else {
|
||||||
|
height / 15.0
|
||||||
|
};
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.update_via_tor, "Update via Tor")).on_hover_text(GUPAX_UPDATE_VIA_TOR);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.update_via_tor, "Update via Tor"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_UPDATE_VIA_TOR);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.auto_update, "Auto-Update")).on_hover_text(GUPAX_AUTO_UPDATE);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.auto_update, "Auto-Update"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_AUTO_UPDATE);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool")).on_hover_text(GUPAX_AUTO_P2POOL);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_AUTO_P2POOL);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig")).on_hover_text(GUPAX_AUTO_XMRIG);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_AUTO_XMRIG);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.ask_before_quit, "Ask before quit")).on_hover_text(GUPAX_ASK_BEFORE_QUIT);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.ask_before_quit, "Ask before quit"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_ASK_BEFORE_QUIT);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.save_before_quit, "Save before quit")).on_hover_text(GUPAX_SAVE_BEFORE_QUIT);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.save_before_quit, "Save before quit"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_SAVE_BEFORE_QUIT);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.simple { return }
|
if self.simple {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
debug!("Gupax Tab | Rendering P2Pool/XMRig path selection");
|
debug!("Gupax Tab | Rendering P2Pool/XMRig path selection");
|
||||||
// P2Pool/XMRig binary path selection
|
// P2Pool/XMRig binary path selection
|
||||||
let height = height/28.0;
|
let height = height / 28.0;
|
||||||
let text_edit = (ui.available_width()/10.0)-SPACE;
|
let text_edit = (ui.available_width() / 10.0) - SPACE;
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("P2Pool/XMRig PATHs").underline().color(LIGHT_GRAY))).on_hover_text("Gupax is online");
|
ui.add_sized(
|
||||||
|
[ui.available_width(), height / 2.0],
|
||||||
|
Label::new(
|
||||||
|
RichText::new("P2Pool/XMRig PATHs")
|
||||||
|
.underline()
|
||||||
|
.color(LIGHT_GRAY),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text("Gupax is online");
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if self.p2pool_path.is_empty() {
|
if self.p2pool_path.is_empty() {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ➖").color(LIGHT_GRAY))).on_hover_text(P2POOL_PATH_EMPTY);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new("P2Pool Binary Path ➖").color(LIGHT_GRAY)),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_PATH_EMPTY);
|
||||||
} else if !Self::path_is_file(&self.p2pool_path) {
|
} else if !Self::path_is_file(&self.p2pool_path) {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ❌").color(RED))).on_hover_text(P2POOL_PATH_NOT_FILE);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new("P2Pool Binary Path ❌").color(RED)),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_PATH_NOT_FILE);
|
||||||
} else if !crate::update::check_p2pool_path(&self.p2pool_path) {
|
} else if !crate::update::check_p2pool_path(&self.p2pool_path) {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ❌").color(RED))).on_hover_text(P2POOL_PATH_NOT_VALID);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new("P2Pool Binary Path ❌").color(RED)),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_PATH_NOT_VALID);
|
||||||
} else {
|
} else {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new("P2Pool Binary Path ✔").color(GREEN))).on_hover_text(P2POOL_PATH_OK);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new("P2Pool Binary Path ✔").color(GREEN)),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_PATH_OK);
|
||||||
}
|
}
|
||||||
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
||||||
ui.set_enabled(!lock!(file_window).thread);
|
ui.set_enabled(!lock!(file_window).thread);
|
||||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||||
Self::spawn_file_window_thread(file_window, FileType::P2pool);
|
Self::spawn_file_window_thread(file_window, FileType::P2pool);
|
||||||
}
|
}
|
||||||
ui.add_sized([ui.available_width(), height], TextEdit::singleline(&mut self.p2pool_path)).on_hover_text(GUPAX_PATH_P2POOL);
|
ui.add_sized(
|
||||||
|
[ui.available_width(), height],
|
||||||
|
TextEdit::singleline(&mut self.p2pool_path),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_PATH_P2POOL);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if self.xmrig_path.is_empty() {
|
if self.xmrig_path.is_empty() {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ➖").color(LIGHT_GRAY))).on_hover_text(XMRIG_PATH_EMPTY);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new(" XMRig Binary Path ➖").color(LIGHT_GRAY)),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_PATH_EMPTY);
|
||||||
} else if !Self::path_is_file(&self.xmrig_path) {
|
} else if !Self::path_is_file(&self.xmrig_path) {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ❌").color(RED))).on_hover_text(XMRIG_PATH_NOT_FILE);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new(" XMRig Binary Path ❌").color(RED)),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_PATH_NOT_FILE);
|
||||||
} else if !crate::update::check_xmrig_path(&self.xmrig_path) {
|
} else if !crate::update::check_xmrig_path(&self.xmrig_path) {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ❌").color(RED))).on_hover_text(XMRIG_PATH_NOT_VALID);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new(" XMRig Binary Path ❌").color(RED)),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_PATH_NOT_VALID);
|
||||||
} else {
|
} else {
|
||||||
ui.add_sized([text_edit, height], Label::new(RichText::new(" XMRig Binary Path ✔").color(GREEN))).on_hover_text(XMRIG_PATH_OK);
|
ui.add_sized(
|
||||||
|
[text_edit, height],
|
||||||
|
Label::new(RichText::new(" XMRig Binary Path ✔").color(GREEN)),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_PATH_OK);
|
||||||
}
|
}
|
||||||
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
|
||||||
ui.set_enabled(!lock!(file_window).thread);
|
ui.set_enabled(!lock!(file_window).thread);
|
||||||
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
|
||||||
Self::spawn_file_window_thread(file_window, FileType::Xmrig);
|
Self::spawn_file_window_thread(file_window, FileType::Xmrig);
|
||||||
}
|
}
|
||||||
ui.add_sized([ui.available_width(), height], TextEdit::singleline(&mut self.xmrig_path)).on_hover_text(GUPAX_PATH_XMRIG);
|
ui.add_sized(
|
||||||
|
[ui.available_width(), height],
|
||||||
|
TextEdit::singleline(&mut self.xmrig_path),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_PATH_XMRIG);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
let mut guard = lock!(file_window);
|
let mut guard = lock!(file_window);
|
||||||
if guard.picked_p2pool { self.p2pool_path = guard.p2pool_path.clone(); guard.picked_p2pool = false; }
|
if guard.picked_p2pool {
|
||||||
if guard.picked_xmrig { self.xmrig_path = guard.xmrig_path.clone(); guard.picked_xmrig = false; }
|
self.p2pool_path = guard.p2pool_path.clone();
|
||||||
|
guard.picked_p2pool = false;
|
||||||
|
}
|
||||||
|
if guard.picked_xmrig {
|
||||||
|
self.xmrig_path = guard.xmrig_path.clone();
|
||||||
|
guard.picked_xmrig = false;
|
||||||
|
}
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
let height = ui.available_height()/6.0;
|
let height = ui.available_height() / 6.0;
|
||||||
|
|
||||||
// Saved [Tab]
|
// Saved [Tab]
|
||||||
debug!("Gupax Tab | Rendering [Tab] selector");
|
debug!("Gupax Tab | Rendering [Tab] selector");
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
let width = (width/5.0)-(SPACE*1.93);
|
let width = (width / 5.0) - (SPACE * 1.93);
|
||||||
ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("Default Tab").underline().color(LIGHT_GRAY))).on_hover_text(GUPAX_TAB);
|
ui.add_sized(
|
||||||
|
[ui.available_width(), height / 2.0],
|
||||||
|
Label::new(RichText::new("Default Tab").underline().color(LIGHT_GRAY)),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_TAB);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).on_hover_text(GUPAX_TAB_ABOUT).clicked() { self.tab = Tab::About; }
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.tab == Tab::About, "About"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_TAB_ABOUT)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.tab = Tab::About;
|
||||||
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Status, "Status")).on_hover_text(GUPAX_TAB_STATUS).clicked() { self.tab = Tab::Status; }
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.tab == Tab::Status, "Status"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_TAB_STATUS)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.tab = Tab::Status;
|
||||||
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Gupax, "Gupax")).on_hover_text(GUPAX_TAB_GUPAX).clicked() { self.tab = Tab::Gupax; }
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.tab == Tab::Gupax, "Gupax"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_TAB_GUPAX)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.tab = Tab::Gupax;
|
||||||
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool")).on_hover_text(GUPAX_TAB_P2POOL).clicked() { self.tab = Tab::P2pool; }
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_TAB_P2POOL)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.tab = Tab::P2pool;
|
||||||
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig")).on_hover_text(GUPAX_TAB_XMRIG).clicked() { self.tab = Tab::Xmrig; }
|
if ui
|
||||||
})});
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_TAB_XMRIG)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.tab = Tab::Xmrig;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
// Gupax App resolution sliders
|
// Gupax App resolution sliders
|
||||||
debug!("Gupax Tab | Rendering resolution sliders");
|
debug!("Gupax Tab | Rendering resolution sliders");
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
ui.add_sized([ui.available_width(), height/2.0], Label::new(RichText::new("Width/Height Adjust").underline().color(LIGHT_GRAY))).on_hover_text(GUPAX_ADJUST);
|
ui.add_sized(
|
||||||
|
[ui.available_width(), height / 2.0],
|
||||||
|
Label::new(
|
||||||
|
RichText::new("Width/Height Adjust")
|
||||||
|
.underline()
|
||||||
|
.color(LIGHT_GRAY),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_ADJUST);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
let width = width/10.0;
|
let width = width / 10.0;
|
||||||
ui.spacing_mut().icon_width = width / 25.0;
|
ui.spacing_mut().icon_width = width / 25.0;
|
||||||
ui.spacing_mut().slider_width = width*7.6;
|
ui.spacing_mut().slider_width = width * 7.6;
|
||||||
match self.ratio {
|
match self.ratio {
|
||||||
Ratio::None => (),
|
Ratio::None => (),
|
||||||
Ratio::Width => {
|
Ratio::Width => {
|
||||||
let width = self.selected_width as f64;
|
let width = self.selected_width as f64;
|
||||||
let height = (width / 1.333).round();
|
let height = (width / 1.333).round();
|
||||||
self.selected_height = height as u16;
|
self.selected_height = height as u16;
|
||||||
},
|
}
|
||||||
Ratio::Height => {
|
Ratio::Height => {
|
||||||
let height = self.selected_height as f64;
|
let height = self.selected_height as f64;
|
||||||
let width = (height * 1.333).round();
|
let width = (height * 1.333).round();
|
||||||
self.selected_width = width as u16;
|
self.selected_width = width as u16;
|
||||||
},
|
|
||||||
}
|
}
|
||||||
let height = height/3.5;
|
}
|
||||||
|
let height = height / 3.5;
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.set_enabled(self.ratio != Ratio::Height);
|
ui.set_enabled(self.ratio != Ratio::Height);
|
||||||
ui.add_sized([width, height], Label::new(format!(" Width [{}-{}]:", APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16)));
|
ui.add_sized(
|
||||||
ui.add_sized([width, height], Slider::new(&mut self.selected_width, APP_MIN_WIDTH as u16..=APP_MAX_WIDTH as u16)).on_hover_text(GUPAX_WIDTH);
|
[width, height],
|
||||||
|
Label::new(format!(
|
||||||
|
" Width [{}-{}]:",
|
||||||
|
APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Slider::new(
|
||||||
|
&mut self.selected_width,
|
||||||
|
APP_MIN_WIDTH as u16..=APP_MAX_WIDTH as u16,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_WIDTH);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.set_enabled(self.ratio != Ratio::Width);
|
ui.set_enabled(self.ratio != Ratio::Width);
|
||||||
ui.add_sized([width, height], Label::new(format!("Height [{}-{}]:", APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16)));
|
ui.add_sized(
|
||||||
ui.add_sized([width, height], Slider::new(&mut self.selected_height, APP_MIN_HEIGHT as u16..=APP_MAX_HEIGHT as u16)).on_hover_text(GUPAX_HEIGHT);
|
[width, height],
|
||||||
|
Label::new(format!(
|
||||||
|
"Height [{}-{}]:",
|
||||||
|
APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Slider::new(
|
||||||
|
&mut self.selected_height,
|
||||||
|
APP_MIN_HEIGHT as u16..=APP_MAX_HEIGHT as u16,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_HEIGHT);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_sized([width, height], Label::new(format!("Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")));
|
ui.add_sized(
|
||||||
ui.add_sized([width, height], Slider::new(&mut self.selected_scale, APP_MIN_SCALE..=APP_MAX_SCALE).step_by(0.1)).on_hover_text(GUPAX_SCALE);
|
[width, height],
|
||||||
|
Label::new(format!("Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")),
|
||||||
|
);
|
||||||
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Slider::new(&mut self.selected_scale, APP_MIN_SCALE..=APP_MAX_SCALE)
|
||||||
|
.step_by(0.1),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_SCALE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Button);
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Button);
|
||||||
|
@ -265,17 +445,50 @@ impl crate::disk::Gupax {
|
||||||
// Width/Height locks
|
// Width/Height locks
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
use Ratio::*;
|
use Ratio::*;
|
||||||
let width = (width/4.0)-(SPACE*1.5);
|
let width = (width / 4.0) - (SPACE * 1.5);
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.ratio == Width, "Lock to width")).on_hover_text(GUPAX_LOCK_WIDTH).clicked() { self.ratio = Width; }
|
if ui
|
||||||
ui.separator();
|
.add_sized(
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.ratio == Height, "Lock to height")).on_hover_text(GUPAX_LOCK_HEIGHT).clicked() { self.ratio = Height; }
|
[width, height],
|
||||||
ui.separator();
|
SelectableLabel::new(self.ratio == Width, "Lock to width"),
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.ratio == None, "No lock")).on_hover_text(GUPAX_NO_LOCK).clicked() { self.ratio = None; }
|
)
|
||||||
if ui.add_sized([width, height], Button::new("Set")).on_hover_text(GUPAX_SET).clicked() {
|
.on_hover_text(GUPAX_LOCK_WIDTH)
|
||||||
let size = Vec2::new(self.selected_width as f32, self.selected_height as f32);
|
.clicked()
|
||||||
ui.ctx().send_viewport_cmd(egui::viewport::ViewportCommand::InnerSize(size));
|
{
|
||||||
|
self.ratio = Width;
|
||||||
}
|
}
|
||||||
})});
|
ui.separator();
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.ratio == Height, "Lock to height"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_LOCK_HEIGHT)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.ratio = Height;
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.ratio == None, "No lock"),
|
||||||
|
)
|
||||||
|
.on_hover_text(GUPAX_NO_LOCK)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.ratio = None;
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.add_sized([width, height], Button::new("Set"))
|
||||||
|
.on_hover_text(GUPAX_SET)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
let size = Vec2::new(self.selected_width as f32, self.selected_height as f32);
|
||||||
|
ui.ctx()
|
||||||
|
.send_viewport_cmd(egui::viewport::ViewportCommand::InnerSize(size));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a path is a valid path to a file.
|
// Checks if a path is a valid path to a file.
|
||||||
|
@ -297,15 +510,24 @@ impl crate::disk::Gupax {
|
||||||
};
|
};
|
||||||
let file_window = file_window.clone();
|
let file_window = file_window.clone();
|
||||||
lock!(file_window).thread = true;
|
lock!(file_window).thread = true;
|
||||||
thread::spawn(move|| {
|
thread::spawn(move || {
|
||||||
match rfd::FileDialog::new().set_title(&format!("Select {} Binary for Gupax", name)).pick_file() {
|
match rfd::FileDialog::new()
|
||||||
|
.set_title(&format!("Select {} Binary for Gupax", name))
|
||||||
|
.pick_file()
|
||||||
|
{
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
info!("Gupax | Path selected for {} ... {}", name, path.display());
|
info!("Gupax | Path selected for {} ... {}", name, path.display());
|
||||||
match file_type {
|
match file_type {
|
||||||
P2pool => { lock!(file_window).p2pool_path = path.display().to_string(); lock!(file_window).picked_p2pool = true; },
|
P2pool => {
|
||||||
Xmrig => { lock!(file_window).xmrig_path = path.display().to_string(); lock!(file_window).picked_xmrig = true; },
|
lock!(file_window).p2pool_path = path.display().to_string();
|
||||||
|
lock!(file_window).picked_p2pool = true;
|
||||||
|
}
|
||||||
|
Xmrig => {
|
||||||
|
lock!(file_window).xmrig_path = path.display().to_string();
|
||||||
|
lock!(file_window).picked_xmrig = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
None => info!("Gupax | No path selected for {}", name),
|
None => info!("Gupax | No path selected for {}", name),
|
||||||
};
|
};
|
||||||
lock!(file_window).thread = false;
|
lock!(file_window).thread = false;
|
||||||
|
|
1030
src/helper.rs
1030
src/helper.rs
File diff suppressed because it is too large
Load diff
116
src/human.rs
116
src/human.rs
|
@ -51,7 +51,12 @@ impl HumanTime {
|
||||||
HumanTime(Duration::from_secs(u))
|
HumanTime(Duration::from_secs(u))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plural(f: &mut std::fmt::Formatter, started: &mut bool, name: &str, value: u64) -> std::fmt::Result {
|
fn plural(
|
||||||
|
f: &mut std::fmt::Formatter,
|
||||||
|
started: &mut bool,
|
||||||
|
name: &str,
|
||||||
|
value: u64,
|
||||||
|
) -> std::fmt::Result {
|
||||||
if value > 0 {
|
if value > 0 {
|
||||||
if *started {
|
if *started {
|
||||||
f.write_str(", ")?;
|
f.write_str(", ")?;
|
||||||
|
@ -206,7 +211,7 @@ impl HumanNumber {
|
||||||
buf.write_formatted(&f, &LOCALE);
|
buf.write_formatted(&f, &LOCALE);
|
||||||
string.push_str(buf.as_str());
|
string.push_str(buf.as_str());
|
||||||
string.push_str(" H/s");
|
string.push_str(" H/s");
|
||||||
},
|
}
|
||||||
None => string.push_str("??? H/s"),
|
None => string.push_str("??? H/s"),
|
||||||
}
|
}
|
||||||
if n != 2 {
|
if n != 2 {
|
||||||
|
@ -214,7 +219,7 @@ impl HumanNumber {
|
||||||
n += 1;
|
n += 1;
|
||||||
} else {
|
} else {
|
||||||
string.push(']');
|
string.push(']');
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +239,7 @@ impl HumanNumber {
|
||||||
n += 1;
|
n += 1;
|
||||||
} else {
|
} else {
|
||||||
string.push(']');
|
string.push(']');
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self(string)
|
Self(string)
|
||||||
|
@ -242,14 +247,14 @@ impl HumanNumber {
|
||||||
// [1_000_000] -> [1.000 MH/s]
|
// [1_000_000] -> [1.000 MH/s]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_u64_to_megahash_3_point(hash: u64) -> Self {
|
pub fn from_u64_to_megahash_3_point(hash: u64) -> Self {
|
||||||
let hash = (hash as f64)/1_000_000.0;
|
let hash = (hash as f64) / 1_000_000.0;
|
||||||
let hash = format!("{:.3} MH/s", hash);
|
let hash = format!("{:.3} MH/s", hash);
|
||||||
Self(hash)
|
Self(hash)
|
||||||
}
|
}
|
||||||
// [1_000_000_000] -> [1.000 GH/s]
|
// [1_000_000_000] -> [1.000 GH/s]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_u64_to_gigahash_3_point(hash: u64) -> Self {
|
pub fn from_u64_to_gigahash_3_point(hash: u64) -> Self {
|
||||||
let hash = (hash as f64)/1_000_000_000.0;
|
let hash = (hash as f64) / 1_000_000_000.0;
|
||||||
let hash = format!("{:.3} GH/s", hash);
|
let hash = format!("{:.3} GH/s", hash);
|
||||||
Self(hash)
|
Self(hash)
|
||||||
}
|
}
|
||||||
|
@ -278,10 +283,22 @@ mod test {
|
||||||
assert!(HumanNumber::to_percent(0.001).to_string() == "0%");
|
assert!(HumanNumber::to_percent(0.001).to_string() == "0%");
|
||||||
assert!(HumanNumber::to_percent(12.123123123123).to_string() == "12.12%");
|
assert!(HumanNumber::to_percent(12.123123123123).to_string() == "12.12%");
|
||||||
assert!(HumanNumber::to_percent_3_point(0.001).to_string() == "0.001%");
|
assert!(HumanNumber::to_percent_3_point(0.001).to_string() == "0.001%");
|
||||||
assert!(HumanNumber::from_hashrate([Some(123.1), Some(11111.1), None]).to_string() == "[123 H/s, 11,111 H/s, ??? H/s]");
|
assert!(
|
||||||
assert!(HumanNumber::from_hashrate([None, Some(1.123), Some(123123.312)]).to_string() == "[??? H/s, 1 H/s, 123,123 H/s]");
|
HumanNumber::from_hashrate([Some(123.1), Some(11111.1), None]).to_string()
|
||||||
assert!(HumanNumber::from_load([Some(123.1234), Some(321.321), None]).to_string() == "[123.12, 321.32, ???]");
|
== "[123 H/s, 11,111 H/s, ??? H/s]"
|
||||||
assert!(HumanNumber::from_load([None, Some(4321.43), Some(1234.1)]).to_string() == "[???, 4321.43, 1234.10]");
|
);
|
||||||
|
assert!(
|
||||||
|
HumanNumber::from_hashrate([None, Some(1.123), Some(123123.312)]).to_string()
|
||||||
|
== "[??? H/s, 1 H/s, 123,123 H/s]"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanNumber::from_load([Some(123.1234), Some(321.321), None]).to_string()
|
||||||
|
== "[123.12, 321.32, ???]"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanNumber::from_load([None, Some(4321.43), Some(1234.1)]).to_string()
|
||||||
|
== "[???, 4321.43, 1234.10]"
|
||||||
|
);
|
||||||
assert!(HumanNumber::from_f32(123_123.123123123).to_string() == "123,123");
|
assert!(HumanNumber::from_f32(123_123.123123123).to_string() == "123,123");
|
||||||
assert!(HumanNumber::from_f64(123_123_123.123123123123123).to_string() == "123,123,123");
|
assert!(HumanNumber::from_f64(123_123_123.123123123123123).to_string() == "123,123,123");
|
||||||
assert!(HumanNumber::from_u16(1_000).to_string() == "1,000");
|
assert!(HumanNumber::from_u16(1_000).to_string() == "1,000");
|
||||||
|
@ -299,16 +316,31 @@ mod test {
|
||||||
assert!(HumanNumber::from_u64(1_000_000_000_000).to_string() == "1,000,000,000,000");
|
assert!(HumanNumber::from_u64(1_000_000_000_000).to_string() == "1,000,000,000,000");
|
||||||
assert!(HumanNumber::from_u64(10_000_000_000_000).to_string() == "10,000,000,000,000");
|
assert!(HumanNumber::from_u64(10_000_000_000_000).to_string() == "10,000,000,000,000");
|
||||||
assert!(HumanNumber::from_u64(100_000_000_000_000).to_string() == "100,000,000,000,000");
|
assert!(HumanNumber::from_u64(100_000_000_000_000).to_string() == "100,000,000,000,000");
|
||||||
assert!(HumanNumber::from_u64(1_000_000_000_000_000).to_string() == "1,000,000,000,000,000");
|
assert!(
|
||||||
assert!(HumanNumber::from_u64(10_000_000_000_000_000).to_string() == "10,000,000,000,000,000");
|
HumanNumber::from_u64(1_000_000_000_000_000).to_string() == "1,000,000,000,000,000"
|
||||||
assert!(HumanNumber::from_u64(18_446_744_073_709_551_615).to_string() == "18,446,744,073,709,551,615");
|
);
|
||||||
assert!(HumanNumber::from_u128(18_446_744_073_709_551_616).to_string() == "18,446,744,073,709,551,616");
|
assert!(
|
||||||
assert!(HumanNumber::from_u128(100_000_000_000_000_000_000).to_string() == "100,000,000,000,000,000,000");
|
HumanNumber::from_u64(10_000_000_000_000_000).to_string() == "10,000,000,000,000,000"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanNumber::from_u64(18_446_744_073_709_551_615).to_string()
|
||||||
|
== "18,446,744,073,709,551,615"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanNumber::from_u128(18_446_744_073_709_551_616).to_string()
|
||||||
|
== "18,446,744,073,709,551,616"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanNumber::from_u128(100_000_000_000_000_000_000).to_string()
|
||||||
|
== "100,000,000,000,000,000,000"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
HumanNumber::from_u128(340_282_366_920_938_463_463_374_607_431_768_211_455).to_string(),
|
HumanNumber::from_u128(340_282_366_920_938_463_463_374_607_431_768_211_455).to_string(),
|
||||||
"340,282,366,920,938,463,463,374,607,431,768,211,455",
|
"340,282,366,920,938,463,463,374,607,431,768,211,455",
|
||||||
);
|
);
|
||||||
assert!(HumanNumber::from_u64_to_gigahash_3_point(1_000_000_000).to_string() == "1.000 GH/s");
|
assert!(
|
||||||
|
HumanNumber::from_u64_to_gigahash_3_point(1_000_000_000).to_string() == "1.000 GH/s"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -321,29 +353,57 @@ mod test {
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(59)).to_string() == "59 seconds");
|
assert!(HumanTime::into_human(Duration::from_secs(59)).to_string() == "59 seconds");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(60)).to_string() == "1 minute");
|
assert!(HumanTime::into_human(Duration::from_secs(60)).to_string() == "1 minute");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(61)).to_string() == "1 minute, 1 second");
|
assert!(HumanTime::into_human(Duration::from_secs(61)).to_string() == "1 minute, 1 second");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(62)).to_string() == "1 minute, 2 seconds");
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(62)).to_string() == "1 minute, 2 seconds"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(120)).to_string() == "2 minutes");
|
assert!(HumanTime::into_human(Duration::from_secs(120)).to_string() == "2 minutes");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(121)).to_string() == "2 minutes, 1 second");
|
assert!(
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(122)).to_string() == "2 minutes, 2 seconds");
|
HumanTime::into_human(Duration::from_secs(121)).to_string() == "2 minutes, 1 second"
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(179)).to_string() == "2 minutes, 59 seconds");
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(3599)).to_string() == "59 minutes, 59 seconds");
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(122)).to_string() == "2 minutes, 2 seconds"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(179)).to_string() == "2 minutes, 59 seconds"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(3599)).to_string()
|
||||||
|
== "59 minutes, 59 seconds"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(3600)).to_string() == "1 hour");
|
assert!(HumanTime::into_human(Duration::from_secs(3600)).to_string() == "1 hour");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(3601)).to_string() == "1 hour, 1 second");
|
assert!(HumanTime::into_human(Duration::from_secs(3601)).to_string() == "1 hour, 1 second");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(3602)).to_string() == "1 hour, 2 seconds");
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(3602)).to_string() == "1 hour, 2 seconds"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(3660)).to_string() == "1 hour, 1 minute");
|
assert!(HumanTime::into_human(Duration::from_secs(3660)).to_string() == "1 hour, 1 minute");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(3720)).to_string() == "1 hour, 2 minutes");
|
assert!(
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(86399)).to_string() == "23 hours, 59 minutes, 59 seconds");
|
HumanTime::into_human(Duration::from_secs(3720)).to_string() == "1 hour, 2 minutes"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(86399)).to_string()
|
||||||
|
== "23 hours, 59 minutes, 59 seconds"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(86400)).to_string() == "1 day");
|
assert!(HumanTime::into_human(Duration::from_secs(86400)).to_string() == "1 day");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(86401)).to_string() == "1 day, 1 second");
|
assert!(HumanTime::into_human(Duration::from_secs(86401)).to_string() == "1 day, 1 second");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(86402)).to_string() == "1 day, 2 seconds");
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(86402)).to_string() == "1 day, 2 seconds"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(86460)).to_string() == "1 day, 1 minute");
|
assert!(HumanTime::into_human(Duration::from_secs(86460)).to_string() == "1 day, 1 minute");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(86520)).to_string() == "1 day, 2 minutes");
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(86520)).to_string() == "1 day, 2 minutes"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(90000)).to_string() == "1 day, 1 hour");
|
assert!(HumanTime::into_human(Duration::from_secs(90000)).to_string() == "1 day, 1 hour");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(93600)).to_string() == "1 day, 2 hours");
|
assert!(HumanTime::into_human(Duration::from_secs(93600)).to_string() == "1 day, 2 hours");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(604799)).to_string() == "6 days, 23 hours, 59 minutes, 59 seconds");
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(604799)).to_string()
|
||||||
|
== "6 days, 23 hours, 59 minutes, 59 seconds"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(604800)).to_string() == "7 days");
|
assert!(HumanTime::into_human(Duration::from_secs(604800)).to_string() == "7 days");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(2630016)).to_string() == "1 month");
|
assert!(HumanTime::into_human(Duration::from_secs(2630016)).to_string() == "1 month");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(3234815)).to_string() == "1 month, 6 days, 23 hours, 59 minutes, 59 seconds");
|
assert!(
|
||||||
|
HumanTime::into_human(Duration::from_secs(3234815)).to_string()
|
||||||
|
== "1 month, 6 days, 23 hours, 59 minutes, 59 seconds"
|
||||||
|
);
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(5260032)).to_string() == "2 months");
|
assert!(HumanTime::into_human(Duration::from_secs(5260032)).to_string() == "2 months");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(31557600)).to_string() == "1 year");
|
assert!(HumanTime::into_human(Duration::from_secs(31557600)).to_string() == "1 year");
|
||||||
assert!(HumanTime::into_human(Duration::from_secs(63115200)).to_string() == "2 years");
|
assert!(HumanTime::into_human(Duration::from_secs(63115200)).to_string() == "2 years");
|
||||||
|
|
|
@ -74,7 +74,7 @@ pub(crate) use sleep;
|
||||||
macro_rules! flip {
|
macro_rules! flip {
|
||||||
($b:expr) => {
|
($b:expr) => {
|
||||||
match $b {
|
match $b {
|
||||||
true|false => $b = !$b,
|
true | false => $b = !$b,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ pub(crate) use flip;
|
||||||
mod test {
|
mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn lock() {
|
fn lock() {
|
||||||
use std::sync::{Arc,Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
let arc_mutex = Arc::new(Mutex::new(false));
|
let arc_mutex = Arc::new(Mutex::new(false));
|
||||||
*lock!(arc_mutex) = true;
|
*lock!(arc_mutex) = true;
|
||||||
assert!(*lock!(arc_mutex) == true);
|
assert!(*lock!(arc_mutex) == true);
|
||||||
|
@ -96,14 +96,12 @@ mod test {
|
||||||
struct Ab {
|
struct Ab {
|
||||||
a: Arc<Mutex<bool>>,
|
a: Arc<Mutex<bool>>,
|
||||||
}
|
}
|
||||||
use std::sync::{Arc,Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
let arc_mutex = Arc::new(Mutex::new(
|
let arc_mutex = Arc::new(Mutex::new(Ab {
|
||||||
Ab {
|
|
||||||
a: Arc::new(Mutex::new(false)),
|
a: Arc::new(Mutex::new(false)),
|
||||||
}
|
}));
|
||||||
));
|
*lock2!(arc_mutex, a) = true;
|
||||||
*lock2!(arc_mutex,a) = true;
|
assert!(*lock2!(arc_mutex, a) == true);
|
||||||
assert!(*lock2!(arc_mutex,a) == true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
1095
src/main.rs
1095
src/main.rs
File diff suppressed because it is too large
Load diff
138
src/node.rs
138
src/node.rs
|
@ -15,20 +15,14 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{
|
use crate::{constants::*, macros::*};
|
||||||
constants::*,
|
|
||||||
macros::*,
|
|
||||||
};
|
|
||||||
use serde::{Serialize,Deserialize};
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
use std::time::{Instant,Duration};
|
|
||||||
use std::sync::{Arc,Mutex};
|
|
||||||
use egui::Color32;
|
use egui::Color32;
|
||||||
|
use hyper::{client::HttpConnector, Body, Client, Request};
|
||||||
use log::*;
|
use log::*;
|
||||||
use hyper::{
|
use rand::{thread_rng, Rng};
|
||||||
client::HttpConnector,
|
use serde::{Deserialize, Serialize};
|
||||||
Client,Body,Request,
|
use std::sync::{Arc, Mutex};
|
||||||
};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Node list
|
//---------------------------------------------------------------------------------------------------- Node list
|
||||||
// Remote Monero Nodes with ZMQ enabled.
|
// Remote Monero Nodes with ZMQ enabled.
|
||||||
|
@ -36,7 +30,7 @@ use hyper::{
|
||||||
|
|
||||||
pub const REMOTE_NODES: [(&str, &str, &str, &str); 18] = [
|
pub const REMOTE_NODES: [(&str, &str, &str, &str); 18] = [
|
||||||
("monero.10z.com.ar", "Argentina", "18089", "18084"),
|
("monero.10z.com.ar", "Argentina", "18089", "18084"),
|
||||||
("monero1.heitechsoft.com","Canada", "18081", "18084"),
|
("monero1.heitechsoft.com", "Canada", "18081", "18084"),
|
||||||
("node.monerodevs.org", "Canada", "18089", "18084"),
|
("node.monerodevs.org", "Canada", "18089", "18084"),
|
||||||
("xmr3.rs.me", "Germany", "18089", "18084"),
|
("xmr3.rs.me", "Germany", "18089", "18084"),
|
||||||
("node.cryptocano.de", "Germany", "18089", "18083"),
|
("node.cryptocano.de", "Germany", "18089", "18083"),
|
||||||
|
@ -96,11 +90,14 @@ impl RemoteNode {
|
||||||
for (ip, _, _, _) in REMOTE_NODES {
|
for (ip, _, _, _) in REMOTE_NODES {
|
||||||
if og_ip == ip {
|
if og_ip == ip {
|
||||||
info!("Found remote node in array: {}", ip);
|
info!("Found remote node in array: {}", ip);
|
||||||
return ip.to_string()
|
return ip.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ip = REMOTE_NODES[0].0.to_string();
|
let ip = REMOTE_NODES[0].0.to_string();
|
||||||
warn!("[{}] remote node does not exist, returning default: {}", og_ip, ip);
|
warn!(
|
||||||
|
"[{}] remote node does not exist, returning default: {}",
|
||||||
|
og_ip, ip
|
||||||
|
);
|
||||||
ip
|
ip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +105,12 @@ impl RemoteNode {
|
||||||
pub fn from_ip(from_ip: &str) -> Self {
|
pub fn from_ip(from_ip: &str) -> Self {
|
||||||
for (ip, location, rpc, zmq) in REMOTE_NODES {
|
for (ip, location, rpc, zmq) in REMOTE_NODES {
|
||||||
if from_ip == ip {
|
if from_ip == ip {
|
||||||
return Self { ip, location, rpc, zmq }
|
return Self {
|
||||||
|
ip,
|
||||||
|
location,
|
||||||
|
rpc,
|
||||||
|
zmq,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::new()
|
Self::new()
|
||||||
|
@ -120,18 +122,30 @@ impl RemoteNode {
|
||||||
Self::new()
|
Self::new()
|
||||||
} else {
|
} else {
|
||||||
let (ip, location, rpc, zmq) = REMOTE_NODES[index];
|
let (ip, location, rpc, zmq) = REMOTE_NODES[index];
|
||||||
Self { ip, location, rpc, zmq }
|
Self {
|
||||||
|
ip,
|
||||||
|
location,
|
||||||
|
rpc,
|
||||||
|
zmq,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_tuple(t: (&'static str, &'static str, &'static str, &'static str)) -> Self {
|
pub fn from_tuple(t: (&'static str, &'static str, &'static str, &'static str)) -> Self {
|
||||||
let (ip, location, rpc, zmq) = (t.0, t.1, t.2, t.3);
|
let (ip, location, rpc, zmq) = (t.0, t.1, t.2, t.3);
|
||||||
Self { ip, location, rpc, zmq }
|
Self {
|
||||||
|
ip,
|
||||||
|
location,
|
||||||
|
rpc,
|
||||||
|
zmq,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ip_rpc_zmq(og_ip: &str) -> (&str, &str, &str) {
|
pub fn get_ip_rpc_zmq(og_ip: &str) -> (&str, &str, &str) {
|
||||||
for (ip, _, rpc, zmq) in REMOTE_NODES {
|
for (ip, _, rpc, zmq) in REMOTE_NODES {
|
||||||
if og_ip == ip { return (ip, rpc, zmq) }
|
if og_ip == ip {
|
||||||
|
return (ip, rpc, zmq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let (ip, _, rpc, zmq) = REMOTE_NODES[0];
|
let (ip, _, rpc, zmq) = REMOTE_NODES[0];
|
||||||
(ip, rpc, zmq)
|
(ip, rpc, zmq)
|
||||||
|
@ -159,8 +173,14 @@ impl RemoteNode {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
let mut last = current_ip;
|
let mut last = current_ip;
|
||||||
for (ip, _, _, _) in REMOTE_NODES {
|
for (ip, _, _, _) in REMOTE_NODES {
|
||||||
if found { return ip.to_string() }
|
if found {
|
||||||
if current_ip == ip { found = true; } else { last = ip; }
|
return ip.to_string();
|
||||||
|
}
|
||||||
|
if current_ip == ip {
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
last = ip;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
last.to_string()
|
last.to_string()
|
||||||
}
|
}
|
||||||
|
@ -169,8 +189,12 @@ impl RemoteNode {
|
||||||
pub fn get_next(current_ip: &str) -> String {
|
pub fn get_next(current_ip: &str) -> String {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for (ip, _, _, _) in REMOTE_NODES {
|
for (ip, _, _, _) in REMOTE_NODES {
|
||||||
if found { return ip.to_string() }
|
if found {
|
||||||
if current_ip == ip { found = true; }
|
return ip.to_string();
|
||||||
|
}
|
||||||
|
if current_ip == ip {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_ip.to_string()
|
current_ip.to_string()
|
||||||
}
|
}
|
||||||
|
@ -180,8 +204,14 @@ impl RemoteNode {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
let mut last = current_ip;
|
let mut last = current_ip;
|
||||||
for data in nodes {
|
for data in nodes {
|
||||||
if found { return last.to_string() }
|
if found {
|
||||||
if current_ip == data.ip { found = true; } else { last = data.ip; }
|
return last.to_string();
|
||||||
|
}
|
||||||
|
if current_ip == data.ip {
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
last = data.ip;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
last.to_string()
|
last.to_string()
|
||||||
}
|
}
|
||||||
|
@ -189,8 +219,12 @@ impl RemoteNode {
|
||||||
pub fn get_next_from_ping(current_ip: &str, nodes: &Vec<NodeData>) -> String {
|
pub fn get_next_from_ping(current_ip: &str, nodes: &Vec<NodeData>) -> String {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for data in nodes {
|
for data in nodes {
|
||||||
if found { return data.ip.to_string() }
|
if found {
|
||||||
if current_ip == data.ip { found = true; }
|
return data.ip.to_string();
|
||||||
|
}
|
||||||
|
if current_ip == data.ip {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_ip.to_string()
|
current_ip.to_string()
|
||||||
}
|
}
|
||||||
|
@ -218,7 +252,11 @@ pub fn format_ms(ms: u128) -> String {
|
||||||
pub fn format_ip_location(og_ip: &str, extra_space: bool) -> String {
|
pub fn format_ip_location(og_ip: &str, extra_space: bool) -> String {
|
||||||
for (ip, location, _, _) in REMOTE_NODES {
|
for (ip, location, _, _) in REMOTE_NODES {
|
||||||
if og_ip == ip {
|
if og_ip == ip {
|
||||||
let ip = if extra_space { format_ip(ip) } else { ip.to_string() };
|
let ip = if extra_space {
|
||||||
|
format_ip(ip)
|
||||||
|
} else {
|
||||||
|
ip.to_string()
|
||||||
|
};
|
||||||
return format!("{ip} | {location}");
|
return format!("{ip} | {location}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,7 +264,9 @@ pub fn format_ip_location(og_ip: &str, extra_space: bool) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_ip(ip: &str) -> String {
|
pub fn format_ip(ip: &str) -> String {
|
||||||
const _: () = if 23 != REMOTE_NODE_MAX_CHARS { panic!(); };
|
const _: () = if 23 != REMOTE_NODE_MAX_CHARS {
|
||||||
|
panic!();
|
||||||
|
};
|
||||||
format!("{ip: >23}")
|
format!("{ip: >23}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +354,7 @@ impl Ping {
|
||||||
pub fn spawn_thread(ping: &Arc<Mutex<Self>>) {
|
pub fn spawn_thread(ping: &Arc<Mutex<Self>>) {
|
||||||
info!("Spawning ping thread...");
|
info!("Spawning ping thread...");
|
||||||
let ping = Arc::clone(ping);
|
let ping = Arc::clone(ping);
|
||||||
std::thread::spawn(move|| {
|
std::thread::spawn(move || {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
match Self::ping(&ping) {
|
match Self::ping(&ping) {
|
||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
|
@ -323,12 +363,12 @@ impl Ping {
|
||||||
lock!(ping).pinged = true;
|
lock!(ping).pinged = true;
|
||||||
lock!(ping).auto_selected = false;
|
lock!(ping).auto_selected = false;
|
||||||
lock!(ping).prog = 100.0;
|
lock!(ping).prog = 100.0;
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Ping ... FAIL ... {}", err);
|
error!("Ping ... FAIL ... {}", err);
|
||||||
lock!(ping).pinged = false;
|
lock!(ping).pinged = false;
|
||||||
lock!(ping).msg = err.to_string();
|
lock!(ping).msg = err.to_string();
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
info!("Ping ... Took [{}] seconds...", now.elapsed().as_secs_f32());
|
info!("Ping ... Took [{}] seconds...", now.elapsed().as_secs_f32());
|
||||||
lock!(ping).pinging = false;
|
lock!(ping).pinging = false;
|
||||||
|
@ -365,8 +405,7 @@ impl Ping {
|
||||||
// Create HTTP client
|
// Create HTTP client
|
||||||
let info = "Creating HTTP Client".to_string();
|
let info = "Creating HTTP Client".to_string();
|
||||||
lock!(ping).msg = info;
|
lock!(ping).msg = info;
|
||||||
let client: Client<HttpConnector> = Client::builder()
|
let client: Client<HttpConnector> = Client::builder().build(HttpConnector::new());
|
||||||
.build(HttpConnector::new());
|
|
||||||
|
|
||||||
// Random User Agent
|
// Random User Agent
|
||||||
let rand_user_agent = crate::Pkg::get_user_agent();
|
let rand_user_agent = crate::Pkg::get_user_agent();
|
||||||
|
@ -382,9 +421,13 @@ impl Ping {
|
||||||
.method("POST")
|
.method("POST")
|
||||||
.uri("http://".to_string() + ip + ":" + rpc + "/json_rpc")
|
.uri("http://".to_string() + ip + ":" + rpc + "/json_rpc")
|
||||||
.header("User-Agent", rand_user_agent)
|
.header("User-Agent", rand_user_agent)
|
||||||
.body(hyper::Body::from(r#"{"jsonrpc":"2.0","id":"0","method":"get_info"}"#))
|
.body(hyper::Body::from(
|
||||||
|
r#"{"jsonrpc":"2.0","id":"0","method":"get_info"}"#,
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let handle = tokio::task::spawn(async move { Self::response(client, request, ip, ping, percent, node_vec).await; });
|
let handle = tokio::task::spawn(async move {
|
||||||
|
Self::response(client, request, ip, ping, percent, node_vec).await;
|
||||||
|
});
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +457,7 @@ impl Ping {
|
||||||
ip: &'static str,
|
ip: &'static str,
|
||||||
ping: Arc<Mutex<Self>>,
|
ping: Arc<Mutex<Self>>,
|
||||||
percent: f32,
|
percent: f32,
|
||||||
node_vec: Arc<Mutex<Vec<NodeData>>>
|
node_vec: Arc<Mutex<Vec<NodeData>>>,
|
||||||
) {
|
) {
|
||||||
let ms;
|
let ms;
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
@ -423,8 +466,7 @@ impl Ping {
|
||||||
Ok(Ok(json_rpc)) => {
|
Ok(Ok(json_rpc)) => {
|
||||||
// Attempt to convert to JSON-RPC.
|
// Attempt to convert to JSON-RPC.
|
||||||
match hyper::body::to_bytes(json_rpc.into_body()).await {
|
match hyper::body::to_bytes(json_rpc.into_body()).await {
|
||||||
Ok(b) => {
|
Ok(b) => match serde_json::from_slice::<GetInfo<'_>>(&b) {
|
||||||
match serde_json::from_slice::<GetInfo<'_>>(&b) {
|
|
||||||
Ok(rpc) => {
|
Ok(rpc) => {
|
||||||
if rpc.result.mainnet && rpc.result.synchronized {
|
if rpc.result.mainnet && rpc.result.synchronized {
|
||||||
ms = now.elapsed().as_millis();
|
ms = now.elapsed().as_millis();
|
||||||
|
@ -437,11 +479,10 @@ impl Ping {
|
||||||
ms = TIMEOUT_NODE_PING;
|
ms = TIMEOUT_NODE_PING;
|
||||||
warn!("Ping | {ip} responded but with invalid get_info, remove this node!");
|
warn!("Ping | {ip} responded but with invalid get_info, remove this node!");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => ms = TIMEOUT_NODE_PING,
|
_ => ms = TIMEOUT_NODE_PING,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
_ => ms = TIMEOUT_NODE_PING,
|
_ => ms = TIMEOUT_NODE_PING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -493,14 +534,11 @@ mod test {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
async fn full_ping() {
|
async fn full_ping() {
|
||||||
use hyper::{
|
use crate::{REMOTE_NODES, REMOTE_NODE_LENGTH};
|
||||||
client::HttpConnector,
|
use hyper::{client::HttpConnector, Body, Client, Request};
|
||||||
Client,Body,Request,
|
use serde::{Deserialize, Serialize};
|
||||||
};
|
|
||||||
use crate::{REMOTE_NODES,REMOTE_NODE_LENGTH};
|
|
||||||
use serde::{Serialize,Deserialize};
|
|
||||||
|
|
||||||
#[derive(Deserialize,Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct GetInfo {
|
struct GetInfo {
|
||||||
id: String,
|
id: String,
|
||||||
jsonrpc: String,
|
jsonrpc: String,
|
||||||
|
@ -529,7 +567,9 @@ mod test {
|
||||||
.method("POST")
|
.method("POST")
|
||||||
.uri("http://".to_string() + ip + ":" + rpc + "/json_rpc")
|
.uri("http://".to_string() + ip + ":" + rpc + "/json_rpc")
|
||||||
.header("User-Agent", rand_user_agent)
|
.header("User-Agent", rand_user_agent)
|
||||||
.body(hyper::Body::from(r#"{"jsonrpc":"2.0","id":"0","method":"get_info"}"#))
|
.body(hyper::Body::from(
|
||||||
|
r#"{"jsonrpc":"2.0","id":"0","method":"get_info"}"#,
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match client.request(request).await {
|
match client.request(request).await {
|
||||||
Ok(response) => break response,
|
Ok(response) => break response,
|
||||||
|
|
247
src/p2pool.rs
247
src/p2pool.rs
|
@ -15,32 +15,21 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{
|
use crate::regex::REGEXES;
|
||||||
Regexes,
|
use crate::{constants::*, disk::*, helper::*, macros::*, node::*, Regexes};
|
||||||
constants::*,
|
|
||||||
disk::*,
|
|
||||||
node::*,
|
|
||||||
helper::*,
|
|
||||||
macros::*,
|
|
||||||
};
|
|
||||||
use egui::{
|
use egui::{
|
||||||
TextEdit,SelectableLabel,ComboBox,Label,Button,
|
Button, Checkbox, Color32, ComboBox, Hyperlink, Label, ProgressBar, RichText, SelectableLabel,
|
||||||
Color32,RichText,Slider,Checkbox,ProgressBar,Spinner,
|
Slider, Spinner, TextEdit, TextStyle::*,
|
||||||
TextStyle::*,Hyperlink
|
|
||||||
};
|
};
|
||||||
use std::sync::{Arc,Mutex};
|
|
||||||
use regex::Regex;
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use crate::regex::{
|
use regex::Regex;
|
||||||
REGEXES,
|
use std::sync::{Arc, Mutex};
|
||||||
};
|
|
||||||
|
|
||||||
impl crate::disk::P2pool {
|
impl crate::disk::P2pool {
|
||||||
#[inline(always)] // called once
|
#[inline(always)] // called once
|
||||||
pub fn show(
|
pub fn show(
|
||||||
&mut self,
|
&mut self,
|
||||||
node_vec: &mut Vec<(String,
|
node_vec: &mut Vec<(String, Node)>,
|
||||||
Node)>,
|
|
||||||
_og: &Arc<Mutex<State>>,
|
_og: &Arc<Mutex<State>>,
|
||||||
ping: &Arc<Mutex<Ping>>,
|
ping: &Arc<Mutex<Ping>>,
|
||||||
process: &Arc<Mutex<Process>>,
|
process: &Arc<Mutex<Process>>,
|
||||||
|
@ -49,8 +38,8 @@ pub fn show(
|
||||||
width: f32,
|
width: f32,
|
||||||
height: f32,
|
height: f32,
|
||||||
_ctx: &egui::Context,
|
_ctx: &egui::Context,
|
||||||
ui: &mut egui::Ui
|
ui: &mut egui::Ui,
|
||||||
) {
|
) {
|
||||||
let text_edit = height / 25.0;
|
let text_edit = height / 25.0;
|
||||||
//---------------------------------------------------------------------------------------------------- [Simple] Console
|
//---------------------------------------------------------------------------------------------------- [Simple] Console
|
||||||
debug!("P2Pool Tab | Rendering [Console]");
|
debug!("P2Pool Tab | Rendering [Console]");
|
||||||
|
@ -60,8 +49,16 @@ pub fn show(
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||||
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
||||||
egui::ScrollArea::vertical().stick_to_bottom(true).max_width(width).max_height(height).auto_shrink([false; 2]).show_viewport(ui, |ui, _| {
|
egui::ScrollArea::vertical()
|
||||||
ui.add_sized([width, height], TextEdit::multiline(&mut lock!(api).output.as_str()));
|
.stick_to_bottom(true)
|
||||||
|
.max_width(width)
|
||||||
|
.max_height(height)
|
||||||
|
.auto_shrink([false; 2])
|
||||||
|
.show_viewport(ui, |ui, _| {
|
||||||
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
TextEdit::multiline(&mut lock!(api).output.as_str()),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
||||||
|
@ -70,18 +67,36 @@ pub fn show(
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||||
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
||||||
egui::ScrollArea::vertical().stick_to_bottom(true).max_width(width).max_height(height).auto_shrink([false; 2]).show_viewport(ui, |ui, _| {
|
egui::ScrollArea::vertical()
|
||||||
ui.add_sized([width, height], TextEdit::multiline(&mut lock!(api).output.as_str()));
|
.stick_to_bottom(true)
|
||||||
|
.max_width(width)
|
||||||
|
.max_height(height)
|
||||||
|
.auto_shrink([false; 2])
|
||||||
|
.show_viewport(ui, |ui, _| {
|
||||||
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
TextEdit::multiline(&mut lock!(api).output.as_str()),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.separator();
|
ui.separator();
|
||||||
let response = ui.add_sized([width, text_edit], TextEdit::hint_text(TextEdit::singleline(buffer), r#"Type a command (e.g "help" or "status") and press Enter"#)).on_hover_text(P2POOL_INPUT);
|
let response = ui
|
||||||
|
.add_sized(
|
||||||
|
[width, text_edit],
|
||||||
|
TextEdit::hint_text(
|
||||||
|
TextEdit::singleline(buffer),
|
||||||
|
r#"Type a command (e.g "help" or "status") and press Enter"#,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_INPUT);
|
||||||
// If the user pressed enter, dump buffer contents into the process STDIN
|
// If the user pressed enter, dump buffer contents into the process STDIN
|
||||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||||
response.request_focus(); // Get focus back
|
response.request_focus(); // Get focus back
|
||||||
let buffer = std::mem::take(buffer); // Take buffer
|
let buffer = std::mem::take(buffer); // Take buffer
|
||||||
let mut process = lock!(process); // Lock
|
let mut process = lock!(process); // Lock
|
||||||
if process.is_alive() { process.input.push(buffer); } // Push only if alive
|
if process.is_alive() {
|
||||||
|
process.input.push(buffer);
|
||||||
|
} // Push only if alive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -89,12 +104,21 @@ pub fn show(
|
||||||
//---------------------------------------------------------------------------------------------------- Args
|
//---------------------------------------------------------------------------------------------------- Args
|
||||||
if !self.simple {
|
if !self.simple {
|
||||||
debug!("P2Pool Tab | Rendering [Arguments]");
|
debug!("P2Pool Tab | Rendering [Arguments]");
|
||||||
ui.group(|ui| { ui.horizontal(|ui| {
|
ui.group(|ui| {
|
||||||
let width = (width/10.0) - SPACE;
|
ui.horizontal(|ui| {
|
||||||
|
let width = (width / 10.0) - SPACE;
|
||||||
ui.add_sized([width, text_edit], Label::new("Command arguments:"));
|
ui.add_sized([width, text_edit], Label::new("Command arguments:"));
|
||||||
ui.add_sized([ui.available_width(), text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.arguments), r#"--wallet <...> --host <...>"#)).on_hover_text(P2POOL_ARGUMENTS);
|
ui.add_sized(
|
||||||
|
[ui.available_width(), text_edit],
|
||||||
|
TextEdit::hint_text(
|
||||||
|
TextEdit::singleline(&mut self.arguments),
|
||||||
|
r#"--wallet <...> --host <...>"#,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_ARGUMENTS);
|
||||||
self.arguments.truncate(1024);
|
self.arguments.truncate(1024);
|
||||||
})});
|
})
|
||||||
|
});
|
||||||
ui.set_enabled(self.arguments.is_empty());
|
ui.set_enabled(self.arguments.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +126,7 @@ pub fn show(
|
||||||
debug!("P2Pool Tab | Rendering [Address]");
|
debug!("P2Pool Tab | Rendering [Address]");
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
ui.spacing_mut().text_edit_width = (width)-(SPACE*3.0);
|
ui.spacing_mut().text_edit_width = (width) - (SPACE * 3.0);
|
||||||
let text;
|
let text;
|
||||||
let color;
|
let color;
|
||||||
let len = format!("{:02}", self.address.len());
|
let len = format!("{:02}", self.address.len());
|
||||||
|
@ -116,8 +140,15 @@ pub fn show(
|
||||||
text = format!("Monero Address [{}/95] ❌", len);
|
text = format!("Monero Address [{}/95] ❌", len);
|
||||||
color = Color32::from_rgb(230, 50, 50);
|
color = Color32::from_rgb(230, 50, 50);
|
||||||
}
|
}
|
||||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
ui.add_sized(
|
||||||
ui.add_sized([width, text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4...")).on_hover_text(P2POOL_ADDRESS);
|
[width, text_edit],
|
||||||
|
Label::new(RichText::new(text).color(color)),
|
||||||
|
);
|
||||||
|
ui.add_sized(
|
||||||
|
[width, text_edit],
|
||||||
|
TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4..."),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_ADDRESS);
|
||||||
self.address.truncate(95);
|
self.address.truncate(95);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -147,7 +178,6 @@ pub fn show(
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
|
||||||
debug!("P2Pool Tab | Rendering [Ping List]");
|
debug!("P2Pool Tab | Rendering [Ping List]");
|
||||||
// [Ping List]
|
// [Ping List]
|
||||||
let mut ms = 0;
|
let mut ms = 0;
|
||||||
|
@ -157,18 +187,22 @@ pub fn show(
|
||||||
if data.ip == self.node {
|
if data.ip == self.node {
|
||||||
ms = data.ms;
|
ms = data.ms;
|
||||||
color = data.color;
|
color = data.color;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("P2Pool Tab | Rendering [ComboBox] of Remote Nodes");
|
debug!("P2Pool Tab | Rendering [ComboBox] of Remote Nodes");
|
||||||
let ip_location = crate::node::format_ip_location(&self.node, false);
|
let ip_location = crate::node::format_ip_location(&self.node, false);
|
||||||
let text = RichText::new(format!(" ⏺ {}ms | {}", ms, ip_location)).color(color);
|
let text = RichText::new(format!(" ⏺ {}ms | {}", ms, ip_location)).color(color);
|
||||||
ComboBox::from_id_source("remote_nodes").selected_text(text).width(width).show_ui(ui, |ui| {
|
ComboBox::from_id_source("remote_nodes")
|
||||||
|
.selected_text(text)
|
||||||
|
.width(width)
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
for data in lock!(ping).nodes.iter() {
|
for data in lock!(ping).nodes.iter() {
|
||||||
let ms = crate::node::format_ms(data.ms);
|
let ms = crate::node::format_ms(data.ms);
|
||||||
let ip_location = crate::node::format_ip_location(data.ip, true);
|
let ip_location = crate::node::format_ip_location(data.ip, true);
|
||||||
let text = RichText::new(format!(" ⏺ {} | {}", ms, ip_location)).color(data.color);
|
let text = RichText::new(format!(" ⏺ {} | {}", ms, ip_location))
|
||||||
|
.color(data.color);
|
||||||
ui.selectable_value(&mut self.node, data.ip.to_string(), text);
|
ui.selectable_value(&mut self.node, data.ip.to_string(), text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -178,35 +212,60 @@ pub fn show(
|
||||||
|
|
||||||
debug!("P2Pool Tab | Rendering [Select fastest ... Ping] buttons");
|
debug!("P2Pool Tab | Rendering [Select fastest ... Ping] buttons");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let width = (width/5.0)-6.0;
|
let width = (width / 5.0) - 6.0;
|
||||||
// [Select random node]
|
// [Select random node]
|
||||||
if ui.add_sized([width, height], Button::new("Select random node")).on_hover_text(P2POOL_SELECT_RANDOM).clicked() {
|
if ui
|
||||||
|
.add_sized([width, height], Button::new("Select random node"))
|
||||||
|
.on_hover_text(P2POOL_SELECT_RANDOM)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
self.node = RemoteNode::get_random(&self.node);
|
self.node = RemoteNode::get_random(&self.node);
|
||||||
}
|
}
|
||||||
// [Select fastest node]
|
// [Select fastest node]
|
||||||
if ui.add_sized([width, height], Button::new("Select fastest node")).on_hover_text(P2POOL_SELECT_FASTEST).clicked() && lock!(ping).pinged {
|
if ui
|
||||||
|
.add_sized([width, height], Button::new("Select fastest node"))
|
||||||
|
.on_hover_text(P2POOL_SELECT_FASTEST)
|
||||||
|
.clicked()
|
||||||
|
&& lock!(ping).pinged
|
||||||
|
{
|
||||||
self.node = lock!(ping).fastest.to_string();
|
self.node = lock!(ping).fastest.to_string();
|
||||||
}
|
}
|
||||||
// [Ping Button]
|
// [Ping Button]
|
||||||
ui.add_enabled_ui(!lock!(ping).pinging, |ui| {
|
ui.add_enabled_ui(!lock!(ping).pinging, |ui| {
|
||||||
if ui.add_sized([width, height], Button::new("Ping remote nodes")).on_hover_text(P2POOL_PING).clicked() {
|
if ui
|
||||||
|
.add_sized([width, height], Button::new("Ping remote nodes"))
|
||||||
|
.on_hover_text(P2POOL_PING)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
Ping::spawn_thread(ping);
|
Ping::spawn_thread(ping);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// [Last <-]
|
// [Last <-]
|
||||||
if ui.add_sized([width, height], Button::new("⬅ Last")).on_hover_text(P2POOL_SELECT_LAST).clicked() {
|
if ui
|
||||||
|
.add_sized([width, height], Button::new("⬅ Last"))
|
||||||
|
.on_hover_text(P2POOL_SELECT_LAST)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
let ping = lock!(ping);
|
let ping = lock!(ping);
|
||||||
match ping.pinged {
|
match ping.pinged {
|
||||||
true => self.node = RemoteNode::get_last_from_ping(&self.node, &ping.nodes),
|
true => {
|
||||||
|
self.node = RemoteNode::get_last_from_ping(&self.node, &ping.nodes)
|
||||||
|
}
|
||||||
false => self.node = RemoteNode::get_last(&self.node),
|
false => self.node = RemoteNode::get_last(&self.node),
|
||||||
}
|
}
|
||||||
drop(ping);
|
drop(ping);
|
||||||
}
|
}
|
||||||
// [Next ->]
|
// [Next ->]
|
||||||
if ui.add_sized([width, height], Button::new("Next ➡")).on_hover_text(P2POOL_SELECT_NEXT).clicked() {
|
if ui
|
||||||
|
.add_sized([width, height], Button::new("Next ➡"))
|
||||||
|
.on_hover_text(P2POOL_SELECT_NEXT)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
let ping = lock!(ping);
|
let ping = lock!(ping);
|
||||||
match ping.pinged {
|
match ping.pinged {
|
||||||
true => self.node = RemoteNode::get_next_from_ping(&self.node, &ping.nodes),
|
true => {
|
||||||
|
self.node = RemoteNode::get_next_from_ping(&self.node, &ping.nodes)
|
||||||
|
}
|
||||||
false => self.node = RemoteNode::get_next(&self.node),
|
false => self.node = RemoteNode::get_next(&self.node),
|
||||||
}
|
}
|
||||||
drop(ping);
|
drop(ping);
|
||||||
|
@ -228,7 +287,7 @@ pub fn show(
|
||||||
} else {
|
} else {
|
||||||
ui.add_sized([width, height], Label::new("..."));
|
ui.add_sized([width, height], Label::new("..."));
|
||||||
}
|
}
|
||||||
ui.add_sized([width, height], ProgressBar::new(prog.round()/100.0));
|
ui.add_sized([width, height], ProgressBar::new(prog.round() / 100.0));
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -236,16 +295,29 @@ pub fn show(
|
||||||
debug!("P2Pool Tab | Rendering [Auto-*] buttons");
|
debug!("P2Pool Tab | Rendering [Auto-*] buttons");
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let width = (width/3.0)-(SPACE*1.75);
|
let width = (width / 3.0) - (SPACE * 1.75);
|
||||||
// [Auto-node]
|
// [Auto-node]
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.auto_select, "Auto-select")).on_hover_text(P2POOL_AUTO_SELECT);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.auto_select, "Auto-select"),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_AUTO_SELECT);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
// [Auto-node]
|
// [Auto-node]
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.auto_ping, "Auto-ping")).on_hover_text(P2POOL_AUTO_NODE);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.auto_ping, "Auto-ping"),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_AUTO_NODE);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
// [Backup host]
|
// [Backup host]
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.backup_host, "Backup host")).on_hover_text(P2POOL_BACKUP_HOST_SIMPLE);
|
ui.add_sized(
|
||||||
})});
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.backup_host, "Backup host"),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_BACKUP_HOST_SIMPLE);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
debug!("P2Pool Tab | Rendering warning text");
|
debug!("P2Pool Tab | Rendering warning text");
|
||||||
ui.add_sized([width, height/2.0], Hyperlink::from_label_and_url("WARNING: It is recommended to run/use your own Monero Node (hover for details)", "https://github.com/hinto-janai/gupax#running-a-local-monero-node")).on_hover_text(P2POOL_COMMUNITY_NODE_WARNING);
|
ui.add_sized([width, height/2.0], Hyperlink::from_label_and_url("WARNING: It is recommended to run/use your own Monero Node (hover for details)", "https://github.com/hinto-janai/gupax#running-a-local-monero-node")).on_hover_text(P2POOL_COMMUNITY_NODE_WARNING);
|
||||||
|
@ -471,35 +543,66 @@ pub fn show(
|
||||||
debug!("P2Pool Tab | Rendering [Main/Mini/Peers/Log] elements");
|
debug!("P2Pool Tab | Rendering [Main/Mini/Peers/Log] elements");
|
||||||
// [Main/Mini]
|
// [Main/Mini]
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let height = height/4.0;
|
let height = height / 4.0;
|
||||||
ui.group(|ui| { ui.horizontal(|ui| {
|
ui.group(|ui| {
|
||||||
let width = (width/4.0)-SPACE;
|
ui.horizontal(|ui| {
|
||||||
|
let width = (width / 4.0) - SPACE;
|
||||||
let height = height + 6.0;
|
let height = height + 6.0;
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(!self.mini, "P2Pool Main")).on_hover_text(P2POOL_MAIN).clicked() { self.mini = false; }
|
if ui
|
||||||
if ui.add_sized([width, height], SelectableLabel::new(self.mini, "P2Pool Mini")).on_hover_text(P2POOL_MINI).clicked() { self.mini = true; }
|
.add_sized(
|
||||||
})});
|
[width, height],
|
||||||
|
SelectableLabel::new(!self.mini, "P2Pool Main"),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_MAIN)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.mini = false;
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.add_sized(
|
||||||
|
[width, height],
|
||||||
|
SelectableLabel::new(self.mini, "P2Pool Mini"),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_MINI)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.mini = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
// [Out/In Peers] + [Log Level]
|
// [Out/In Peers] + [Log Level]
|
||||||
ui.group(|ui| { ui.vertical(|ui| {
|
ui.group(|ui| {
|
||||||
let text = (ui.available_width()/10.0)-SPACE;
|
ui.vertical(|ui| {
|
||||||
let width = (text*8.0)-SPACE;
|
let text = (ui.available_width() / 10.0) - SPACE;
|
||||||
let height = height/3.0;
|
let width = (text * 8.0) - SPACE;
|
||||||
ui.style_mut().spacing.slider_width = width/1.1;
|
let height = height / 3.0;
|
||||||
|
ui.style_mut().spacing.slider_width = width / 1.1;
|
||||||
ui.style_mut().spacing.interact_size.y = height;
|
ui.style_mut().spacing.interact_size.y = height;
|
||||||
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_sized([text, height], Label::new("Out peers [10-450]:"));
|
ui.add_sized([text, height], Label::new("Out peers [10-450]:"));
|
||||||
ui.add_sized([width, height], Slider::new(&mut self.out_peers, 10..=450)).on_hover_text(P2POOL_OUT);
|
ui.add_sized(
|
||||||
ui.add_space(ui.available_width()-4.0);
|
[width, height],
|
||||||
|
Slider::new(&mut self.out_peers, 10..=450),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_OUT);
|
||||||
|
ui.add_space(ui.available_width() - 4.0);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_sized([text, height], Label::new(" In peers [10-450]:"));
|
ui.add_sized([text, height], Label::new(" In peers [10-450]:"));
|
||||||
ui.add_sized([width, height], Slider::new(&mut self.in_peers, 10..=450)).on_hover_text(P2POOL_IN);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Slider::new(&mut self.in_peers, 10..=450),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_IN);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_sized([text, height], Label::new(" Log level [0-6]:"));
|
ui.add_sized([text, height], Label::new(" Log level [0-6]:"));
|
||||||
ui.add_sized([width, height], Slider::new(&mut self.log_level, 0..=6)).on_hover_text(P2POOL_LOG);
|
ui.add_sized([width, height], Slider::new(&mut self.log_level, 0..=6))
|
||||||
|
.on_hover_text(P2POOL_LOG);
|
||||||
|
});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
})});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!("P2Pool Tab | Rendering Backup host button");
|
debug!("P2Pool Tab | Rendering Backup host button");
|
||||||
|
@ -507,7 +610,11 @@ pub fn show(
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
let height = ui.available_height() / 3.0;
|
let height = ui.available_height() / 3.0;
|
||||||
// [Backup host]
|
// [Backup host]
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.backup_host, "Backup host")).on_hover_text(P2POOL_BACKUP_HOST_ADVANCED);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.backup_host, "Backup host"),
|
||||||
|
)
|
||||||
|
.on_hover_text(P2POOL_BACKUP_HOST_ADVANCED);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
src/panic.rs
16
src/panic.rs
|
@ -1,11 +1,5 @@
|
||||||
//---------------------------------------------------------------------------------------------------- Use
|
//---------------------------------------------------------------------------------------------------- Use
|
||||||
use crate::constants::{
|
use crate::constants::{COMMIT, GUPAX_VERSION, OS_NAME, P2POOL_VERSION, XMRIG_VERSION};
|
||||||
GUPAX_VERSION,
|
|
||||||
P2POOL_VERSION,
|
|
||||||
XMRIG_VERSION,
|
|
||||||
OS_NAME,
|
|
||||||
COMMIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
#[cold]
|
#[cold]
|
||||||
|
@ -20,7 +14,7 @@ pub(crate) fn set_panic_hook(now: std::time::Instant) {
|
||||||
|
|
||||||
// Re-format panic info.
|
// Re-format panic info.
|
||||||
let panic_info = format!(
|
let panic_info = format!(
|
||||||
"{panic_info:#?}
|
"{panic_info:#?}
|
||||||
|
|
||||||
info:
|
info:
|
||||||
OS | {OS_NAME}
|
OS | {OS_NAME}
|
||||||
|
@ -39,10 +33,12 @@ stack backtrace:\n{stack_trace}",
|
||||||
Ok(mut path) => {
|
Ok(mut path) => {
|
||||||
path.push("crash.txt");
|
path.push("crash.txt");
|
||||||
match std::fs::write(&path, &panic_info) {
|
match std::fs::write(&path, &panic_info) {
|
||||||
Ok(_) => eprintln!("\nmass_panic!() - Saved panic log to: {}\n", path.display()),
|
Ok(_) => {
|
||||||
|
eprintln!("\nmass_panic!() - Saved panic log to: {}\n", path.display())
|
||||||
|
}
|
||||||
Err(e) => eprintln!("\nmass_panic!() - Could not save panic log: {e}\n"),
|
Err(e) => eprintln!("\nmass_panic!() - Could not save panic log: {e}\n"),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => eprintln!("panic_hook PATH error: {e}"),
|
Err(e) => eprintln!("panic_hook PATH error: {e}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
33
src/regex.rs
33
src/regex.rs
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
// Some regexes used throughout Gupax.
|
// Some regexes used throughout Gupax.
|
||||||
|
|
||||||
use regex::Regex;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Lazy
|
//---------------------------------------------------------------------------------------------------- Lazy
|
||||||
pub static REGEXES: Lazy<Regexes> = Lazy::new(|| Regexes::new());
|
pub static REGEXES: Lazy<Regexes> = Lazy::new(|| Regexes::new());
|
||||||
|
@ -54,11 +54,14 @@ impl Regexes {
|
||||||
// This actually only checks for length & Base58, and doesn't do any checksum validation
|
// This actually only checks for length & Base58, and doesn't do any checksum validation
|
||||||
// (the last few bytes of a Monero address are a Keccak hash checksum) so some invalid addresses can trick this function.
|
// (the last few bytes of a Monero address are a Keccak hash checksum) so some invalid addresses can trick this function.
|
||||||
pub fn addr_ok(address: &str) -> bool {
|
pub fn addr_ok(address: &str) -> bool {
|
||||||
address.len() == 95 && REGEXES.address.is_match(address) && !address.contains('0') && !address.contains('O') && !address.contains('l')
|
address.len() == 95
|
||||||
|
&& REGEXES.address.is_match(address)
|
||||||
|
&& !address.contains('0')
|
||||||
|
&& !address.contains('O')
|
||||||
|
&& !address.contains('l')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- [P2poolRegex]
|
//---------------------------------------------------------------------------------------------------- [P2poolRegex]
|
||||||
// Meant for parsing the output of P2Pool and finding payouts and total XMR found.
|
// Meant for parsing the output of P2Pool and finding payouts and total XMR found.
|
||||||
// Why Regex instead of the standard library?
|
// Why Regex instead of the standard library?
|
||||||
|
@ -76,7 +79,7 @@ impl Regexes {
|
||||||
// let n = regex.find_iter(P2POOL_OUTPUT).count();
|
// let n = regex.find_iter(P2POOL_OUTPUT).count();
|
||||||
//
|
//
|
||||||
// Both are nominally fast enough where it doesn't matter too much but meh, why not use regex.
|
// Both are nominally fast enough where it doesn't matter too much but meh, why not use regex.
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct P2poolRegex {
|
pub struct P2poolRegex {
|
||||||
pub date: Regex,
|
pub date: Regex,
|
||||||
pub payout: Regex,
|
pub payout: Regex,
|
||||||
|
@ -124,8 +127,8 @@ impl XmrigRegex {
|
||||||
//---------------------------------------------------------------------------------------------------- TESTS
|
//---------------------------------------------------------------------------------------------------- TESTS
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use regex::Regex;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_regexes() {
|
fn build_regexes() {
|
||||||
|
@ -157,9 +160,18 @@ mod test {
|
||||||
let text = "NOTICE 2022-11-11 11:11:11.1111 P2Pool You received a payout of 0.111111111111 XMR in block 1111111";
|
let text = "NOTICE 2022-11-11 11:11:11.1111 P2Pool You received a payout of 0.111111111111 XMR in block 1111111";
|
||||||
let text2 = "2022-11-11 11:11:11.1111 | 0.111111111111 XMR | Block 1,111,111";
|
let text2 = "2022-11-11 11:11:11.1111 | 0.111111111111 XMR | Block 1,111,111";
|
||||||
let text3 = "NOTICE 2020-12-11 12:35:41.3150 SideChain SYNCHRONIZED";
|
let text3 = "NOTICE 2020-12-11 12:35:41.3150 SideChain SYNCHRONIZED";
|
||||||
assert_eq!(r.payout.find(text).unwrap().as_str(), "payout of 0.111111111111 XMR");
|
assert_eq!(
|
||||||
assert_eq!(r.payout_float.find(text).unwrap().as_str(), "0.111111111111");
|
r.payout.find(text).unwrap().as_str(),
|
||||||
assert_eq!(r.date.find(text).unwrap().as_str(), "2022-11-11 11:11:11.1111");
|
"payout of 0.111111111111 XMR"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
r.payout_float.find(text).unwrap().as_str(),
|
||||||
|
"0.111111111111"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
r.date.find(text).unwrap().as_str(),
|
||||||
|
"2022-11-11 11:11:11.1111"
|
||||||
|
);
|
||||||
assert_eq!(r.block.find(text).unwrap().as_str(), "block 1111111");
|
assert_eq!(r.block.find(text).unwrap().as_str(), "block 1111111");
|
||||||
assert_eq!(r.block_int.find(text).unwrap().as_str(), "1111111");
|
assert_eq!(r.block_int.find(text).unwrap().as_str(), "1111111");
|
||||||
assert_eq!(r.block_comma.find(text2).unwrap().as_str(), "1,111,111");
|
assert_eq!(r.block_comma.find(text2).unwrap().as_str(), "1,111,111");
|
||||||
|
@ -171,7 +183,10 @@ mod test {
|
||||||
let r = XmrigRegex::new();
|
let r = XmrigRegex::new();
|
||||||
let text = "[2022-02-12 12:49:30.311] net no active pools, stop mining";
|
let text = "[2022-02-12 12:49:30.311] net no active pools, stop mining";
|
||||||
let text2 = "[2022-02-12 12:49:30.311] net new job from 192.168.2.1:3333 diff 402K algo rx/0 height 2241142 (11 tx)";
|
let text2 = "[2022-02-12 12:49:30.311] net new job from 192.168.2.1:3333 diff 402K algo rx/0 height 2241142 (11 tx)";
|
||||||
assert_eq!(r.not_mining.find(text).unwrap().as_str(), "no active pools, stop mining");
|
assert_eq!(
|
||||||
|
r.not_mining.find(text).unwrap().as_str(),
|
||||||
|
"no active pools, stop mining"
|
||||||
|
);
|
||||||
assert_eq!(r.new_job.find(text2).unwrap().as_str(), "new job");
|
assert_eq!(r.new_job.find(text2).unwrap().as_str(), "new job");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
914
src/status.rs
914
src/status.rs
File diff suppressed because it is too large
Load diff
65
src/sudo.rs
65
src/sudo.rs
|
@ -19,24 +19,18 @@
|
||||||
// [zeroize] is used to wipe the memory after use.
|
// [zeroize] is used to wipe the memory after use.
|
||||||
// Only gets imported in [main.rs] for Unix.
|
// Only gets imported in [main.rs] for Unix.
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use crate::{constants::*, disk::Xmrig, macros::*, Helper, ProcessSignal};
|
||||||
|
use log::*;
|
||||||
use std::{
|
use std::{
|
||||||
thread,
|
|
||||||
sync::{Arc,Mutex},
|
|
||||||
process::*,
|
|
||||||
io::Write,
|
io::Write,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
process::*,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread,
|
||||||
};
|
};
|
||||||
use crate::{
|
use zeroize::Zeroize;
|
||||||
Helper,
|
|
||||||
disk::Xmrig,
|
|
||||||
ProcessSignal,
|
|
||||||
constants::*,
|
|
||||||
macros::*,
|
|
||||||
};
|
|
||||||
use log::*;
|
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SudoState {
|
pub struct SudoState {
|
||||||
pub windows: bool, // If this bool is set, this struct is just a dummy so I don't have to change my type signatures :)
|
pub windows: bool, // If this bool is set, this struct is just a dummy so I don't have to change my type signatures :)
|
||||||
pub testing: bool, // Are we attempting a sudo test right now?
|
pub testing: bool, // Are we attempting a sudo test right now?
|
||||||
|
@ -91,7 +85,7 @@ impl SudoState {
|
||||||
let mut state = lock!(state);
|
let mut state = lock!(state);
|
||||||
state.testing = false;
|
state.testing = false;
|
||||||
state.success = false;
|
state.success = false;
|
||||||
// state.signal = ProcessSignal::None;
|
// state.signal = ProcessSignal::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
|
@ -113,7 +107,12 @@ impl SudoState {
|
||||||
// Spawns a thread and tests sudo with the provided password.
|
// Spawns a thread and tests sudo with the provided password.
|
||||||
// Sudo takes the password through STDIN via [--stdin].
|
// Sudo takes the password through STDIN via [--stdin].
|
||||||
// Sets the appropriate state fields on success/failure.
|
// Sets the appropriate state fields on success/failure.
|
||||||
pub fn test_sudo(state: Arc<Mutex<Self>>, helper: &Arc<Mutex<Helper>>, xmrig: &Xmrig, path: &PathBuf) {
|
pub fn test_sudo(
|
||||||
|
state: Arc<Mutex<Self>>,
|
||||||
|
helper: &Arc<Mutex<Helper>>,
|
||||||
|
xmrig: &Xmrig,
|
||||||
|
path: &PathBuf,
|
||||||
|
) {
|
||||||
let helper = Arc::clone(helper);
|
let helper = Arc::clone(helper);
|
||||||
let xmrig = xmrig.clone();
|
let xmrig = xmrig.clone();
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
|
@ -135,8 +134,8 @@ impl SudoState {
|
||||||
Self::wipe(&state);
|
Self::wipe(&state);
|
||||||
lock!(state).msg = format!("Sudo error: {}", e);
|
lock!(state).msg = format!("Sudo error: {}", e);
|
||||||
lock!(state).testing = false;
|
lock!(state).testing = false;
|
||||||
return
|
return;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn testing sudo
|
// Spawn testing sudo
|
||||||
|
@ -158,30 +157,44 @@ impl SudoState {
|
||||||
// results for 5 seconds in a loop.
|
// results for 5 seconds in a loop.
|
||||||
for i in 1..=5 {
|
for i in 1..=5 {
|
||||||
match sudo.try_wait() {
|
match sudo.try_wait() {
|
||||||
Ok(Some(code)) => if code.success() {
|
Ok(Some(code)) => {
|
||||||
|
if code.success() {
|
||||||
info!("Sudo | Password ... OK!");
|
info!("Sudo | Password ... OK!");
|
||||||
lock!(state).success = true;
|
lock!(state).success = true;
|
||||||
break
|
break;
|
||||||
},
|
}
|
||||||
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
info!("Sudo | Waiting [{}/5]...", i);
|
info!("Sudo | Waiting [{}/5]...", i);
|
||||||
std::thread::sleep(SECOND);
|
std::thread::sleep(SECOND);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Sudo | Couldn't reset timestamp: {}", e);
|
error!("Sudo | Couldn't reset timestamp: {}", e);
|
||||||
Self::wipe(&state);
|
Self::wipe(&state);
|
||||||
lock!(state).msg = format!("Sudo error: {}", e);
|
lock!(state).msg = format!("Sudo error: {}", e);
|
||||||
lock!(state).testing = false;
|
lock!(state).testing = false;
|
||||||
return
|
return;
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = sudo.kill() { warn!("Sudo | Kill error (it probably already exited): {}", e); }
|
}
|
||||||
|
if let Err(e) = sudo.kill() {
|
||||||
|
warn!("Sudo | Kill error (it probably already exited): {}", e);
|
||||||
|
}
|
||||||
if lock!(state).success {
|
if lock!(state).success {
|
||||||
match lock!(state).signal {
|
match lock!(state).signal {
|
||||||
ProcessSignal::Restart => crate::helper::Helper::restart_xmrig(&helper, &xmrig, &path, Arc::clone(&state)),
|
ProcessSignal::Restart => crate::helper::Helper::restart_xmrig(
|
||||||
|
&helper,
|
||||||
|
&xmrig,
|
||||||
|
&path,
|
||||||
|
Arc::clone(&state),
|
||||||
|
),
|
||||||
ProcessSignal::Stop => crate::helper::Helper::stop_xmrig(&helper),
|
ProcessSignal::Stop => crate::helper::Helper::stop_xmrig(&helper),
|
||||||
_ => crate::helper::Helper::start_xmrig(&helper, &xmrig, &path, Arc::clone(&state)),
|
_ => crate::helper::Helper::start_xmrig(
|
||||||
|
&helper,
|
||||||
|
&xmrig,
|
||||||
|
&path,
|
||||||
|
Arc::clone(&state),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lock!(state).msg = "Incorrect password! (or sudo timeout)".to_string();
|
lock!(state).msg = "Incorrect password! (or sudo timeout)".to_string();
|
||||||
|
|
418
src/update.rs
418
src/update.rs
|
@ -24,27 +24,23 @@
|
||||||
// b. auto-update at startup
|
// b. auto-update at startup
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Imports
|
//---------------------------------------------------------------------------------------------------- Imports
|
||||||
use anyhow::{anyhow,Error};
|
use crate::{
|
||||||
|
constants::GUPAX_VERSION, disk::*, macros::*, update::Name::*, ErrorButtons, ErrorFerris,
|
||||||
|
ErrorState, Restart,
|
||||||
|
};
|
||||||
|
use anyhow::{anyhow, Error};
|
||||||
use arti_client::TorClient;
|
use arti_client::TorClient;
|
||||||
use arti_hyper::*;
|
use arti_hyper::*;
|
||||||
use crate::{
|
|
||||||
constants::GUPAX_VERSION,
|
|
||||||
disk::*,
|
|
||||||
update::Name::*,
|
|
||||||
ErrorState,ErrorFerris,ErrorButtons,
|
|
||||||
Restart,
|
|
||||||
macros::*,
|
|
||||||
};
|
|
||||||
use hyper::{
|
use hyper::{
|
||||||
Client,Body,Request,
|
header::{HeaderValue, LOCATION},
|
||||||
header::{HeaderValue,LOCATION},
|
Body, Client, Request,
|
||||||
};
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use serde::{Serialize,Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::{Path,PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc,Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
@ -52,15 +48,12 @@ use walkdir::WalkDir;
|
||||||
// tls implementation so this makes it fall back to the openssl variant.
|
// tls implementation so this makes it fall back to the openssl variant.
|
||||||
//
|
//
|
||||||
// https://gitlab.torproject.org/tpo/core/arti/-/issues/715
|
// https://gitlab.torproject.org/tpo/core/arti/-/issues/715
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
use tls_api_openssl::TlsConnector;
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
use tls_api_native_tls::TlsConnector;
|
use tls_api_native_tls::TlsConnector;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use tls_api_openssl::TlsConnector;
|
||||||
|
|
||||||
use tls_api::{
|
use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder};
|
||||||
TlsConnector as TlsConnectorTrait,
|
|
||||||
TlsConnectorBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
@ -169,14 +162,41 @@ use impl_platform::*;
|
||||||
|
|
||||||
const VALID_GUPAX: [&str; 3] = [VALID_GUPAX_1, VALID_GUPAX_2, VALID_GUPAX_3];
|
const VALID_GUPAX: [&str; 3] = [VALID_GUPAX_1, VALID_GUPAX_2, VALID_GUPAX_3];
|
||||||
const VALID_XMRIG: [&str; 4] = [VALID_XMRIG_1, VALID_XMRIG_2, VALID_XMRIG_3, VALID_XMRIG_4];
|
const VALID_XMRIG: [&str; 4] = [VALID_XMRIG_1, VALID_XMRIG_2, VALID_XMRIG_3, VALID_XMRIG_4];
|
||||||
const VALID_P2POOL: [&str; 4] = [VALID_P2POOL_1, VALID_P2POOL_2, VALID_P2POOL_3, VALID_P2POOL_4];
|
const VALID_P2POOL: [&str; 4] = [
|
||||||
|
VALID_P2POOL_1,
|
||||||
|
VALID_P2POOL_2,
|
||||||
|
VALID_P2POOL_3,
|
||||||
|
VALID_P2POOL_4,
|
||||||
|
];
|
||||||
|
|
||||||
// Some fake Curl/Wget user-agents because GitHub API requires one and a Tor browser
|
// Some fake Curl/Wget user-agents because GitHub API requires one and a Tor browser
|
||||||
// user-agent might be fingerprintable without all the associated headers.
|
// user-agent might be fingerprintable without all the associated headers.
|
||||||
const FAKE_USER_AGENT: [&str; 25] = [
|
const FAKE_USER_AGENT: [&str; 25] = [
|
||||||
"Wget/1.16.3","Wget/1.17","Wget/1.17.1","Wget/1.18","Wget/1.18","Wget/1.19","Wget/1.19.1","Wget/1.19.2","Wget/1.19.3","Wget/1.19.4",
|
"Wget/1.16.3",
|
||||||
"Wget/1.19.5","Wget/1.20","Wget/1.20.1","Wget/1.20.2","Wget/1.20.3","Wget/1.21","Wget/1.21.1","Wget/1.21.2","Wget/1.21.3","Wget/1.21.4",
|
"Wget/1.17",
|
||||||
"curl/7.65.3","curl/7.66.0","curl/7.67.0","curl/7.68.0","curl/8.4.0",
|
"Wget/1.17.1",
|
||||||
|
"Wget/1.18",
|
||||||
|
"Wget/1.18",
|
||||||
|
"Wget/1.19",
|
||||||
|
"Wget/1.19.1",
|
||||||
|
"Wget/1.19.2",
|
||||||
|
"Wget/1.19.3",
|
||||||
|
"Wget/1.19.4",
|
||||||
|
"Wget/1.19.5",
|
||||||
|
"Wget/1.20",
|
||||||
|
"Wget/1.20.1",
|
||||||
|
"Wget/1.20.2",
|
||||||
|
"Wget/1.20.3",
|
||||||
|
"Wget/1.21",
|
||||||
|
"Wget/1.21.1",
|
||||||
|
"Wget/1.21.2",
|
||||||
|
"Wget/1.21.3",
|
||||||
|
"Wget/1.21.4",
|
||||||
|
"curl/7.65.3",
|
||||||
|
"curl/7.66.0",
|
||||||
|
"curl/7.67.0",
|
||||||
|
"curl/7.68.0",
|
||||||
|
"curl/8.4.0",
|
||||||
];
|
];
|
||||||
|
|
||||||
const MSG_NONE: &str = "No update in progress";
|
const MSG_NONE: &str = "No update in progress";
|
||||||
|
@ -194,7 +214,8 @@ const MSG_EXTRACT: &str = "Extracting packages";
|
||||||
const MSG_UPGRADE: &str = "Upgrading packages";
|
const MSG_UPGRADE: &str = "Upgrading packages";
|
||||||
pub const MSG_SUCCESS: &str = "Update successful";
|
pub const MSG_SUCCESS: &str = "Update successful";
|
||||||
pub const MSG_FAILED: &str = "Update failed";
|
pub const MSG_FAILED: &str = "Update failed";
|
||||||
pub const MSG_FAILED_HELP: &str = "Consider manually replacing your executable from: https://gupax.io/downloads";
|
pub const MSG_FAILED_HELP: &str =
|
||||||
|
"Consider manually replacing your executable from: https://gupax.io/downloads";
|
||||||
|
|
||||||
const INIT: &str = "------------------- Init -------------------";
|
const INIT: &str = "------------------- Init -------------------";
|
||||||
const METADATA: &str = "----------------- Metadata -----------------";
|
const METADATA: &str = "----------------- Metadata -----------------";
|
||||||
|
@ -211,9 +232,15 @@ pub fn check_p2pool_path(path: &str) -> bool {
|
||||||
};
|
};
|
||||||
let path = match path.file_name() {
|
let path = match path.file_name() {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => { error!("Couldn't get P2Pool file name"); return false; },
|
None => {
|
||||||
|
error!("Couldn't get P2Pool file name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
path == VALID_P2POOL[0] || path == VALID_P2POOL[1] || path == VALID_P2POOL[2] || path == VALID_P2POOL[3]
|
path == VALID_P2POOL[0]
|
||||||
|
|| path == VALID_P2POOL[1]
|
||||||
|
|| path == VALID_P2POOL[2]
|
||||||
|
|| path == VALID_P2POOL[3]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_xmrig_path(path: &str) -> bool {
|
pub fn check_xmrig_path(path: &str) -> bool {
|
||||||
|
@ -223,9 +250,15 @@ pub fn check_xmrig_path(path: &str) -> bool {
|
||||||
};
|
};
|
||||||
let path = match path.file_name() {
|
let path = match path.file_name() {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => { error!("Couldn't get XMRig file name"); return false; },
|
None => {
|
||||||
|
error!("Couldn't get XMRig file name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
path == VALID_XMRIG[0] || path == VALID_XMRIG[1] || path == VALID_XMRIG[2] || path == VALID_XMRIG[3]
|
path == VALID_XMRIG[0]
|
||||||
|
|| path == VALID_XMRIG[1]
|
||||||
|
|| path == VALID_XMRIG[2]
|
||||||
|
|| path == VALID_XMRIG[3]
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Update struct/impl
|
//---------------------------------------------------------------------------------------------------- Update struct/impl
|
||||||
|
@ -306,7 +339,9 @@ impl Update {
|
||||||
// Below is async, bootstraps immediately but has issues when recreating the circuit
|
// Below is async, bootstraps immediately but has issues when recreating the circuit
|
||||||
// let tor = TorClient::create_bootstrapped(TorClientConfig::default()).await?;
|
// let tor = TorClient::create_bootstrapped(TorClientConfig::default()).await?;
|
||||||
// This one below is non-async, and doesn't bootstrap immediately.
|
// This one below is non-async, and doesn't bootstrap immediately.
|
||||||
let tor = TorClient::builder().bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand).create_unbootstrapped()?;
|
let tor = TorClient::builder()
|
||||||
|
.bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand)
|
||||||
|
.create_unbootstrapped()?;
|
||||||
// This makes sure the Tor circuit is different each time
|
// This makes sure the Tor circuit is different each time
|
||||||
let tor = TorClient::isolated_client(&tor);
|
let tor = TorClient::isolated_client(&tor);
|
||||||
let tls = TlsConnector::builder()?.build()?;
|
let tls = TlsConnector::builder()?.build()?;
|
||||||
|
@ -328,7 +363,14 @@ impl Update {
|
||||||
// actually contains the code. This is so that everytime
|
// actually contains the code. This is so that everytime
|
||||||
// an update needs to happen (Gupax tab, auto-update), the
|
// an update needs to happen (Gupax tab, auto-update), the
|
||||||
// code only needs to be edited once, here.
|
// code only needs to be edited once, here.
|
||||||
pub fn spawn_thread(og: &Arc<Mutex<State>>, gupax: &crate::disk::Gupax, state_path: &Path, update: &Arc<Mutex<Update>>, error_state: &mut ErrorState, restart: &Arc<Mutex<Restart>>) {
|
pub fn spawn_thread(
|
||||||
|
og: &Arc<Mutex<State>>,
|
||||||
|
gupax: &crate::disk::Gupax,
|
||||||
|
state_path: &Path,
|
||||||
|
update: &Arc<Mutex<Update>>,
|
||||||
|
error_state: &mut ErrorState,
|
||||||
|
restart: &Arc<Mutex<Restart>>,
|
||||||
|
) {
|
||||||
// We really shouldn't be in the function for
|
// We really shouldn't be in the function for
|
||||||
// the Linux distro Gupax (UI gets disabled)
|
// the Linux distro Gupax (UI gets disabled)
|
||||||
// but if somehow get in here, just return.
|
// but if somehow get in here, just return.
|
||||||
|
@ -341,7 +383,17 @@ impl Update {
|
||||||
// Attempt relative to absolute path
|
// Attempt relative to absolute path
|
||||||
let p2pool_path = match into_absolute_path(gupax.p2pool_path.clone()) {
|
let p2pool_path = match into_absolute_path(gupax.p2pool_path.clone()) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => { error_state.set(format!("Provided P2Pool path could not be turned into an absolute path: {}", e), ErrorFerris::Error, ErrorButtons::Okay); return; },
|
Err(e) => {
|
||||||
|
error_state.set(
|
||||||
|
format!(
|
||||||
|
"Provided P2Pool path could not be turned into an absolute path: {}",
|
||||||
|
e
|
||||||
|
),
|
||||||
|
ErrorFerris::Error,
|
||||||
|
ErrorButtons::Okay,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Attempt to get basename
|
// Attempt to get basename
|
||||||
let file = match p2pool_path.file_name() {
|
let file = match p2pool_path.file_name() {
|
||||||
|
@ -349,10 +401,20 @@ impl Update {
|
||||||
// Attempt to turn into str
|
// Attempt to turn into str
|
||||||
match p.to_str() {
|
match p.to_str() {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => { error_state.set("Provided P2Pool path could not be turned into a UTF-8 string (are you using non-English characters?)", ErrorFerris::Error, ErrorButtons::Okay); return; },
|
None => {
|
||||||
|
error_state.set("Provided P2Pool path could not be turned into a UTF-8 string (are you using non-English characters?)", ErrorFerris::Error, ErrorButtons::Okay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error_state.set(
|
||||||
|
"Provided P2Pool path could not be found",
|
||||||
|
ErrorFerris::Error,
|
||||||
|
ErrorButtons::Okay,
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
None => { error_state.set("Provided P2Pool path could not be found", ErrorFerris::Error, ErrorButtons::Okay); return; },
|
|
||||||
};
|
};
|
||||||
// If it doesn't look like [P2Pool], its probably a bad move
|
// If it doesn't look like [P2Pool], its probably a bad move
|
||||||
// to overwrite it with an update, so set an error.
|
// to overwrite it with an update, so set an error.
|
||||||
|
@ -361,7 +423,10 @@ impl Update {
|
||||||
if check_p2pool_path(file) {
|
if check_p2pool_path(file) {
|
||||||
info!("Update | Using P2Pool path: [{}]", p2pool_path.display());
|
info!("Update | Using P2Pool path: [{}]", p2pool_path.display());
|
||||||
} else {
|
} else {
|
||||||
warn!("Update | Aborting update, incorrect P2Pool path: [{}]", file);
|
warn!(
|
||||||
|
"Update | Aborting update, incorrect P2Pool path: [{}]",
|
||||||
|
file
|
||||||
|
);
|
||||||
let text = format!("Provided P2Pool path seems incorrect. Not starting update for safety.\nTry one of these: {:?}", VALID_P2POOL);
|
let text = format!("Provided P2Pool path seems incorrect. Not starting update for safety.\nTry one of these: {:?}", VALID_P2POOL);
|
||||||
error_state.set(text, ErrorFerris::Error, ErrorButtons::Okay);
|
error_state.set(text, ErrorFerris::Error, ErrorButtons::Okay);
|
||||||
return;
|
return;
|
||||||
|
@ -370,17 +435,37 @@ impl Update {
|
||||||
// Check XMRig path for safety
|
// Check XMRig path for safety
|
||||||
let xmrig_path = match into_absolute_path(gupax.xmrig_path.clone()) {
|
let xmrig_path = match into_absolute_path(gupax.xmrig_path.clone()) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => { error_state.set(format!("Provided XMRig path could not be turned into an absolute path: {}", e), ErrorFerris::Error, ErrorButtons::Okay); return; },
|
Err(e) => {
|
||||||
|
error_state.set(
|
||||||
|
format!(
|
||||||
|
"Provided XMRig path could not be turned into an absolute path: {}",
|
||||||
|
e
|
||||||
|
),
|
||||||
|
ErrorFerris::Error,
|
||||||
|
ErrorButtons::Okay,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let file = match xmrig_path.file_name() {
|
let file = match xmrig_path.file_name() {
|
||||||
Some(p) => {
|
Some(p) => {
|
||||||
// Attempt to turn into str
|
// Attempt to turn into str
|
||||||
match p.to_str() {
|
match p.to_str() {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => { error_state.set("Provided XMRig path could not be turned into a UTF-8 string (are you using non-English characters?)", ErrorFerris::Error, ErrorButtons::Okay); return; },
|
None => {
|
||||||
|
error_state.set("Provided XMRig path could not be turned into a UTF-8 string (are you using non-English characters?)", ErrorFerris::Error, ErrorButtons::Okay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error_state.set(
|
||||||
|
"Provided XMRig path could not be found",
|
||||||
|
ErrorFerris::Error,
|
||||||
|
ErrorButtons::Okay,
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
None => { error_state.set("Provided XMRig path could not be found", ErrorFerris::Error, ErrorButtons::Okay); return; },
|
|
||||||
};
|
};
|
||||||
if check_xmrig_path(file) {
|
if check_xmrig_path(file) {
|
||||||
info!("Update | Using XMRig path: [{}]", xmrig_path.display());
|
info!("Update | Using XMRig path: [{}]", xmrig_path.display());
|
||||||
|
@ -402,7 +487,7 @@ impl Update {
|
||||||
let update = Arc::clone(update);
|
let update = Arc::clone(update);
|
||||||
let restart = Arc::clone(restart);
|
let restart = Arc::clone(restart);
|
||||||
info!("Spawning update thread...");
|
info!("Spawning update thread...");
|
||||||
std::thread::spawn(move|| {
|
std::thread::spawn(move || {
|
||||||
match Update::start(update.clone(), og.clone(), state_ver.clone(), restart) {
|
match Update::start(update.clone(), og.clone(), state_ver.clone(), restart) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Update | Saving state...");
|
info!("Update | Saving state...");
|
||||||
|
@ -413,16 +498,17 @@ impl Update {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Update | Saving state ... FAIL: {}", e);
|
warn!("Update | Saving state ... FAIL: {}", e);
|
||||||
lock!(og).version = original_version;
|
lock!(og).version = original_version;
|
||||||
*lock2!(update,msg) = "Saving new versions into state failed".to_string();
|
*lock2!(update, msg) =
|
||||||
},
|
"Saving new versions into state failed".to_string();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
info!("Update ... FAIL: {}", e);
|
info!("Update ... FAIL: {}", e);
|
||||||
*lock2!(update,msg) = format!("{} | {}\n{}", MSG_FAILED, e, MSG_FAILED_HELP);
|
*lock2!(update, msg) = format!("{} | {}\n{}", MSG_FAILED, e, MSG_FAILED_HELP);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
*lock2!(update,updating) = false;
|
*lock2!(update, updating) = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,39 +522,42 @@ impl Update {
|
||||||
// 4. loop over vec, download links
|
// 4. loop over vec, download links
|
||||||
// 5. extract, upgrade
|
// 5. extract, upgrade
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn start(update: Arc<Mutex<Self>>, _og: Arc<Mutex<State>>, state_ver: Arc<Mutex<Version>>, restart: Arc<Mutex<Restart>>) -> Result<(), anyhow::Error> {
|
pub async fn start(
|
||||||
|
update: Arc<Mutex<Self>>,
|
||||||
|
_og: Arc<Mutex<State>>,
|
||||||
|
state_ver: Arc<Mutex<Version>>,
|
||||||
|
restart: Arc<Mutex<Restart>>,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
#[cfg(feature = "distro")]
|
#[cfg(feature = "distro")]
|
||||||
error!("Update | This is the [Linux distro] version of Gupax, updates are disabled");
|
error!("Update | This is the [Linux distro] version of Gupax, updates are disabled");
|
||||||
#[cfg(feature = "distro")]
|
#[cfg(feature = "distro")]
|
||||||
return Err(anyhow!("This is the [Linux distro] version of Gupax, updates are disabled"));
|
return Err(anyhow!(
|
||||||
|
"This is the [Linux distro] version of Gupax, updates are disabled"
|
||||||
|
));
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Init
|
//---------------------------------------------------------------------------------------------------- Init
|
||||||
*lock2!(update,updating) = true;
|
*lock2!(update, updating) = true;
|
||||||
// Set timer
|
// Set timer
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
|
|
||||||
// Set progress bar
|
// Set progress bar
|
||||||
*lock2!(update,msg) = MSG_START.to_string();
|
*lock2!(update, msg) = MSG_START.to_string();
|
||||||
*lock2!(update,prog) = 0.0;
|
*lock2!(update, prog) = 0.0;
|
||||||
info!("Update | {}", INIT);
|
info!("Update | {}", INIT);
|
||||||
|
|
||||||
// Get temporary directory
|
// Get temporary directory
|
||||||
let msg = MSG_TMP.to_string();
|
let msg = MSG_TMP.to_string();
|
||||||
info!("Update | {}", msg);
|
info!("Update | {}", msg);
|
||||||
*lock2!(update,msg) = msg;
|
*lock2!(update, msg) = msg;
|
||||||
let tmp_dir = Self::get_tmp_dir()?;
|
let tmp_dir = Self::get_tmp_dir()?;
|
||||||
std::fs::create_dir(&tmp_dir)?;
|
std::fs::create_dir(&tmp_dir)?;
|
||||||
|
|
||||||
// Make Pkg vector
|
// Make Pkg vector
|
||||||
let mut vec = vec![
|
let mut vec = vec![Pkg::new(Gupax), Pkg::new(P2pool), Pkg::new(Xmrig)];
|
||||||
Pkg::new(Gupax),
|
|
||||||
Pkg::new(P2pool),
|
|
||||||
Pkg::new(Xmrig),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Generate fake user-agent
|
// Generate fake user-agent
|
||||||
let user_agent = Pkg::get_user_agent();
|
let user_agent = Pkg::get_user_agent();
|
||||||
*lock2!(update,prog) = 5.0;
|
*lock2!(update, prog) = 5.0;
|
||||||
|
|
||||||
// Create Tor/HTTPS client
|
// Create Tor/HTTPS client
|
||||||
let lock = lock!(update);
|
let lock = lock!(update);
|
||||||
|
@ -484,11 +573,11 @@ impl Update {
|
||||||
}
|
}
|
||||||
drop(lock);
|
drop(lock);
|
||||||
let mut client = Self::get_client(tor)?;
|
let mut client = Self::get_client(tor)?;
|
||||||
*lock2!(update,prog) += 5.0;
|
*lock2!(update, prog) += 5.0;
|
||||||
info!("Update | Init ... OK ... {}%", lock2!(update,prog));
|
info!("Update | Init ... OK ... {}%", lock2!(update, prog));
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Metadata
|
//---------------------------------------------------------------------------------------------------- Metadata
|
||||||
*lock2!(update,msg) = MSG_METADATA.to_string();
|
*lock2!(update, msg) = MSG_METADATA.to_string();
|
||||||
info!("Update | {}", METADATA);
|
info!("Update | {}", METADATA);
|
||||||
let mut vec2 = vec![];
|
let mut vec2 = vec![];
|
||||||
// Loop process:
|
// Loop process:
|
||||||
|
@ -503,7 +592,9 @@ impl Update {
|
||||||
// function itself but for some reason, it was getting skipped over,
|
// function itself but for some reason, it was getting skipped over,
|
||||||
// so the [new_ver] check is now here, in the outer scope.
|
// so the [new_ver] check is now here, in the outer scope.
|
||||||
for i in 1..=3 {
|
for i in 1..=3 {
|
||||||
if i > 1 { *lock2!(update,msg) = format!("{} [{}/3]", MSG_METADATA_RETRY, i); }
|
if i > 1 {
|
||||||
|
*lock2!(update, msg) = format!("{} [{}/3]", MSG_METADATA_RETRY, i);
|
||||||
|
}
|
||||||
let mut handles: Vec<JoinHandle<Result<(), anyhow::Error>>> = vec![];
|
let mut handles: Vec<JoinHandle<Result<(), anyhow::Error>>> = vec![];
|
||||||
for pkg in vec.iter() {
|
for pkg in vec.iter() {
|
||||||
// Clone data before sending to async
|
// Clone data before sending to async
|
||||||
|
@ -514,7 +605,9 @@ impl Update {
|
||||||
let handle: JoinHandle<Result<(), anyhow::Error>> = tokio::spawn(async move {
|
let handle: JoinHandle<Result<(), anyhow::Error>> = tokio::spawn(async move {
|
||||||
match client {
|
match client {
|
||||||
ClientEnum::Tor(t) => Pkg::get_metadata(new_ver, t, link, user_agent).await,
|
ClientEnum::Tor(t) => Pkg::get_metadata(new_ver, t, link, user_agent).await,
|
||||||
ClientEnum::Https(h) => Pkg::get_metadata(new_ver, h, link, user_agent).await,
|
ClientEnum::Https(h) => {
|
||||||
|
Pkg::get_metadata(new_ver, h, link, user_agent).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
|
@ -524,17 +617,19 @@ impl Update {
|
||||||
// Two [??] will send the error.
|
// Two [??] will send the error.
|
||||||
// We don't actually want to return the error here since we
|
// We don't actually want to return the error here since we
|
||||||
// prefer looping and retrying over immediately erroring out.
|
// prefer looping and retrying over immediately erroring out.
|
||||||
if let Err(e) = handle.await? { warn!("Update | {}", e) }
|
if let Err(e) = handle.await? {
|
||||||
|
warn!("Update | {}", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Check for empty version
|
// Check for empty version
|
||||||
let mut indexes = vec![];
|
let mut indexes = vec![];
|
||||||
for (index, pkg) in vec.iter().enumerate() {
|
for (index, pkg) in vec.iter().enumerate() {
|
||||||
if lock!(pkg.new_ver).is_empty() {
|
if lock!(pkg.new_ver).is_empty() {
|
||||||
warn!("Update | {} failed, attempt [{}/3]...", pkg.name, i+1);
|
warn!("Update | {} failed, attempt [{}/3]...", pkg.name, i + 1);
|
||||||
} else {
|
} else {
|
||||||
indexes.push(index);
|
indexes.push(index);
|
||||||
vec2.push(pkg.clone());
|
vec2.push(pkg.clone());
|
||||||
*lock2!(update,prog) += 10.0;
|
*lock2!(update, prog) += 10.0;
|
||||||
info!("Update | {} {} ... OK", pkg.name, lock!(pkg.new_ver));
|
info!("Update | {} {} ... OK", pkg.name, lock!(pkg.new_ver));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -545,7 +640,9 @@ impl Update {
|
||||||
for index in indexes {
|
for index in indexes {
|
||||||
vec.remove(index);
|
vec.remove(index);
|
||||||
}
|
}
|
||||||
if vec.is_empty() { break }
|
if vec.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
// Some Tor exit nodes seem to be blocked by GitHub's API,
|
// Some Tor exit nodes seem to be blocked by GitHub's API,
|
||||||
// so recreate the circuit every loop.
|
// so recreate the circuit every loop.
|
||||||
if tor {
|
if tor {
|
||||||
|
@ -554,14 +651,14 @@ impl Update {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if vec.is_empty() {
|
if vec.is_empty() {
|
||||||
info!("Update | Metadata ... OK ... {}%", lock2!(update,prog));
|
info!("Update | Metadata ... OK ... {}%", lock2!(update, prog));
|
||||||
} else {
|
} else {
|
||||||
error!("Update | Metadata ... FAIL");
|
error!("Update | Metadata ... FAIL");
|
||||||
return Err(anyhow!("Metadata fetch failed"))
|
return Err(anyhow!("Metadata fetch failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Compare
|
//---------------------------------------------------------------------------------------------------- Compare
|
||||||
*lock2!(update,msg) = MSG_COMPARE.to_string();
|
*lock2!(update, msg) = MSG_COMPARE.to_string();
|
||||||
info!("Update | {}", COMPARE);
|
info!("Update | {}", COMPARE);
|
||||||
let mut vec3 = vec![];
|
let mut vec3 = vec![];
|
||||||
let mut new_pkgs = vec![];
|
let mut new_pkgs = vec![];
|
||||||
|
@ -592,33 +689,41 @@ impl Update {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if diff {
|
if diff {
|
||||||
info!("Update | {} {} != {} ... ADDING", pkg.name, old_ver, new_ver);
|
info!(
|
||||||
|
"Update | {} {} != {} ... ADDING",
|
||||||
|
pkg.name, old_ver, new_ver
|
||||||
|
);
|
||||||
new_pkgs.push(format!("\n{} {} -> {}", name, old_ver, new_ver));
|
new_pkgs.push(format!("\n{} {} -> {}", name, old_ver, new_ver));
|
||||||
vec3.push(pkg);
|
vec3.push(pkg);
|
||||||
} else {
|
} else {
|
||||||
info!("Update | {} {} == {} ... SKIPPING", pkg.name, old_ver, new_ver);
|
info!(
|
||||||
|
"Update | {} {} == {} ... SKIPPING",
|
||||||
|
pkg.name, old_ver, new_ver
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*lock2!(update,prog) += 5.0;
|
*lock2!(update, prog) += 5.0;
|
||||||
info!("Update | Compare ... OK ... {}%", lock2!(update,prog));
|
info!("Update | Compare ... OK ... {}%", lock2!(update, prog));
|
||||||
|
|
||||||
// Return if 0 (all packages up-to-date)
|
// Return if 0 (all packages up-to-date)
|
||||||
// Get amount of packages to divide up the percentage increases
|
// Get amount of packages to divide up the percentage increases
|
||||||
let pkg_amount = vec3.len() as f32;
|
let pkg_amount = vec3.len() as f32;
|
||||||
if pkg_amount == 0.0 {
|
if pkg_amount == 0.0 {
|
||||||
info!("Update | All packages up-to-date ... RETURNING");
|
info!("Update | All packages up-to-date ... RETURNING");
|
||||||
*lock2!(update,prog) = 100.0;
|
*lock2!(update, prog) = 100.0;
|
||||||
*lock2!(update,msg) = MSG_UP_TO_DATE.to_string();
|
*lock2!(update, msg) = MSG_UP_TO_DATE.to_string();
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
let new_pkgs: String = new_pkgs.concat();
|
let new_pkgs: String = new_pkgs.concat();
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Download
|
//---------------------------------------------------------------------------------------------------- Download
|
||||||
*lock2!(update,msg) = format!("{}{}", MSG_DOWNLOAD, new_pkgs);
|
*lock2!(update, msg) = format!("{}{}", MSG_DOWNLOAD, new_pkgs);
|
||||||
info!("Update | {}", DOWNLOAD);
|
info!("Update | {}", DOWNLOAD);
|
||||||
let mut vec4 = vec![];
|
let mut vec4 = vec![];
|
||||||
for i in 1..=3 {
|
for i in 1..=3 {
|
||||||
if i > 1 { *lock2!(update,msg) = format!("{} [{}/3]{}", MSG_DOWNLOAD_RETRY, i, new_pkgs); }
|
if i > 1 {
|
||||||
|
*lock2!(update, msg) = format!("{} [{}/3]{}", MSG_DOWNLOAD_RETRY, i, new_pkgs);
|
||||||
|
}
|
||||||
let mut handles: Vec<JoinHandle<Result<(), anyhow::Error>>> = vec![];
|
let mut handles: Vec<JoinHandle<Result<(), anyhow::Error>>> = vec![];
|
||||||
for pkg in vec3.iter() {
|
for pkg in vec3.iter() {
|
||||||
// Clone data before async
|
// Clone data before async
|
||||||
|
@ -629,8 +734,20 @@ impl Update {
|
||||||
// Example: https://github.com/hinto-janai/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-x64-standalone
|
// Example: https://github.com/hinto-janai/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-x64-standalone
|
||||||
// XMRig doesn't have a [v], so slice it out
|
// XMRig doesn't have a [v], so slice it out
|
||||||
let link = match pkg.name {
|
let link = match pkg.name {
|
||||||
Name::Xmrig => pkg.link_prefix.to_string() + &version + pkg.link_suffix + &version[1..] + pkg.link_extension,
|
Name::Xmrig => {
|
||||||
_ => pkg.link_prefix.to_string() + &version + pkg.link_suffix + &version + pkg.link_extension,
|
pkg.link_prefix.to_string()
|
||||||
|
+ &version
|
||||||
|
+ pkg.link_suffix
|
||||||
|
+ &version[1..]
|
||||||
|
+ pkg.link_extension
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
pkg.link_prefix.to_string()
|
||||||
|
+ &version
|
||||||
|
+ pkg.link_suffix
|
||||||
|
+ &version
|
||||||
|
+ pkg.link_extension
|
||||||
|
}
|
||||||
};
|
};
|
||||||
info!("Update | {} ... {}", pkg.name, link);
|
info!("Update | {} ... {}", pkg.name, link);
|
||||||
let handle: JoinHandle<Result<(), anyhow::Error>> = tokio::spawn(async move {
|
let handle: JoinHandle<Result<(), anyhow::Error>> = tokio::spawn(async move {
|
||||||
|
@ -643,7 +760,9 @@ impl Update {
|
||||||
}
|
}
|
||||||
// Handle await
|
// Handle await
|
||||||
for handle in handles {
|
for handle in handles {
|
||||||
if let Err(e) = handle.await? { warn!("Update | {}", e) }
|
if let Err(e) = handle.await? {
|
||||||
|
warn!("Update | {}", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Check for empty bytes
|
// Check for empty bytes
|
||||||
let mut indexes = vec![];
|
let mut indexes = vec![];
|
||||||
|
@ -653,7 +772,7 @@ impl Update {
|
||||||
} else {
|
} else {
|
||||||
indexes.push(index);
|
indexes.push(index);
|
||||||
vec4.push(pkg.clone());
|
vec4.push(pkg.clone());
|
||||||
*lock2!(update,prog) += (30.0 / pkg_amount).round();
|
*lock2!(update, prog) += (30.0 / pkg_amount).round();
|
||||||
info!("Update | {} ... OK", pkg.name);
|
info!("Update | {} ... OK", pkg.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -664,17 +783,19 @@ impl Update {
|
||||||
for index in indexes {
|
for index in indexes {
|
||||||
vec3.remove(index);
|
vec3.remove(index);
|
||||||
}
|
}
|
||||||
if vec3.is_empty() { break }
|
if vec3.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if vec3.is_empty() {
|
if vec3.is_empty() {
|
||||||
info!("Update | Download ... OK ... {}%", *lock2!(update,prog));
|
info!("Update | Download ... OK ... {}%", *lock2!(update, prog));
|
||||||
} else {
|
} else {
|
||||||
error!("Update | Download ... FAIL");
|
error!("Update | Download ... FAIL");
|
||||||
return Err(anyhow!("Download failed"))
|
return Err(anyhow!("Download failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Extract
|
//---------------------------------------------------------------------------------------------------- Extract
|
||||||
*lock2!(update,msg) = format!("{}{}", MSG_EXTRACT, new_pkgs);
|
*lock2!(update, msg) = format!("{}{}", MSG_EXTRACT, new_pkgs);
|
||||||
info!("Update | {}", EXTRACT);
|
info!("Update | {}", EXTRACT);
|
||||||
for pkg in vec4.iter() {
|
for pkg in vec4.iter() {
|
||||||
let tmp = match pkg.name {
|
let tmp = match pkg.name {
|
||||||
|
@ -682,20 +803,24 @@ impl Update {
|
||||||
_ => tmp_dir.to_owned() + &pkg.name.to_string(),
|
_ => tmp_dir.to_owned() + &pkg.name.to_string(),
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
ZipArchive::extract(&mut ZipArchive::new(std::io::Cursor::new(lock!(pkg.bytes).as_ref()))?, tmp)?;
|
ZipArchive::extract(
|
||||||
|
&mut ZipArchive::new(std::io::Cursor::new(lock!(pkg.bytes).as_ref()))?,
|
||||||
|
tmp,
|
||||||
|
)?;
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
tar::Archive::new(flate2::read::GzDecoder::new(lock!(pkg.bytes).as_ref())).unpack(tmp)?;
|
tar::Archive::new(flate2::read::GzDecoder::new(lock!(pkg.bytes).as_ref()))
|
||||||
*lock2!(update,prog) += (5.0 / pkg_amount).round();
|
.unpack(tmp)?;
|
||||||
|
*lock2!(update, prog) += (5.0 / pkg_amount).round();
|
||||||
info!("Update | {} ... OK", pkg.name);
|
info!("Update | {} ... OK", pkg.name);
|
||||||
}
|
}
|
||||||
info!("Update | Extract ... OK ... {}%", *lock2!(update,prog));
|
info!("Update | Extract ... OK ... {}%", *lock2!(update, prog));
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Upgrade
|
//---------------------------------------------------------------------------------------------------- Upgrade
|
||||||
// 1. Walk directories
|
// 1. Walk directories
|
||||||
// 2. If basename matches known binary name, start
|
// 2. If basename matches known binary name, start
|
||||||
// 3. Rename tmp path into current path
|
// 3. Rename tmp path into current path
|
||||||
// 4. Update [State/Version]
|
// 4. Update [State/Version]
|
||||||
*lock2!(update,msg) = format!("{}{}", MSG_UPGRADE, new_pkgs);
|
*lock2!(update, msg) = format!("{}{}", MSG_UPGRADE, new_pkgs);
|
||||||
info!("Update | {}", UPGRADE);
|
info!("Update | {}", UPGRADE);
|
||||||
// If this bool doesn't get set, something has gone wrong because
|
// If this bool doesn't get set, something has gone wrong because
|
||||||
// we _didn't_ find a binary even though we downloaded it.
|
// we _didn't_ find a binary even though we downloaded it.
|
||||||
|
@ -703,16 +828,21 @@ impl Update {
|
||||||
for entry in WalkDir::new(tmp_dir.clone()) {
|
for entry in WalkDir::new(tmp_dir.clone()) {
|
||||||
let entry = entry?.clone();
|
let entry = entry?.clone();
|
||||||
// If not a file, continue
|
// If not a file, continue
|
||||||
if ! entry.file_type().is_file() { continue }
|
if !entry.file_type().is_file() {
|
||||||
let basename = entry.file_name().to_str().ok_or_else(|| anyhow!("WalkDir basename failed"))?;
|
continue;
|
||||||
|
}
|
||||||
|
let basename = entry
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| anyhow!("WalkDir basename failed"))?;
|
||||||
match basename {
|
match basename {
|
||||||
VALID_GUPAX_1|VALID_GUPAX_2|VALID_GUPAX_3|
|
VALID_GUPAX_1 | VALID_GUPAX_2 | VALID_GUPAX_3 | VALID_P2POOL_1 | VALID_P2POOL_2
|
||||||
VALID_P2POOL_1|VALID_P2POOL_2|VALID_P2POOL_3|VALID_P2POOL_4|
|
| VALID_P2POOL_3 | VALID_P2POOL_4 | VALID_XMRIG_1 | VALID_XMRIG_2
|
||||||
VALID_XMRIG_1|VALID_XMRIG_2|VALID_XMRIG_3|VALID_XMRIG_4 => {
|
| VALID_XMRIG_3 | VALID_XMRIG_4 => {
|
||||||
found = true;
|
found = true;
|
||||||
let name = match basename {
|
let name = match basename {
|
||||||
VALID_GUPAX_1|VALID_GUPAX_2|VALID_GUPAX_3 => Gupax,
|
VALID_GUPAX_1 | VALID_GUPAX_2 | VALID_GUPAX_3 => Gupax,
|
||||||
VALID_P2POOL_1|VALID_P2POOL_2|VALID_P2POOL_3|VALID_P2POOL_4 => P2pool,
|
VALID_P2POOL_1 | VALID_P2POOL_2 | VALID_P2POOL_3 | VALID_P2POOL_4 => P2pool,
|
||||||
_ => Xmrig,
|
_ => Xmrig,
|
||||||
};
|
};
|
||||||
let path = match name {
|
let path = match name {
|
||||||
|
@ -733,13 +863,24 @@ impl Update {
|
||||||
P2pool => tmp_dir.clone() + "p2pool_old.exe",
|
P2pool => tmp_dir.clone() + "p2pool_old.exe",
|
||||||
Xmrig => tmp_dir.clone() + "xmrig_old.exe",
|
Xmrig => tmp_dir.clone() + "xmrig_old.exe",
|
||||||
};
|
};
|
||||||
info!("Update | WINDOWS ONLY ... Moving old [{}] -> [{}]", path.display(), tmp_windows);
|
info!(
|
||||||
|
"Update | WINDOWS ONLY ... Moving old [{}] -> [{}]",
|
||||||
|
path.display(),
|
||||||
|
tmp_windows
|
||||||
|
);
|
||||||
std::fs::rename(&path, tmp_windows)?;
|
std::fs::rename(&path, tmp_windows)?;
|
||||||
}
|
}
|
||||||
info!("Update | Moving new [{}] -> [{}]", entry.path().display(), path.display());
|
info!(
|
||||||
|
"Update | Moving new [{}] -> [{}]",
|
||||||
|
entry.path().display(),
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
// Create folder for [P2Pool/XMRig]
|
// Create folder for [P2Pool/XMRig]
|
||||||
if name == P2pool || name == Xmrig {
|
if name == P2pool || name == Xmrig {
|
||||||
std::fs::create_dir_all(path.parent().ok_or_else(|| anyhow!(format!("{} path failed", name)))?)?;
|
std::fs::create_dir_all(
|
||||||
|
path.parent()
|
||||||
|
.ok_or_else(|| anyhow!(format!("{} path failed", name)))?,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
// Move downloaded path into old path
|
// Move downloaded path into old path
|
||||||
std::fs::rename(entry.path(), path)?;
|
std::fs::rename(entry.path(), path)?;
|
||||||
|
@ -749,16 +890,20 @@ impl Update {
|
||||||
lock!(state_ver).gupax = Pkg::get_new_pkg_version(Gupax, &vec4)?;
|
lock!(state_ver).gupax = Pkg::get_new_pkg_version(Gupax, &vec4)?;
|
||||||
// If we're updating Gupax, set the [Restart] state so that the user knows to restart
|
// If we're updating Gupax, set the [Restart] state so that the user knows to restart
|
||||||
*lock!(restart) = Restart::Yes;
|
*lock!(restart) = Restart::Yes;
|
||||||
},
|
}
|
||||||
P2pool => lock!(state_ver).p2pool = Pkg::get_new_pkg_version(P2pool, &vec4)?,
|
P2pool => {
|
||||||
|
lock!(state_ver).p2pool = Pkg::get_new_pkg_version(P2pool, &vec4)?
|
||||||
|
}
|
||||||
Xmrig => lock!(state_ver).xmrig = Pkg::get_new_pkg_version(Xmrig, &vec4)?,
|
Xmrig => lock!(state_ver).xmrig = Pkg::get_new_pkg_version(Xmrig, &vec4)?,
|
||||||
};
|
};
|
||||||
*lock2!(update,prog) += (5.0 / pkg_amount).round();
|
*lock2!(update, prog) += (5.0 / pkg_amount).round();
|
||||||
},
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found { return Err(anyhow!("Fatal error: Package binary could not be found")) }
|
if !found {
|
||||||
|
return Err(anyhow!("Fatal error: Package binary could not be found"));
|
||||||
|
}
|
||||||
|
|
||||||
// Remove tmp dir (on Unix)
|
// Remove tmp dir (on Unix)
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
|
@ -769,23 +914,31 @@ impl Update {
|
||||||
let seconds = now.elapsed().as_secs();
|
let seconds = now.elapsed().as_secs();
|
||||||
info!("Update | Seconds elapsed ... [{}s]", seconds);
|
info!("Update | Seconds elapsed ... [{}s]", seconds);
|
||||||
match seconds {
|
match seconds {
|
||||||
0 => *lock2!(update,msg) = format!("{}! Took 0 seconds... What...?!{}", MSG_SUCCESS, new_pkgs),
|
0 => {
|
||||||
1 => *lock2!(update,msg) = format!("{}! Took 1 second... Wow!{}", MSG_SUCCESS, new_pkgs),
|
*lock2!(update, msg) =
|
||||||
_ => *lock2!(update,msg) = format!("{}! Took {} seconds.{}", MSG_SUCCESS, seconds, new_pkgs),
|
format!("{}! Took 0 seconds... What...?!{}", MSG_SUCCESS, new_pkgs)
|
||||||
}
|
}
|
||||||
*lock2!(update,prog) = 100.0;
|
1 => {
|
||||||
|
*lock2!(update, msg) = format!("{}! Took 1 second... Wow!{}", MSG_SUCCESS, new_pkgs)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*lock2!(update, msg) =
|
||||||
|
format!("{}! Took {} seconds.{}", MSG_SUCCESS, seconds, new_pkgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*lock2!(update, prog) = 100.0;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ClientEnum {
|
pub enum ClientEnum {
|
||||||
Tor(hyper::Client<ArtiHttpConnector<tor_rtcompat::PreferredRuntime, TlsConnector>>),
|
Tor(hyper::Client<ArtiHttpConnector<tor_rtcompat::PreferredRuntime, TlsConnector>>),
|
||||||
Https(hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>),
|
Https(hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Pkg struct/impl
|
//---------------------------------------------------------------------------------------------------- Pkg struct/impl
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Pkg {
|
pub struct Pkg {
|
||||||
name: Name,
|
name: Name,
|
||||||
link_metadata: &'static str,
|
link_metadata: &'static str,
|
||||||
|
@ -851,7 +1004,10 @@ impl Pkg {
|
||||||
let request = Request::builder()
|
let request = Request::builder()
|
||||||
.method("GET")
|
.method("GET")
|
||||||
.uri(link)
|
.uri(link)
|
||||||
.header(hyper::header::USER_AGENT, HeaderValue::from_static(user_agent))
|
.header(
|
||||||
|
hyper::header::USER_AGENT,
|
||||||
|
HeaderValue::from_static(user_agent),
|
||||||
|
)
|
||||||
.body(Body::empty())?;
|
.body(Body::empty())?;
|
||||||
Ok(request)
|
Ok(request)
|
||||||
}
|
}
|
||||||
|
@ -860,8 +1016,15 @@ impl Pkg {
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
// Get metadata using [Generic hyper::client<C>] & [Request]
|
// Get metadata using [Generic hyper::client<C>] & [Request]
|
||||||
// and change [version, prog] under an Arc<Mutex>
|
// and change [version, prog] under an Arc<Mutex>
|
||||||
async fn get_metadata<C>(new_ver: Arc<Mutex<String>>, client: Client<C>, link: String, user_agent: &'static str) -> Result<(), Error>
|
async fn get_metadata<C>(
|
||||||
where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, {
|
new_ver: Arc<Mutex<String>>,
|
||||||
|
client: Client<C>,
|
||||||
|
link: String,
|
||||||
|
user_agent: &'static str,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
let request = Pkg::get_request(link, user_agent)?;
|
let request = Pkg::get_request(link, user_agent)?;
|
||||||
let mut response = client.request(request).await?;
|
let mut response = client.request(request).await?;
|
||||||
let body = hyper::body::to_bytes(response.body_mut()).await?;
|
let body = hyper::body::to_bytes(response.body_mut()).await?;
|
||||||
|
@ -874,15 +1037,30 @@ impl Pkg {
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
// Takes a [Request], fills the appropriate [Pkg]
|
// Takes a [Request], fills the appropriate [Pkg]
|
||||||
// [bytes] field with the [Archive/Standalone]
|
// [bytes] field with the [Archive/Standalone]
|
||||||
async fn get_bytes<C>(bytes: Arc<Mutex<bytes::Bytes>>, client: Client<C>, link: String, user_agent: &'static str) -> Result<(), anyhow::Error>
|
async fn get_bytes<C>(
|
||||||
where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, {
|
bytes: Arc<Mutex<bytes::Bytes>>,
|
||||||
|
client: Client<C>,
|
||||||
|
link: String,
|
||||||
|
user_agent: &'static str,
|
||||||
|
) -> Result<(), anyhow::Error>
|
||||||
|
where
|
||||||
|
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
let request = Self::get_request(link, user_agent)?;
|
let request = Self::get_request(link, user_agent)?;
|
||||||
let mut response = client.request(request).await?;
|
let mut response = client.request(request).await?;
|
||||||
// GitHub sends a 302 redirect, so we must follow
|
// GitHub sends a 302 redirect, so we must follow
|
||||||
// the [Location] header... only if Reqwest had custom
|
// the [Location] header... only if Reqwest had custom
|
||||||
// connectors so I didn't have to manually do this...
|
// connectors so I didn't have to manually do this...
|
||||||
if response.headers().contains_key(LOCATION) {
|
if response.headers().contains_key(LOCATION) {
|
||||||
let request = Self::get_request(response.headers().get(LOCATION).ok_or_else(|| anyhow!("HTTP Location header GET failed"))?.to_str()?.to_string(), user_agent)?;
|
let request = Self::get_request(
|
||||||
|
response
|
||||||
|
.headers()
|
||||||
|
.get(LOCATION)
|
||||||
|
.ok_or_else(|| anyhow!("HTTP Location header GET failed"))?
|
||||||
|
.to_str()?
|
||||||
|
.to_string(),
|
||||||
|
user_agent,
|
||||||
|
)?;
|
||||||
response = client.request(request).await?;
|
response = client.request(request).await?;
|
||||||
}
|
}
|
||||||
let body = hyper::body::to_bytes(response.into_body()).await?;
|
let body = hyper::body::to_bytes(response.into_body()).await?;
|
||||||
|
@ -897,7 +1075,7 @@ impl Pkg {
|
||||||
fn get_new_pkg_version(name: Name, vec: &[&Pkg]) -> Result<String, Error> {
|
fn get_new_pkg_version(name: Name, vec: &[&Pkg]) -> Result<String, Error> {
|
||||||
for pkg in vec.iter() {
|
for pkg in vec.iter() {
|
||||||
if pkg.name == name {
|
if pkg.name == name {
|
||||||
return Ok(lock!(pkg.new_ver).to_string())
|
return Ok(lock!(pkg.new_ver).to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(anyhow!("Couldn't find new_pkg_version"))
|
Err(anyhow!("Couldn't find new_pkg_version"))
|
||||||
|
|
188
src/xmr.rs
188
src/xmr.rs
|
@ -22,12 +22,8 @@
|
||||||
// These represent:
|
// These represent:
|
||||||
// "(DATE, ATOMIC_UNIT, MONERO_BLOCK)"
|
// "(DATE, ATOMIC_UNIT, MONERO_BLOCK)"
|
||||||
|
|
||||||
use crate::{
|
use crate::human::*;
|
||||||
human::*,
|
use crate::regex::P2POOL_REGEX;
|
||||||
};
|
|
||||||
use crate::regex::{
|
|
||||||
P2POOL_REGEX,
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
|
@ -41,7 +37,7 @@ use log::*;
|
||||||
// [u64] can hold max: 18_446_744_073_709_551_615 which equals to 18,446,744,073 XMR (18 billion).
|
// [u64] can hold max: 18_446_744_073_709_551_615 which equals to 18,446,744,073 XMR (18 billion).
|
||||||
// Given the constant XMR tail emission of (0.3 per minute|18 per hour|432 per day|157,680 per year)
|
// Given the constant XMR tail emission of (0.3 per minute|18 per hour|432 per day|157,680 per year)
|
||||||
// this would take: 116,976~ years to overflow.
|
// this would take: 116,976~ years to overflow.
|
||||||
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct AtomicUnit(u64);
|
pub struct AtomicUnit(u64);
|
||||||
|
|
||||||
impl AtomicUnit {
|
impl AtomicUnit {
|
||||||
|
@ -117,12 +113,16 @@ impl std::fmt::Display for AtomicUnit {
|
||||||
// [0] = DATE
|
// [0] = DATE
|
||||||
// [1] = XMR IN ATOMIC-UNITS
|
// [1] = XMR IN ATOMIC-UNITS
|
||||||
// [2] = MONERO BLOCK
|
// [2] = MONERO BLOCK
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PayoutOrd(Vec<(String, AtomicUnit, HumanNumber)>);
|
pub struct PayoutOrd(Vec<(String, AtomicUnit, HumanNumber)>);
|
||||||
|
|
||||||
impl PayoutOrd {
|
impl PayoutOrd {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(vec![(String::from("????-??-?? ??:??:??.????"), AtomicUnit::new(), HumanNumber::unknown())])
|
Self(vec![(
|
||||||
|
String::from("????-??-?? ??:??:??.????"),
|
||||||
|
AtomicUnit::new(),
|
||||||
|
HumanNumber::unknown(),
|
||||||
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn from_vec(vec: Vec<(String, AtomicUnit, HumanNumber)>) -> Self {
|
pub const fn from_vec(vec: Vec<(String, AtomicUnit, HumanNumber)>) -> Self {
|
||||||
|
@ -130,13 +130,23 @@ impl PayoutOrd {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_same(a: &Self, b: &Self) -> bool {
|
pub fn is_same(a: &Self, b: &Self) -> bool {
|
||||||
if a.0.is_empty() && b.0.is_empty() { return true }
|
if a.0.is_empty() && b.0.is_empty() {
|
||||||
if a.0.len() != b.0.len() { return false }
|
return true;
|
||||||
|
}
|
||||||
|
if a.0.len() != b.0.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
for (date, atomic_unit, block) in &a.0 {
|
for (date, atomic_unit, block) in &a.0 {
|
||||||
if *date != b.0[n].0 { return false }
|
if *date != b.0[n].0 {
|
||||||
if *atomic_unit != b.0[n].1 { return false }
|
return false;
|
||||||
if *block != b.0[n].2 { return false }
|
}
|
||||||
|
if *atomic_unit != b.0[n].1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if *block != b.0[n].2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -151,14 +161,20 @@ impl PayoutOrd {
|
||||||
// Date
|
// Date
|
||||||
let date = match P2POOL_REGEX.date.find(line) {
|
let date = match P2POOL_REGEX.date.find(line) {
|
||||||
Some(date) => date.as_str().to_string(),
|
Some(date) => date.as_str().to_string(),
|
||||||
None => { error!("P2Pool | Date parse error: [{}]", line); "????-??-?? ??:??:??.????".to_string() },
|
None => {
|
||||||
|
error!("P2Pool | Date parse error: [{}]", line);
|
||||||
|
"????-??-?? ??:??:??.????".to_string()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// AtomicUnit
|
// AtomicUnit
|
||||||
let atomic_unit = if let Some(word) = P2POOL_REGEX.payout.find(line) {
|
let atomic_unit = if let Some(word) = P2POOL_REGEX.payout.find(line) {
|
||||||
if let Some(word) = P2POOL_REGEX.payout_float.find(word.as_str()) {
|
if let Some(word) = P2POOL_REGEX.payout_float.find(word.as_str()) {
|
||||||
match word.as_str().parse::<f64>() {
|
match word.as_str().parse::<f64>() {
|
||||||
Ok(au) => AtomicUnit::from_f64(au),
|
Ok(au) => AtomicUnit::from_f64(au),
|
||||||
Err(e) => { error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line); AtomicUnit::new() },
|
Err(e) => {
|
||||||
|
error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line);
|
||||||
|
AtomicUnit::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("P2Pool | AtomicUnit parse error: [{}]", line);
|
error!("P2Pool | AtomicUnit parse error: [{}]", line);
|
||||||
|
@ -173,7 +189,10 @@ impl PayoutOrd {
|
||||||
if let Some(word) = P2POOL_REGEX.block_int.find(word.as_str()) {
|
if let Some(word) = P2POOL_REGEX.block_int.find(word.as_str()) {
|
||||||
match word.as_str().parse::<u64>() {
|
match word.as_str().parse::<u64>() {
|
||||||
Ok(b) => HumanNumber::from_u64(b),
|
Ok(b) => HumanNumber::from_u64(b),
|
||||||
Err(e) => { error!("P2Pool | Block parse error: [{}] on [{}]", e, line); HumanNumber::unknown() },
|
Err(e) => {
|
||||||
|
error!("P2Pool | Block parse error: [{}] on [{}]", e, line);
|
||||||
|
HumanNumber::unknown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("P2Pool | Block parse error: [{}]", line);
|
error!("P2Pool | Block parse error: [{}]", line);
|
||||||
|
@ -191,13 +210,19 @@ impl PayoutOrd {
|
||||||
// Date
|
// Date
|
||||||
let date = match P2POOL_REGEX.date.find(line) {
|
let date = match P2POOL_REGEX.date.find(line) {
|
||||||
Some(date) => date.as_str().to_string(),
|
Some(date) => date.as_str().to_string(),
|
||||||
None => { error!("P2Pool | Date parse error: [{}]", line); "????-??-?? ??:??:??.????".to_string() },
|
None => {
|
||||||
|
error!("P2Pool | Date parse error: [{}]", line);
|
||||||
|
"????-??-?? ??:??:??.????".to_string()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// AtomicUnit
|
// AtomicUnit
|
||||||
let atomic_unit = if let Some(word) = P2POOL_REGEX.payout_float.find(line) {
|
let atomic_unit = if let Some(word) = P2POOL_REGEX.payout_float.find(line) {
|
||||||
match word.as_str().parse::<f64>() {
|
match word.as_str().parse::<f64>() {
|
||||||
Ok(au) => AtomicUnit::from_f64(au),
|
Ok(au) => AtomicUnit::from_f64(au),
|
||||||
Err(e) => { error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line); AtomicUnit::new() },
|
Err(e) => {
|
||||||
|
error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line);
|
||||||
|
AtomicUnit::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("P2Pool | AtomicUnit parse error: [{}]", line);
|
error!("P2Pool | AtomicUnit parse error: [{}]", line);
|
||||||
|
@ -206,7 +231,10 @@ impl PayoutOrd {
|
||||||
// Block
|
// Block
|
||||||
let block = match P2POOL_REGEX.block_comma.find(line) {
|
let block = match P2POOL_REGEX.block_comma.find(line) {
|
||||||
Some(b) => HumanNumber::from_str(b.as_str()),
|
Some(b) => HumanNumber::from_str(b.as_str()),
|
||||||
None => { error!("P2Pool | Block parse error: [{}]", line); HumanNumber::unknown() },
|
None => {
|
||||||
|
error!("P2Pool | Block parse error: [{}]", line);
|
||||||
|
HumanNumber::unknown()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
(date, atomic_unit, block)
|
(date, atomic_unit, block)
|
||||||
}
|
}
|
||||||
|
@ -222,7 +250,8 @@ impl PayoutOrd {
|
||||||
// Add 7 more bytes for wrapper type overhead and it's an even [70] bytes per line.
|
// Add 7 more bytes for wrapper type overhead and it's an even [70] bytes per line.
|
||||||
pub fn update_from_payout_log(&mut self, log: &str) {
|
pub fn update_from_payout_log(&mut self, log: &str) {
|
||||||
let amount_of_lines = log.lines().count();
|
let amount_of_lines = log.lines().count();
|
||||||
let mut vec: Vec<(String, AtomicUnit, HumanNumber)> = Vec::with_capacity(70 * amount_of_lines);
|
let mut vec: Vec<(String, AtomicUnit, HumanNumber)> =
|
||||||
|
Vec::with_capacity(70 * amount_of_lines);
|
||||||
for line in log.lines() {
|
for line in log.lines() {
|
||||||
debug!("PayoutOrd | Parsing line: [{}]", line);
|
debug!("PayoutOrd | Parsing line: [{}]", line);
|
||||||
vec.push(Self::parse_formatted_payout_line(line));
|
vec.push(Self::parse_formatted_payout_line(line));
|
||||||
|
@ -256,18 +285,20 @@ impl PayoutOrd {
|
||||||
// self.0 = The [Vec] within [PayoutOrd]
|
// self.0 = The [Vec] within [PayoutOrd]
|
||||||
// b.1.0 = [b] is [(String, AtomicUnit, HumanNumber)], [.1] is the [AtomicUnit] inside it, [.0] is the [u64] inside that
|
// b.1.0 = [b] is [(String, AtomicUnit, HumanNumber)], [.1] is the [AtomicUnit] inside it, [.0] is the [u64] inside that
|
||||||
// a.1.0 = Same deal, but we compare it with the previous value (b)
|
// a.1.0 = Same deal, but we compare it with the previous value (b)
|
||||||
self.0.sort_by(|a, b| b.1.0.cmp(&a.1.0));
|
self.0.sort_by(|a, b| b.1 .0.cmp(&a.1 .0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// These sorting functions take around [0.0035~] seconds on a Ryzen 5950x
|
// These sorting functions take around [0.0035~] seconds on a Ryzen 5950x
|
||||||
// given a Vec filled with 1_000_000 elements, not bad.
|
// given a Vec filled with 1_000_000 elements, not bad.
|
||||||
pub fn sort_payout_low_to_high(&mut self) {
|
pub fn sort_payout_low_to_high(&mut self) {
|
||||||
self.0.sort_by(|a, b| a.1.0.cmp(&b.1.0));
|
self.0.sort_by(|a, b| a.1 .0.cmp(&b.1 .0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a reversed [Iter] of the [PayoutOrd]
|
// Returns a reversed [Iter] of the [PayoutOrd]
|
||||||
// This is obviously faster than actually reordering the Vec.
|
// This is obviously faster than actually reordering the Vec.
|
||||||
pub fn rev_iter(&self) -> std::iter::Rev<std::slice::Iter<'_, (String, AtomicUnit, HumanNumber)>> {
|
pub fn rev_iter(
|
||||||
|
&self,
|
||||||
|
) -> std::iter::Rev<std::slice::Iter<'_, (String, AtomicUnit, HumanNumber)>> {
|
||||||
self.0.iter().rev()
|
self.0.iter().rev()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +306,11 @@ impl PayoutOrd {
|
||||||
// The raw log lines will be shown instead of this struct.
|
// The raw log lines will be shown instead of this struct.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PayoutOrd { fn default() -> Self { Self::new() } }
|
impl Default for PayoutOrd {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for PayoutOrd {
|
impl std::fmt::Display for PayoutOrd {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
@ -292,8 +327,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn update_p2pool_payout_log() {
|
fn update_p2pool_payout_log() {
|
||||||
use crate::xmr::PayoutOrd;
|
use crate::xmr::PayoutOrd;
|
||||||
let log =
|
let log = r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
|
||||||
r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
|
|
||||||
2021-12-21 02:01:01.1111 | 0.002000000000 XMR | Block 2,345,678
|
2021-12-21 02:01:01.1111 | 0.002000000000 XMR | Block 2,345,678
|
||||||
2021-12-21 03:01:01.1111 | 0.003000000000 XMR | Block 3,456,789
|
2021-12-21 03:01:01.1111 | 0.003000000000 XMR | Block 3,456,789
|
||||||
"#;
|
"#;
|
||||||
|
@ -306,9 +340,9 @@ r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn push_to_payout_ord() {
|
fn push_to_payout_ord() {
|
||||||
use crate::xmr::PayoutOrd;
|
|
||||||
use crate::xmr::AtomicUnit;
|
|
||||||
use crate::human::HumanNumber;
|
use crate::human::HumanNumber;
|
||||||
|
use crate::xmr::AtomicUnit;
|
||||||
|
use crate::xmr::PayoutOrd;
|
||||||
let mut payout_ord = PayoutOrd::from_vec(vec![]);
|
let mut payout_ord = PayoutOrd::from_vec(vec![]);
|
||||||
let should_be = "2022-09-08 18:42:55.4636 | 0.000000000001 XMR | Block 2,654,321\n";
|
let should_be = "2022-09-08 18:42:55.4636 | 0.000000000001 XMR | Block 2,654,321\n";
|
||||||
println!("BEFORE: {:#?}", payout_ord);
|
println!("BEFORE: {:#?}", payout_ord);
|
||||||
|
@ -320,13 +354,25 @@ r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sum_payout_ord_atomic_unit() {
|
fn sum_payout_ord_atomic_unit() {
|
||||||
use crate::xmr::PayoutOrd;
|
|
||||||
use crate::xmr::AtomicUnit;
|
|
||||||
use crate::human::HumanNumber;
|
use crate::human::HumanNumber;
|
||||||
|
use crate::xmr::AtomicUnit;
|
||||||
|
use crate::xmr::PayoutOrd;
|
||||||
let mut payout_ord = PayoutOrd::from_vec(vec![
|
let mut payout_ord = PayoutOrd::from_vec(vec![
|
||||||
("2022-09-08 18:42:55.4636".to_string(), AtomicUnit::from_u64(1), HumanNumber::from_u64(2654321)),
|
(
|
||||||
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(1), HumanNumber::from_u64(2654322)),
|
"2022-09-08 18:42:55.4636".to_string(),
|
||||||
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(1), HumanNumber::from_u64(2654323)),
|
AtomicUnit::from_u64(1),
|
||||||
|
HumanNumber::from_u64(2654321),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-09 16:18:26.7582".to_string(),
|
||||||
|
AtomicUnit::from_u64(1),
|
||||||
|
HumanNumber::from_u64(2654322),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-10 11:15:21.1272".to_string(),
|
||||||
|
AtomicUnit::from_u64(1),
|
||||||
|
HumanNumber::from_u64(2654323),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
println!("OG: {:#?}", payout_ord);
|
println!("OG: {:#?}", payout_ord);
|
||||||
let sum = PayoutOrd::atomic_unit_sum(&payout_ord);
|
let sum = PayoutOrd::atomic_unit_sum(&payout_ord);
|
||||||
|
@ -336,21 +382,32 @@ r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sort_p2pool_payout_ord() {
|
fn sort_p2pool_payout_ord() {
|
||||||
use crate::xmr::PayoutOrd;
|
|
||||||
use crate::xmr::AtomicUnit;
|
|
||||||
use crate::human::HumanNumber;
|
use crate::human::HumanNumber;
|
||||||
|
use crate::xmr::AtomicUnit;
|
||||||
|
use crate::xmr::PayoutOrd;
|
||||||
let mut payout_ord = PayoutOrd::from_vec(vec![
|
let mut payout_ord = PayoutOrd::from_vec(vec![
|
||||||
("2022-09-08 18:42:55.4636".to_string(), AtomicUnit::from_u64(1000000000), HumanNumber::from_u64(2654321)),
|
(
|
||||||
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(2000000000), HumanNumber::from_u64(2654322)),
|
"2022-09-08 18:42:55.4636".to_string(),
|
||||||
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(3000000000), HumanNumber::from_u64(2654323)),
|
AtomicUnit::from_u64(1000000000),
|
||||||
|
HumanNumber::from_u64(2654321),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-09 16:18:26.7582".to_string(),
|
||||||
|
AtomicUnit::from_u64(2000000000),
|
||||||
|
HumanNumber::from_u64(2654322),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-10 11:15:21.1272".to_string(),
|
||||||
|
AtomicUnit::from_u64(3000000000),
|
||||||
|
HumanNumber::from_u64(2654323),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
println!("OG: {:#?}", payout_ord);
|
println!("OG: {:#?}", payout_ord);
|
||||||
|
|
||||||
// High to Low
|
// High to Low
|
||||||
PayoutOrd::sort_payout_high_to_low(&mut payout_ord);
|
PayoutOrd::sort_payout_high_to_low(&mut payout_ord);
|
||||||
println!("AFTER PAYOUT HIGH TO LOW: {:#?}", payout_ord);
|
println!("AFTER PAYOUT HIGH TO LOW: {:#?}", payout_ord);
|
||||||
let should_be =
|
let should_be = r#"2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
||||||
r#"2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
|
||||||
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
||||||
2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||||
"#;
|
"#;
|
||||||
|
@ -361,8 +418,7 @@ r#"2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
||||||
// Low to High
|
// Low to High
|
||||||
PayoutOrd::sort_payout_low_to_high(&mut payout_ord);
|
PayoutOrd::sort_payout_low_to_high(&mut payout_ord);
|
||||||
println!("AFTER PAYOUT LOW TO HIGH: {:#?}", payout_ord);
|
println!("AFTER PAYOUT LOW TO HIGH: {:#?}", payout_ord);
|
||||||
let should_be =
|
let should_be = r#"2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||||
r#"2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
|
||||||
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
2022-09-09 16:18:26.7582 | 0.002000000000 XMR | Block 2,654,322
|
||||||
2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
2022-09-10 11:15:21.1272 | 0.003000000000 XMR | Block 2,654,323
|
||||||
"#;
|
"#;
|
||||||
|
@ -373,13 +429,25 @@ r#"2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn payout_ord_is_same() {
|
fn payout_ord_is_same() {
|
||||||
use crate::xmr::PayoutOrd;
|
|
||||||
use crate::xmr::AtomicUnit;
|
|
||||||
use crate::human::HumanNumber;
|
use crate::human::HumanNumber;
|
||||||
|
use crate::xmr::AtomicUnit;
|
||||||
|
use crate::xmr::PayoutOrd;
|
||||||
let mut payout_ord = PayoutOrd::from_vec(vec![
|
let mut payout_ord = PayoutOrd::from_vec(vec![
|
||||||
("2022-09-08 18:42:55.4636".to_string(), AtomicUnit::from_u64(1000000000), HumanNumber::from_u64(2654321)),
|
(
|
||||||
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(2000000000), HumanNumber::from_u64(2654322)),
|
"2022-09-08 18:42:55.4636".to_string(),
|
||||||
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(3000000000), HumanNumber::from_u64(2654323)),
|
AtomicUnit::from_u64(1000000000),
|
||||||
|
HumanNumber::from_u64(2654321),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-09 16:18:26.7582".to_string(),
|
||||||
|
AtomicUnit::from_u64(2000000000),
|
||||||
|
HumanNumber::from_u64(2654322),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-10 11:15:21.1272".to_string(),
|
||||||
|
AtomicUnit::from_u64(3000000000),
|
||||||
|
HumanNumber::from_u64(2654323),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
let payout_ord_2 = payout_ord.clone();
|
let payout_ord_2 = payout_ord.clone();
|
||||||
println!("1: {:#?}", payout_ord);
|
println!("1: {:#?}", payout_ord);
|
||||||
|
@ -394,20 +462,32 @@ r#"2022-09-08 18:42:55.4636 | 0.001000000000 XMR | Block 2,654,321
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn view_reverse_payout_ord() {
|
fn view_reverse_payout_ord() {
|
||||||
use crate::xmr::PayoutOrd;
|
|
||||||
use crate::xmr::AtomicUnit;
|
|
||||||
use crate::human::HumanNumber;
|
use crate::human::HumanNumber;
|
||||||
|
use crate::xmr::AtomicUnit;
|
||||||
|
use crate::xmr::PayoutOrd;
|
||||||
let mut payout_ord = PayoutOrd::from_vec(vec![
|
let mut payout_ord = PayoutOrd::from_vec(vec![
|
||||||
("2022-09-08 18:42:55.4636".to_string(), AtomicUnit::from_u64(1000000000), HumanNumber::from_u64(2654321)),
|
(
|
||||||
("2022-09-09 16:18:26.7582".to_string(), AtomicUnit::from_u64(2000000000), HumanNumber::from_u64(2654322)),
|
"2022-09-08 18:42:55.4636".to_string(),
|
||||||
("2022-09-10 11:15:21.1272".to_string(), AtomicUnit::from_u64(3000000000), HumanNumber::from_u64(2654323)),
|
AtomicUnit::from_u64(1000000000),
|
||||||
|
HumanNumber::from_u64(2654321),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-09 16:18:26.7582".to_string(),
|
||||||
|
AtomicUnit::from_u64(2000000000),
|
||||||
|
HumanNumber::from_u64(2654322),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"2022-09-10 11:15:21.1272".to_string(),
|
||||||
|
AtomicUnit::from_u64(3000000000),
|
||||||
|
HumanNumber::from_u64(2654323),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
println!("OG: {:#?}", payout_ord);
|
println!("OG: {:#?}", payout_ord);
|
||||||
|
|
||||||
#[allow(clippy::never_loop)]
|
#[allow(clippy::never_loop)]
|
||||||
for (_, atomic_unit, _) in payout_ord.rev_iter() {
|
for (_, atomic_unit, _) in payout_ord.rev_iter() {
|
||||||
if atomic_unit.to_u64() == 3000000000 {
|
if atomic_unit.to_u64() == 3000000000 {
|
||||||
break
|
break;
|
||||||
} else {
|
} else {
|
||||||
println!("expected: 3000000000, found: {}", atomic_unit);
|
println!("expected: 3000000000, found: {}", atomic_unit);
|
||||||
panic!("not reversed");
|
panic!("not reversed");
|
||||||
|
|
175
src/xmrig.rs
175
src/xmrig.rs
|
@ -15,30 +15,18 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{
|
use crate::regex::REGEXES;
|
||||||
Regexes,
|
use crate::{constants::*, disk::*, macros::*, Process, PubXmrigApi, Regexes};
|
||||||
constants::*,
|
|
||||||
disk::*,
|
|
||||||
Process,
|
|
||||||
PubXmrigApi,
|
|
||||||
macros::*,
|
|
||||||
};
|
|
||||||
use egui::{
|
use egui::{
|
||||||
TextEdit,SelectableLabel,ComboBox,Label,Button,RichText,Slider,Checkbox,
|
Button, Checkbox, ComboBox, Label, RichText, SelectableLabel, Slider, TextEdit, TextStyle::*,
|
||||||
TextStyle::*,
|
|
||||||
};
|
};
|
||||||
use std::{
|
|
||||||
sync::{Arc,Mutex},
|
|
||||||
};
|
|
||||||
use regex::Regex;
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use crate::regex::{
|
use regex::Regex;
|
||||||
REGEXES,
|
use std::sync::{Arc, Mutex};
|
||||||
};
|
|
||||||
|
|
||||||
impl crate::disk::Xmrig {
|
impl crate::disk::Xmrig {
|
||||||
#[inline(always)] // called once
|
#[inline(always)] // called once
|
||||||
pub fn show(
|
pub fn show(
|
||||||
&mut self,
|
&mut self,
|
||||||
pool_vec: &mut Vec<(String, Pool)>,
|
pool_vec: &mut Vec<(String, Pool)>,
|
||||||
process: &Arc<Mutex<Process>>,
|
process: &Arc<Mutex<Process>>,
|
||||||
|
@ -47,8 +35,8 @@ pub fn show(
|
||||||
width: f32,
|
width: f32,
|
||||||
height: f32,
|
height: f32,
|
||||||
_ctx: &egui::Context,
|
_ctx: &egui::Context,
|
||||||
ui: &mut egui::Ui
|
ui: &mut egui::Ui,
|
||||||
) {
|
) {
|
||||||
let text_edit = height / 25.0;
|
let text_edit = height / 25.0;
|
||||||
//---------------------------------------------------------------------------------------------------- [Simple] Console
|
//---------------------------------------------------------------------------------------------------- [Simple] Console
|
||||||
debug!("XMRig Tab | Rendering [Console]");
|
debug!("XMRig Tab | Rendering [Console]");
|
||||||
|
@ -58,8 +46,16 @@ pub fn show(
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||||
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
||||||
egui::ScrollArea::vertical().stick_to_bottom(true).max_width(width).max_height(height).auto_shrink([false; 2]).show_viewport(ui, |ui, _| {
|
egui::ScrollArea::vertical()
|
||||||
ui.add_sized([width, height], TextEdit::multiline(&mut lock!(api).output.as_str()));
|
.stick_to_bottom(true)
|
||||||
|
.max_width(width)
|
||||||
|
.max_height(height)
|
||||||
|
.auto_shrink([false; 2])
|
||||||
|
.show_viewport(ui, |ui, _| {
|
||||||
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
TextEdit::multiline(&mut lock!(api).output.as_str()),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
//---------------------------------------------------------------------------------------------------- [Advanced] Console
|
||||||
|
@ -68,18 +64,36 @@ pub fn show(
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
|
||||||
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
|
||||||
egui::ScrollArea::vertical().stick_to_bottom(true).max_width(width).max_height(height).auto_shrink([false; 2]).show_viewport(ui, |ui, _| {
|
egui::ScrollArea::vertical()
|
||||||
ui.add_sized([width, height], TextEdit::multiline(&mut lock!(api).output.as_str()));
|
.stick_to_bottom(true)
|
||||||
|
.max_width(width)
|
||||||
|
.max_height(height)
|
||||||
|
.auto_shrink([false; 2])
|
||||||
|
.show_viewport(ui, |ui, _| {
|
||||||
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
TextEdit::multiline(&mut lock!(api).output.as_str()),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.separator();
|
ui.separator();
|
||||||
let response = ui.add_sized([width, text_edit], TextEdit::hint_text(TextEdit::singleline(buffer), r#"Commands: [h]ashrate, [p]ause, [r]esume, re[s]ults, [c]onnection"#)).on_hover_text(XMRIG_INPUT);
|
let response = ui
|
||||||
|
.add_sized(
|
||||||
|
[width, text_edit],
|
||||||
|
TextEdit::hint_text(
|
||||||
|
TextEdit::singleline(buffer),
|
||||||
|
r#"Commands: [h]ashrate, [p]ause, [r]esume, re[s]ults, [c]onnection"#,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_INPUT);
|
||||||
// If the user pressed enter, dump buffer contents into the process STDIN
|
// If the user pressed enter, dump buffer contents into the process STDIN
|
||||||
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||||
response.request_focus(); // Get focus back
|
response.request_focus(); // Get focus back
|
||||||
let buffer = std::mem::take(buffer); // Take buffer
|
let buffer = std::mem::take(buffer); // Take buffer
|
||||||
let mut process = lock!(process); // Lock
|
let mut process = lock!(process); // Lock
|
||||||
if process.is_alive() { process.input.push(buffer); } // Push only if alive
|
if process.is_alive() {
|
||||||
|
process.input.push(buffer);
|
||||||
|
} // Push only if alive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -87,18 +101,27 @@ pub fn show(
|
||||||
//---------------------------------------------------------------------------------------------------- Arguments
|
//---------------------------------------------------------------------------------------------------- Arguments
|
||||||
if !self.simple {
|
if !self.simple {
|
||||||
debug!("XMRig Tab | Rendering [Arguments]");
|
debug!("XMRig Tab | Rendering [Arguments]");
|
||||||
ui.group(|ui| { ui.horizontal(|ui| {
|
ui.group(|ui| {
|
||||||
let width = (width/10.0) - SPACE;
|
ui.horizontal(|ui| {
|
||||||
|
let width = (width / 10.0) - SPACE;
|
||||||
ui.add_sized([width, text_edit], Label::new("Command arguments:"));
|
ui.add_sized([width, text_edit], Label::new("Command arguments:"));
|
||||||
ui.add_sized([ui.available_width(), text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.arguments), r#"--url <...> --user <...> --config <...>"#)).on_hover_text(XMRIG_ARGUMENTS);
|
ui.add_sized(
|
||||||
|
[ui.available_width(), text_edit],
|
||||||
|
TextEdit::hint_text(
|
||||||
|
TextEdit::singleline(&mut self.arguments),
|
||||||
|
r#"--url <...> --user <...> --config <...>"#,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_ARGUMENTS);
|
||||||
self.arguments.truncate(1024);
|
self.arguments.truncate(1024);
|
||||||
})});
|
})
|
||||||
|
});
|
||||||
ui.set_enabled(self.arguments.is_empty());
|
ui.set_enabled(self.arguments.is_empty());
|
||||||
//---------------------------------------------------------------------------------------------------- Address
|
//---------------------------------------------------------------------------------------------------- Address
|
||||||
debug!("XMRig Tab | Rendering [Address]");
|
debug!("XMRig Tab | Rendering [Address]");
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
let width = width - SPACE;
|
let width = width - SPACE;
|
||||||
ui.spacing_mut().text_edit_width = (width)-(SPACE*3.0);
|
ui.spacing_mut().text_edit_width = (width) - (SPACE * 3.0);
|
||||||
let text;
|
let text;
|
||||||
let color;
|
let color;
|
||||||
let len = format!("{:02}", self.address.len());
|
let len = format!("{:02}", self.address.len());
|
||||||
|
@ -112,14 +135,23 @@ pub fn show(
|
||||||
text = format!("Monero Address [{}/95] ❌", len);
|
text = format!("Monero Address [{}/95] ❌", len);
|
||||||
color = RED;
|
color = RED;
|
||||||
}
|
}
|
||||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
ui.add_sized(
|
||||||
ui.add_sized([width, text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4...")).on_hover_text(XMRIG_ADDRESS);
|
[width, text_edit],
|
||||||
|
Label::new(RichText::new(text).color(color)),
|
||||||
|
);
|
||||||
|
ui.add_sized(
|
||||||
|
[width, text_edit],
|
||||||
|
TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4..."),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_ADDRESS);
|
||||||
self.address.truncate(95);
|
self.address.truncate(95);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Threads
|
//---------------------------------------------------------------------------------------------------- Threads
|
||||||
if self.simple { ui.add_space(SPACE); }
|
if self.simple {
|
||||||
|
ui.add_space(SPACE);
|
||||||
|
}
|
||||||
debug!("XMRig Tab | Rendering [Threads]");
|
debug!("XMRig Tab | Rendering [Threads]");
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
let width = width / 10.0;
|
let width = width / 10.0;
|
||||||
|
@ -127,13 +159,24 @@ pub fn show(
|
||||||
ui.spacing_mut().slider_width = width * 6.5;
|
ui.spacing_mut().slider_width = width * 6.5;
|
||||||
ui.spacing_mut().icon_width = width / 25.0;
|
ui.spacing_mut().icon_width = width / 25.0;
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_sized([text_width, text_edit], Label::new(format!("Threads [1-{}]:", self.max_threads)));
|
ui.add_sized(
|
||||||
ui.add_sized([width, text_edit], Slider::new(&mut self.current_threads, 1..=self.max_threads)).on_hover_text(XMRIG_THREADS);
|
[text_width, text_edit],
|
||||||
|
Label::new(format!("Threads [1-{}]:", self.max_threads)),
|
||||||
|
);
|
||||||
|
ui.add_sized(
|
||||||
|
[width, text_edit],
|
||||||
|
Slider::new(&mut self.current_threads, 1..=self.max_threads),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_THREADS);
|
||||||
});
|
});
|
||||||
#[cfg(not(target_os = "linux"))] // Pause on active isn't supported on Linux
|
#[cfg(not(target_os = "linux"))] // Pause on active isn't supported on Linux
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_sized([text_width, text_edit], Label::new(format!("Pause on active [0-255]:")));
|
ui.add_sized(
|
||||||
ui.add_sized([width, text_edit], Slider::new(&mut self.pause, 0..=255)).on_hover_text(format!("{} [{}] seconds.", XMRIG_PAUSE, self.pause));
|
[text_width, text_edit],
|
||||||
|
Label::new(format!("Pause on active [0-255]:")),
|
||||||
|
);
|
||||||
|
ui.add_sized([width, text_edit], Slider::new(&mut self.pause, 0..=255))
|
||||||
|
.on_hover_text(format!("{} [{}] seconds.", XMRIG_PAUSE, self.pause));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -357,10 +400,11 @@ pub fn show(
|
||||||
|
|
||||||
debug!("XMRig Tab | Rendering [API] TextEdits");
|
debug!("XMRig Tab | Rendering [API] TextEdits");
|
||||||
// [HTTP API IP/Port]
|
// [HTTP API IP/Port]
|
||||||
ui.group(|ui| { ui.horizontal(|ui| {
|
ui.group(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
let width = width/10.0;
|
let width = width / 10.0;
|
||||||
ui.spacing_mut().text_edit_width = width*2.39;
|
ui.spacing_mut().text_edit_width = width * 2.39;
|
||||||
// HTTP API
|
// HTTP API
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let text;
|
let text;
|
||||||
|
@ -370,7 +414,10 @@ pub fn show(
|
||||||
text = format!("HTTP API IP [{}/255]➖", len);
|
text = format!("HTTP API IP [{}/255]➖", len);
|
||||||
color = LIGHT_GRAY;
|
color = LIGHT_GRAY;
|
||||||
incorrect_input = true;
|
incorrect_input = true;
|
||||||
} else if self.api_ip == "localhost" || REGEXES.ipv4.is_match(&self.api_ip) || REGEXES.domain.is_match(&self.api_ip) {
|
} else if self.api_ip == "localhost"
|
||||||
|
|| REGEXES.ipv4.is_match(&self.api_ip)
|
||||||
|
|| REGEXES.domain.is_match(&self.api_ip)
|
||||||
|
{
|
||||||
text = format!("HTTP API IP [{}/255]✔", len);
|
text = format!("HTTP API IP [{}/255]✔", len);
|
||||||
color = GREEN;
|
color = GREEN;
|
||||||
} else {
|
} else {
|
||||||
|
@ -378,8 +425,12 @@ pub fn show(
|
||||||
color = RED;
|
color = RED;
|
||||||
incorrect_input = true;
|
incorrect_input = true;
|
||||||
}
|
}
|
||||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
ui.add_sized(
|
||||||
ui.text_edit_singleline(&mut self.api_ip).on_hover_text(XMRIG_API_IP);
|
[width, text_edit],
|
||||||
|
Label::new(RichText::new(text).color(color)),
|
||||||
|
);
|
||||||
|
ui.text_edit_singleline(&mut self.api_ip)
|
||||||
|
.on_hover_text(XMRIG_API_IP);
|
||||||
self.api_ip.truncate(255);
|
self.api_ip.truncate(255);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
@ -398,8 +449,12 @@ pub fn show(
|
||||||
color = RED;
|
color = RED;
|
||||||
incorrect_input = true;
|
incorrect_input = true;
|
||||||
}
|
}
|
||||||
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
|
ui.add_sized(
|
||||||
ui.text_edit_singleline(&mut self.api_port).on_hover_text(XMRIG_API_PORT);
|
[width, text_edit],
|
||||||
|
Label::new(RichText::new(text).color(color)),
|
||||||
|
);
|
||||||
|
ui.text_edit_singleline(&mut self.api_port)
|
||||||
|
.on_hover_text(XMRIG_API_PORT);
|
||||||
self.api_port.truncate(5);
|
self.api_port.truncate(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -410,16 +465,24 @@ pub fn show(
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
// TLS/Keepalive
|
// TLS/Keepalive
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let width = (ui.available_width()/2.0)-11.0;
|
let width = (ui.available_width() / 2.0) - 11.0;
|
||||||
let height = text_edit*2.0;
|
let height = text_edit * 2.0;
|
||||||
// let mut style = (*ctx.style()).clone();
|
// let mut style = (*ctx.style()).clone();
|
||||||
// style.spacing.icon_width_inner = width / 8.0;
|
// style.spacing.icon_width_inner = width / 8.0;
|
||||||
// style.spacing.icon_width = width / 6.0;
|
// style.spacing.icon_width = width / 6.0;
|
||||||
// style.spacing.icon_spacing = 20.0;
|
// style.spacing.icon_spacing = 20.0;
|
||||||
// ctx.set_style(style);
|
// ctx.set_style(style);
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.tls, "TLS Connection")).on_hover_text(XMRIG_TLS);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.tls, "TLS Connection"),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_TLS);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add_sized([width, height], Checkbox::new(&mut self.keepalive, "Keepalive")).on_hover_text(XMRIG_KEEPALIVE);
|
ui.add_sized(
|
||||||
|
[width, height],
|
||||||
|
Checkbox::new(&mut self.keepalive, "Keepalive"),
|
||||||
|
)
|
||||||
|
.on_hover_text(XMRIG_KEEPALIVE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue