feat: merge new release

This commit is contained in:
mostafaei2002 2024-07-19 14:20:42 +03:30
commit 12bb263076
47 changed files with 3450 additions and 1420 deletions

View file

@ -1,3 +1,30 @@
# v1.2.0
Stable release, bugfixes and new features.
## Changes
### UI
- new big feature: integrate Xmrig-Proxy: New Tab, new status column, new action buttons.
### Internal
- new big feature: integrate Xmrig-Proxy: interact with XMRig and XvB processes.
- improve algorithm debugging
- add more CI checks
- update tests
### Doc
- Update [DIFFERENCES](https://github.com/Cyrix126/gupaxx/blob/main/DIFFERENCES.md) and add [NOTES_XMRIG-PROXY.md](https://github.com/Cyrix126/gupaxx/blob/main/NOTES_XMRIG-PROXY.md) to reflect updates hanges.
## Fixes
- fix https://github.com/Cyrix126/gupaxx/issues/8
- fix https://github.com/Cyrix126/gupaxx/issues/9
## Notes
### Do not use built in updates to upgrade to this version
This update bump the 1.x.0 number, which would mean breaking changes. However, it is only because updating previously from Gupaxx (in =<1.0.0) without manually downloading from github release would upgrade P2Pool and XMRig from upstream, which is a behaviour that has been modified in this release.
No configuration file change is needed, just update from github for this release.
## Bundled Versions
* [`P2Pool v3.10`](https://github.com/SChernykh/p2pool/releases/tag/v3.10)
* [`XMRig v6.21.1`](https://github.com/xmrig/xmrig/releases/tag/v6.21.1)
* [`XMRig-Proxy v6.21.1](https://github.com/xmrig/xmrig-proxy/releases/tag/v6.21.1)
# v1.1.2
## Changes
* [Remote Node](https://github.com/hinto-janai/gupax#remote-monero-nodes) changes:

32
Cargo.lock generated
View file

@ -1853,6 +1853,20 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "flexi_logger"
version = "0.28.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cca927478b3747ba47f98af6ba0ac0daea4f12d12f55e9104071b3dc00276310"
dependencies = [
"chrono",
"glob",
"log",
"nu-ansi-term",
"regex",
"thiserror",
]
[[package]]
name = "flume"
version = "0.11.0"
@ -2051,6 +2065,12 @@ dependencies = [
"xml-rs",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "glow"
version = "0.13.1"
@ -2182,7 +2202,7 @@ dependencies = [
[[package]]
name = "gupaxx"
version = "1.1.1"
version = "1.2.0"
dependencies = [
"anyhow",
"benri",
@ -2200,6 +2220,7 @@ dependencies = [
"env_logger",
"figment",
"flate2",
"flexi_logger",
"image 0.25.1",
"is_elevated",
"log",
@ -3072,6 +3093,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "num-bigint"
version = "0.4.5"

View file

@ -1,7 +1,7 @@
cargo-features = ["profile-rustflags"]
[package]
name = "gupaxx"
version = "1.1.1"
version = "1.2.0"
authors = ["cyrix126 <gupaxx@baermail.fr>"]
description = "Fork of Gupax integrating the XMRvsBeast Raffle "
documentation = "https://github.com/cyrix126/gupaxx"
@ -76,6 +76,7 @@ chrono = {version="0.4.38", default-features=false, features=["clock", "std"]}
enclose = "1.2.0"
bounded-vec-deque = {version="0.1.1", default-features=false}
cfg-if = "1.0"
flexi_logger = "0.28"
# Unix dependencies
[target.'cfg(unix)'.dependencies]
eframe = { version = "0.27.2", features = ["wgpu"] }

View file

@ -1,12 +1,18 @@
# Differences with upstream [Gupax](https://github.com/hinto-janai/gupax)
## Integration of the XvB Raffle
## Added functionalities
### Integration of the XvB Raffle
A new fancy tab to apply an algorithm of distribution of HR to xmrig (see [NOTES_ALGORITHM](NOTES_ALGORITHMS)) with your token from XvB.
This tab also includes a console output to let you track if everything is working and what are the decision of the algorithm, and show you personal stats from XvB.
A new column in Status Tab to see public stats from the raffle.
### Xmrig-Proxy
You can now point all your external miners and get all the juicy stats in Gupaxx. XvB algorithm is able to control Xmrig-Proxy when it is enabled.
## Removed functionality
Updates by tor. The version of the crate used was outdated, plagued with security concerns and bloated the binary.

View file

@ -26,6 +26,8 @@ If Gupaxx could register the user automaticcly to the raffle, it would remove a
It could be an option to ask at first start.
### Setup Guide
At first start, a guide could ask the user what it intends to do with Gupaxx (create node, create wallet, use xmrig-proxy, participate in raffle...) and do the setup for him and show him what it must do manually. An option to skip this guide would be present for advanced users.
### Very noob mode
At first start, a mode is suggested for very noob users. It would only shows the seed phrase of the wallet generated and configure all options for the user.
## Supporting more environnements
### Packaging
@ -34,6 +36,8 @@ Add repository/AUR for Gupaxx and a status of packaging distro/version on the RE
Add on README a table with minimum hardware/software requirements.
### Add more target
Gupaxx could add support for linux arm64 since p2pool and xmrig can compile on this target.
### Refactor size of text
Gupax/x currently resize texts/widgets based on the window size. Instead, the text/widget size should be decided by the OS/config, scroll bar should be used when there is not enough space. It will allow to use Gupaxx on different ratio of screen.
## More Powerful
### Optimization for xmrig
@ -42,10 +46,10 @@ On linux, we can activate 1GB pages after detecting cpu flags. We can also add c
#### Manual optimizations
On the XMRig tab, inform users about manual optimizations that Gupaxx can't control. For example, disabling hyper-threading in BIOS is recommended.
### CLI for Algorithm
For advanced users, a CLI could be made to use the algorithm without a GUI
It would allow the user to do automation and installation on headless environment and save a few HR from the Gupaxx process.
This CLI would still offer a way to the user to pass options to xmrig and to start his p2pool server or to use another one already running.
A simple script could be made to reproduce the algorithm who would take args for every other needed programs.
Gupaxx itself doesn't have any features that bundled programs don't offers.
#### Integrate XMRig-Proxy
[Done](https://github.com/Cyrix126/gupaxx/releases/tag/v1.2.0)
The algorithm of distribution of HR can't control HR outside of his instance.
It must estimate external HR, which can be approximative.
If a user control multiples miners, it could connect all of them to a xmrig-proxy instance.
@ -56,7 +60,12 @@ Right now, the algorithm estimate the eHR with the estimation made by the p2pool
The algorithm could instead watch the stats from the stratum server, which is exact but would take into account only miners which are pointed to it.
Miners using the cli xmrig could point their miners to the p2pool instance of Gupaxx, or have an identical option if they are using the CLI of the algorithm.
The algorithm would still check the estimation made by the p2pool instance of Gupaxx and warn the user if it seems there is too much difference between the data of the stratum server and the one of p2pool. It could prevent the user to forget to configure a miner to the stratum p2pool.
Could also be an option in advanced tab of XvB warning the user that he should point all his miners to the p2pool instance of Gupaxx to take them into account.
### Manually set HR for XvB algo
started by [Sina](https://github.com/mostafaei2002) [PR](https://github.com/Cyrix126/gupaxx/pull/11)
An advanced tab on XvB tab with multiple tools to set the HR manually.
The user can sometime better know the right decision from his HR than the algo that will take more time to get everything right, specially if resources are changing.
## Trust-less
## Builds

View file

@ -13,17 +13,16 @@
## Launch args
- XVB token
- XMR address
- p2pool address:port
- optional: hero
- optional: xmrig custom additional args
- optional: quiet algo
- optional: quiet xmrig
- optional: path of xmrig
- optional: path of p2pool or p2pool address:port
Example:
```
gupaxx --cli --token xxxxx --address xxxxx --hero --p2pool="127.0.0.1:3333" --xmrig-add-args="--xxx --xxx" -t 8 -q --path-xmrig="/path/to/xmrig-binary"
gupaxx --cli --token xxxxx --address xxxxx --hero --p2pool="127.0.0.1:3333" -t 8 -q --path-xmrig="/path/to/xmrig-binary"
```
## Commands
@ -48,3 +47,10 @@ You are not the winner
The cli args are managed by [clap](https://docs.rs/clap).
The code for managing current args from upstream will be replaced to use this crate.
The cli mode is enabled by passing the argument cli.
It will autostart xmrig/xvb processes.
p2pool process will be started if no address is given in args.
Otherwise, it will watch p2pool data and mine on it.
Each argument can be omitted if it's present in the state file.

20
NOTES_XMRIG-PROXY.md Normal file
View file

@ -0,0 +1,20 @@
# Integration of Xmrig-Proxy
## Objective
Allows a user to point his miners on the Gupaxx instance.
1/ to have the sum of the HR in his stats
2/ to let the algorithm of distribution of HR controls the HR of all the external miners.
## UI implementation
New Tab to start Xmrig-Proxy, interact with console output, give custom options, select a pool from the pool list.
New process column in Status Tab for Xmrig-Proxy.
## Technical implementation
xmrig-proxy will mine on p2pool instead of xmrig.
When xmrig-proxy is enabled, xmrig is automatically redirected to it instead of p2pool.
XvB algo will check if xmrig-proxy is enabled and watch/control his data instead of xmrig.

View file

@ -22,7 +22,7 @@ Gupax may not run on machines with:
* [XvB Tab](#xvb-tab)
- [Console](#console-of-xvb)
- [Token Input](#token-input)
- [Private stats](#private-stats)
- [Account stats](#account-stats)
* [XvB Raffle](#xvb-raffle-status)
* [Other changes](#other-changes)
* [License](#license)
@ -73,9 +73,9 @@ The output of console will show useful information on the status of the XvB proc
You need to put the token received when you registered your XMR payout address.
### Private stats
### Account stats
Private information about your address on XMRvsBeast can be found here after the process is started with your token provided.
Account stats about your address on XMRvsBeast can be found here after the process is started with your token provided.
## XvB Raffle Status
@ -89,11 +89,13 @@ This column will be active if the XvB process is started even partially, it does
## Other changes
This fork brings upgrades of dependence and some bugfixes about visual, performance and security that you can find in [DIFFERENCES](DIFFERENCES.md).
I will eventually (meaning when I'll have time) create pull requests for upstream about these differences.
~~I will eventually (meaning when I'll have time) create pull requests for upstream about these differences.~~
**Edit**:
There is no plan to upstream changes as the owner of Gupax said he won't have time to review the PR.
## Troubleshooting
If you have any issue, feel free to ask for support in the [xmrvsbeast matrix room](#xmrvsbeast:monero.social) or to open an issue in this repo. You can also contact me through [email](email:gupaxx@baermail.fr).
If you have any issue, feel free to ask for support in the [xmrvsbeast matrix room](#xmrvsbeast:monero.social) [![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#xmrvsbeast:monero.social) or to open an issue in this repo. You can also contact me through [email](mailto:gupaxx@baermail.fr).
### Windows

View file

@ -36,6 +36,12 @@ impl eframe::App for App {
let xmrig_is_waiting = xmrig.is_waiting();
let xmrig_state = xmrig.state;
drop(xmrig);
debug!("App | Locking and collecting XMRig-Proxy state...");
let xmrig_proxy = lock!(self.xmrig_proxy);
let xmrig_proxy_is_alive = xmrig_proxy.is_alive();
let xmrig_proxy_is_waiting = xmrig_proxy.is_waiting();
let xmrig_proxy_state = xmrig_proxy.state;
drop(xmrig_proxy);
debug!("App | Locking and collecting XvB state...");
let xvb = lock!(self.xvb);
let xvb_is_alive = xvb.is_alive();
@ -84,14 +90,17 @@ impl eframe::App for App {
ctx,
p2pool_state,
xmrig_state,
xmrig_proxy_state,
xvb_state,
&key,
wants_input,
p2pool_is_waiting,
xmrig_is_waiting,
xmrig_proxy_is_waiting,
xvb_is_waiting,
p2pool_is_alive,
xmrig_is_alive,
xmrig_proxy_is_alive,
xvb_is_alive,
);
// xvb_is_alive is not the same for bottom and for middle.
@ -104,6 +113,7 @@ impl eframe::App for App {
key,
p2pool_is_alive,
xmrig_is_alive,
xmrig_proxy_is_alive,
xvb_is_alive,
);
}

View file

@ -128,7 +128,8 @@ impl App {
Tab::Gupax => self.tab = Tab::Status,
Tab::P2pool => self.tab = Tab::Gupax,
Tab::Xmrig => self.tab = Tab::P2pool,
Tab::Xvb => self.tab = Tab::Xmrig,
Tab::XmrigProxy => self.tab = Tab::Xmrig,
Tab::Xvb => self.tab = Tab::XmrigProxy,
};
// Change Tabs RIGHT
} else if key.is_x() && !wants_input {
@ -137,7 +138,8 @@ impl App {
Tab::Status => self.tab = Tab::Gupax,
Tab::Gupax => self.tab = Tab::P2pool,
Tab::P2pool => self.tab = Tab::Xmrig,
Tab::Xmrig => self.tab = Tab::Xvb,
Tab::Xmrig => self.tab = Tab::XmrigProxy,
Tab::XmrigProxy => self.tab = Tab::Xvb,
Tab::Xvb => self.tab = Tab::About,
};
// Change Submenu LEFT

View file

@ -18,8 +18,9 @@ use crate::errors::ErrorFerris;
use crate::errors::ErrorState;
use crate::helper::p2pool::ImgP2pool;
use crate::helper::p2pool::PubP2poolApi;
use crate::helper::xmrig::ImgXmrig;
use crate::helper::xmrig::PubXmrigApi;
use crate::helper::xrig::xmrig::ImgXmrig;
use crate::helper::xrig::xmrig::PubXmrigApi;
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
use crate::helper::xvb::PubXvbApi;
use crate::helper::Helper;
use crate::helper::Process;
@ -104,15 +105,18 @@ pub struct App {
pub pub_sys: Arc<Mutex<Sys>>, // [Sys] state, read by [Status], mutated by [Helper]
pub p2pool: Arc<Mutex<Process>>, // [P2Pool] process state
pub xmrig: Arc<Mutex<Process>>, // [XMRig] process state
pub xmrig_proxy: Arc<Mutex<Process>>, // [XMRig-Proxy] process state
pub xvb: Arc<Mutex<Process>>, // [Xvb] process state
pub p2pool_api: Arc<Mutex<PubP2poolApi>>, // Public ready-to-print P2Pool API made by the "helper" thread
pub xmrig_api: Arc<Mutex<PubXmrigApi>>, // Public ready-to-print XMRig API made by the "helper" thread
pub xvb_api: Arc<Mutex<PubXvbApi>>, // Public XvB API
pub p2pool_img: Arc<Mutex<ImgP2pool>>, // A one-time snapshot of what data P2Pool started with
pub xmrig_img: Arc<Mutex<ImgXmrig>>, // A one-time snapshot of what data XMRig started with
pub xmrig_proxy_api: Arc<Mutex<PubXmrigProxyApi>>, // Public ready-to-print XMRigProxy API made by the "helper" thread
pub xvb_api: Arc<Mutex<PubXvbApi>>, // Public XvB API
pub p2pool_img: Arc<Mutex<ImgP2pool>>, // A one-time snapshot of what data P2Pool started with
pub xmrig_img: Arc<Mutex<ImgXmrig>>, // A one-time snapshot of what data XMRig started with
// STDIN Buffer
pub p2pool_stdin: String, // The buffer between the p2pool console and the [Helper]
pub xmrig_stdin: String, // The buffer between the xmrig console and the [Helper]
pub xmrig_proxy_stdin: String, // The buffer between the xmrig-proxy console and the [Helper]
// Sudo State
pub sudo: Arc<Mutex<SudoState>>, // This is just a dummy struct on [Windows].
// State from [--flags]
@ -184,6 +188,11 @@ impl App {
String::new(),
PathBuf::new()
));
let xmrig_proxy = arc_mut!(Process::new(
ProcessName::Xmrig,
String::new(),
PathBuf::new()
));
let xvb = arc_mut!(Process::new(
ProcessName::Xvb,
String::new(),
@ -191,6 +200,7 @@ impl App {
));
let p2pool_api = arc_mut!(PubP2poolApi::new());
let xmrig_api = arc_mut!(PubXmrigApi::new());
let xmrig_proxy_api = arc_mut!(PubXmrigProxyApi::new());
let xvb_api = arc_mut!(PubXvbApi::new());
let p2pool_img = arc_mut!(ImgP2pool::new());
let xmrig_img = arc_mut!(ImgXmrig::new());
@ -232,7 +242,12 @@ impl App {
must_resize: false,
og: arc_mut!(State::new()),
state: State::new(),
update: arc_mut!(Update::new(String::new(), PathBuf::new(), PathBuf::new(),)),
update: arc_mut!(Update::new(
String::new(),
PathBuf::new(),
PathBuf::new(),
PathBuf::new()
)),
file_window: FileWindow::new(),
og_node_vec: Node::new_vec(),
node_vec: Node::new_vec(),
@ -246,24 +261,29 @@ impl App {
pub_sys.clone(),
p2pool.clone(),
xmrig.clone(),
xmrig_proxy.clone(),
xvb.clone(),
p2pool_api.clone(),
xmrig_api.clone(),
xvb_api.clone(),
xmrig_proxy_api.clone(),
p2pool_img.clone(),
xmrig_img.clone(),
arc_mut!(GupaxP2poolApi::new())
)),
p2pool,
xmrig,
xmrig_proxy,
xvb,
p2pool_api,
xvb_api,
xmrig_api,
xmrig_proxy_api,
p2pool_img,
xmrig_img,
p2pool_stdin: String::with_capacity(10),
xmrig_stdin: String::with_capacity(10),
xmrig_proxy_stdin: String::with_capacity(10),
sudo: arc_mut!(SudoState::new()),
resizing: false,
alpha: 0,
@ -508,13 +528,38 @@ impl App {
app.state.xmrig.selected_name = name;
app.state.xmrig.selected_ip = pool.ip;
app.state.xmrig.selected_port = pool.port;
if og.xmrig_proxy.selected_index > app.og_pool_vec.len() {
warn!(
"App | Overflowing manual pool index [{} > {}], resetting to 1",
og.xmrig_proxy.selected_index,
app.og_pool_vec.len()
);
let (name, pool) = match app.og_pool_vec.first() {
Some(zero) => zero.clone(),
None => Pool::new_tuple(),
};
og.xmrig_proxy.selected_index = 0;
og.xmrig_proxy.selected_name.clone_from(&name);
og.xmrig_proxy.selected_ip.clone_from(&pool.ip);
og.xmrig_proxy.selected_port.clone_from(&pool.port);
app.state.xmrig_proxy.selected_index = 0;
app.state.xmrig_proxy.selected_name = name;
app.state.xmrig_proxy.selected_ip = pool.ip;
app.state.xmrig_proxy.selected_port = pool.port;
}
}
// Apply TOML values to [Update]
info!("App Init | Applying TOML values to [Update]...");
let p2pool_path = og.gupax.absolute_p2pool_path.clone();
let xmrig_path = og.gupax.absolute_xmrig_path.clone();
app.update = arc_mut!(Update::new(app.exe.clone(), p2pool_path, xmrig_path));
let xmrig_proxy_path = og.gupax.absolute_xp_path.clone();
app.update = arc_mut!(Update::new(
app.exe.clone(),
p2pool_path,
xmrig_path,
xmrig_proxy_path
));
// Set state version as compiled in version
info!("App Init | Setting state Gupax version...");
@ -642,6 +687,7 @@ pub enum Tab {
Gupax,
P2pool,
Xmrig,
XmrigProxy,
Xvb,
}

View file

@ -23,14 +23,17 @@ impl crate::app::App {
ctx: &egui::Context,
p2pool_state: ProcessState,
xmrig_state: ProcessState,
xmrig_proxy_state: ProcessState,
xvb_state: ProcessState,
key: &KeyPressed,
wants_input: bool,
p2pool_is_waiting: bool,
xmrig_is_waiting: bool,
xmrig_proxy_is_waiting: bool,
xvb_is_waiting: bool,
p2pool_is_alive: bool,
xmrig_is_alive: bool,
xmrig_proxy_is_alive: bool,
xvb_is_alive: bool,
) {
// Bottom: app info + state/process buttons
@ -74,6 +77,8 @@ impl crate::app::App {
ui.separator();
status_xmrig(xmrig_state, ui, size);
ui.separator();
status_xp(xmrig_proxy_state, ui, size);
ui.separator();
status_xvb(xvb_state, ui, size);
});
@ -112,6 +117,17 @@ impl crate::app::App {
wants_input,
);
}
Tab::XmrigProxy => {
self.xmrig_proxy_submenu(ui, size);
self.xmrig_proxy_run_actions(
ui,
height,
xmrig_proxy_is_waiting,
xmrig_proxy_is_alive,
key,
wants_input,
);
}
Tab::Xvb => {
self.xvb_submenu(ui, size);
self.xvb_run_actions(
@ -121,7 +137,7 @@ impl crate::app::App {
xvb_is_alive,
key,
wants_input,
);
)
}
Tab::About => {}
}
@ -145,6 +161,7 @@ impl crate::app::App {
self.state.gupax = og.gupax;
self.state.p2pool = og.p2pool;
self.state.xmrig = og.xmrig;
self.state.xmrig_proxy = og.xmrig_proxy;
self.state.xvb = og.xvb;
self.node_vec.clone_from(&self.og_node_vec);
self.pool_vec.clone_from(&self.og_pool_vec);
@ -162,6 +179,7 @@ impl crate::app::App {
og.gupax = self.state.gupax.clone();
og.p2pool = self.state.p2pool.clone();
og.xmrig = self.state.xmrig.clone();
og.xmrig_proxy = self.state.xmrig_proxy.clone();
og.xvb = self.state.xvb.clone();
}
Err(e) => {
@ -408,6 +426,33 @@ impl crate::app::App {
}
});
}
fn xmrig_proxy_submenu(&mut self, ui: &mut Ui, size: Vec2) {
ui.group(|ui| {
let width = size.x / 1.5;
let size = vec2(width, size.y);
if ui
.add_sized(
size,
SelectableLabel::new(!self.state.xmrig_proxy.simple, "Advanced"),
)
.on_hover_text(XMRIG_PROXY_ADVANCED)
.clicked()
{
self.state.xmrig_proxy.simple = false;
}
ui.separator();
if ui
.add_sized(
size,
SelectableLabel::new(self.state.xmrig_proxy.simple, "Simple"),
)
.on_hover_text(XMRIG_PROXY_SIMPLE)
.clicked()
{
self.state.xmrig_proxy.simple = true;
}
});
}
fn xmrig_run_actions(
&mut self,
ui: &mut Ui,
@ -566,6 +611,7 @@ impl crate::app::App {
&self.state.xvb,
&self.state.p2pool,
&self.state.xmrig,
&self.state.xmrig_proxy,
);
}
if key.is_down() && !wants_input
@ -605,6 +651,97 @@ impl crate::app::App {
&self.state.xvb,
&self.state.p2pool,
&self.state.xmrig,
&self.state.xmrig_proxy,
);
}
}
});
}
fn xmrig_proxy_run_actions(
&mut self,
ui: &mut Ui,
height: f32,
xmrig_proxy_is_waiting: bool,
xmrig_proxy_is_alive: bool,
key: &KeyPressed,
wants_input: bool,
) {
ui.group(|ui| {
let width = (ui.available_width() / 3.0) - 5.0;
let size = vec2(width, height);
if xmrig_proxy_is_waiting {
ui.add_enabled_ui(false, |ui| {
ui.add_sized(size, Button::new(""))
.on_disabled_hover_text(XMRIG_PROXY_MIDDLE);
ui.add_sized(size, Button::new(""))
.on_disabled_hover_text(XMRIG_PROXY_MIDDLE);
ui.add_sized(size, Button::new(""))
.on_disabled_hover_text(XMRIG_PROXY_MIDDLE);
});
} else if xmrig_proxy_is_alive {
if key.is_up() && !wants_input
|| ui
.add_sized(size, Button::new(""))
.on_hover_text("Restart XMRig-Proxy")
.clicked()
{
let _ = lock!(self.og).update_absolute_path();
let _ = self.state.update_absolute_path();
Helper::restart_xp(
&self.helper,
&self.state.xmrig_proxy,
&self.state.xmrig,
&self.state.gupax.absolute_xp_path,
);
}
if key.is_down() && !wants_input
|| ui
.add_sized(size, Button::new(""))
.on_hover_text("Stop XMRig-Proxy")
.clicked()
{
Helper::stop_xp(&self.helper);
}
ui.add_enabled_ui(false, |ui| {
ui.add_sized(size, Button::new(""))
.on_disabled_hover_text("Start XMRig-Proxy");
});
} else {
ui.add_enabled_ui(false, |ui| {
ui.add_sized(size, Button::new(""))
.on_disabled_hover_text("Restart XMRig-Proxy");
ui.add_sized(size, Button::new(""))
.on_disabled_hover_text("Stop XMRig-Proxy");
});
let mut text = String::new();
let mut ui_enabled = true;
if !Gupax::path_is_file(&self.state.gupax.xmrig_proxy_path) {
ui_enabled = false;
text = format!("Error: {}", XMRIG_PROXY_PATH_NOT_FILE);
} else if !crate::components::update::check_xp_path(
&self.state.gupax.xmrig_proxy_path,
) {
ui_enabled = false;
text = format!("Error: {}", XMRIG_PROXY_PATH_NOT_VALID);
}
ui.set_enabled(ui_enabled);
let color = if ui_enabled { GREEN } else { RED };
if (ui_enabled && key.is_up() && !wants_input)
|| ui
.add_sized(size, Button::new(RichText::new("").color(color)))
.on_hover_text("Start XMRig-Proxy")
.on_disabled_hover_text(text)
.clicked()
{
let _ = lock!(self.og).update_absolute_path();
let _ = self.state.update_absolute_path();
Helper::start_xp(
&self.helper,
&self.state.xmrig_proxy,
&self.state.xmrig,
&self.state.gupax.absolute_xp_path,
);
}
}
@ -665,6 +802,33 @@ fn status_xmrig(state: ProcessState, ui: &mut Ui, size: Vec2) {
};
status(ui, color, hover_text, size, "XMRig ⏺");
}
fn status_xp(state: ProcessState, ui: &mut Ui, size: Vec2) {
let color;
let hover_text = match state {
Alive => {
color = GREEN;
XMRIG_PROXY_ALIVE
}
Dead => {
color = GRAY;
XMRIG_PROXY_DEAD
}
Failed => {
color = RED;
XMRIG_PROXY_FAILED
}
NotMining | OfflineNodesAll => {
color = ORANGE;
XMRIG_PROXY_NOT_MINING
}
Middle | Waiting | Syncing | Retry => {
color = YELLOW;
XMRIG_PROXY_MIDDLE
}
};
status(ui, color, hover_text, size, "Proxy ⏺");
}
fn status_xvb(state: ProcessState, ui: &mut Ui, size: Vec2) {
let color;
let hover_text = match state {

View file

@ -27,395 +27,443 @@ impl Gupax {
) {
// Update button + Progress bar
debug!("Gupaxx Tab | Rendering [Update] button + progress bar");
ui.group(|ui| {
let button = if self.simple {
size.y / 5.0
} else {
size.y / 15.0
};
let height = if self.simple {
size.y / 5.0
} else {
size.y / 10.0
};
let width = size.x - SPACE;
let updating = *lock2!(update, updating);
ui.vertical(|ui| {
// If [Gupax] is being built for a Linux distro,
// disable built-in updating completely.
#[cfg(feature = "distro")]
ui.set_enabled(false);
#[cfg(feature = "distro")]
ui.add_sized([width, button], Button::new("Updates are disabled"))
.on_disabled_hover_text(DISTRO_NO_UPDATE);
#[cfg(not(feature = "distro"))]
ui.set_enabled(!updating && *lock!(restart) == Restart::No);
#[cfg(not(feature = "distro"))]
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);
}
});
ui.vertical(|ui| {
ui.set_enabled(updating);
let prog = *lock2!(update, prog);
let msg = format!("{}\n{}{}", *lock2!(update, msg), prog, "%");
ui.add_sized([width, height * 1.4], Label::new(RichText::new(msg)));
let height = height / 2.0;
let size = vec2(width, height);
if updating {
ui.add_sized(size, Spinner::new().size(height));
} else {
ui.add_sized(size, Label::new("..."));
}
ui.add_sized(size, ProgressBar::new(lock2!(update, prog).round() / 100.0));
});
});
debug!("Gupaxx Tab | Rendering bool buttons");
ui.horizontal(|ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
ui.group(|ui| {
let width = (size.x - SPACE * 15.0) / 7.0;
let height = if self.simple {
size.y / 10.0
let button = if self.simple {
size.y / 5.0
} else {
size.y / 15.0
};
let size = vec2(width, height);
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
ui.add_sized(size, Checkbox::new(&mut self.auto_update, "Auto-Update"))
.on_hover_text(GUPAX_AUTO_UPDATE);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.bundled, "Bundle"))
.on_hover_text(GUPAX_BUNDLED_UPDATE);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool"))
.on_hover_text(GUPAX_AUTO_P2POOL);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig"))
.on_hover_text(GUPAX_AUTO_XMRIG);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.auto_xvb, "Auto-XvB"))
.on_hover_text(GUPAX_AUTO_XVB);
ui.separator();
ui.add_sized(
size,
Checkbox::new(&mut self.ask_before_quit, "Confirm quit"),
)
.on_hover_text(GUPAX_ASK_BEFORE_QUIT);
ui.separator();
ui.add_sized(
size,
Checkbox::new(&mut self.save_before_quit, "Save on quit"),
)
.on_hover_text(GUPAX_SAVE_BEFORE_QUIT);
});
});
if self.simple {
return;
}
debug!("Gupaxx Tab | Rendering P2Pool/XMRig path selection");
// P2Pool/XMRig binary path selection
let height = size.y / 28.0;
let text_edit = (ui.available_width() / 10.0) - SPACE;
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("Gupaxx is online");
ui.separator();
ui.horizontal(|ui| {
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);
} 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);
} else if !crate::components::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);
let height = if self.simple {
size.y / 5.0
} else {
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.set_enabled(!lock!(file_window).thread);
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
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.horizontal(|ui| {
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);
} 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);
} else if !crate::components::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);
} else {
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.set_enabled(!lock!(file_window).thread);
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
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);
});
});
let mut guard = lock!(file_window);
if guard.picked_p2pool {
self.p2pool_path.clone_from(&guard.p2pool_path);
guard.picked_p2pool = false;
}
if guard.picked_xmrig {
self.xmrig_path.clone_from(&guard.xmrig_path);
guard.picked_xmrig = false;
}
drop(guard);
let height = ui.available_height() / 6.0;
// Saved [Tab]
debug!("Gupaxx Tab | Rendering [Tab] selector");
ui.group(|ui| {
let width = (size.x / 6.0) - (SPACE * 1.93);
let size = vec2(width, height);
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.horizontal(|ui| {
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::About, "About"))
.on_hover_text(GUPAX_TAB_ABOUT)
.clicked()
{
self.tab = Tab::About;
}
ui.separator();
if ui
.add_sized(
size,
SelectableLabel::new(self.tab == Tab::Status, "Status"),
)
.on_hover_text(GUPAX_TAB_STATUS)
.clicked()
{
self.tab = Tab::Status;
}
ui.separator();
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::Gupax, "Gupaxx"))
.on_hover_text(GUPAX_TAB_GUPAX)
.clicked()
{
self.tab = Tab::Gupax;
}
ui.separator();
if ui
.add_sized(
size,
SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"),
)
.on_hover_text(GUPAX_TAB_P2POOL)
.clicked()
{
self.tab = Tab::P2pool;
}
ui.separator();
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig"))
.on_hover_text(GUPAX_TAB_XMRIG)
.clicked()
{
self.tab = Tab::Xmrig;
}
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::Xvb, "XvB"))
.on_hover_text(GUPAX_TAB_XVB)
.clicked()
{
self.tab = Tab::Xvb;
}
})
});
// Gupax App resolution sliders
debug!("Gupaxx Tab | Rendering resolution sliders");
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.separator();
ui.vertical(|ui| {
let width = size.x / 10.0;
ui.spacing_mut().icon_width = width / 25.0;
ui.spacing_mut().slider_width = width * 7.6;
match self.ratio {
Ratio::None => (),
Ratio::Width => {
let width = self.selected_width as f64;
let height = (width / 1.333).round();
self.selected_height = height as u16;
size.y / 10.0
};
let width = size.x - SPACE;
let updating = *lock2!(update, updating);
ui.vertical(|ui| {
// If [Gupax] is being built for a Linux distro,
// disable built-in updating completely.
#[cfg(feature = "distro")]
ui.set_enabled(false);
#[cfg(feature = "distro")]
ui.add_sized([width, button], Button::new("Updates are disabled"))
.on_disabled_hover_text(DISTRO_NO_UPDATE);
#[cfg(not(feature = "distro"))]
ui.set_enabled(!updating && *lock!(restart) == Restart::No);
#[cfg(not(feature = "distro"))]
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);
}
Ratio::Height => {
let height = self.selected_height as f64;
let width = (height * 1.333).round();
self.selected_width = width as u16;
});
ui.vertical(|ui| {
ui.set_enabled(updating);
let prog = *lock2!(update, prog);
let msg = format!("{}\n{}{}", *lock2!(update, msg), prog, "%");
ui.add_sized([width, height * 1.4], Label::new(RichText::new(msg)));
let height = height / 2.0;
let size = vec2(width, height);
if updating {
ui.add_sized(size, Spinner::new().size(height));
} else {
ui.add_sized(size, Label::new("..."));
}
}
let height = height / 3.5;
let size = vec2(width, height);
ui.horizontal(|ui| {
ui.set_enabled(self.ratio != Ratio::Height);
ui.add_sized(
size,
Label::new(format!(
" Width [{}-{}]:",
APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16
)),
);
ui.add_sized(
size,
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.set_enabled(self.ratio != Ratio::Width);
ui.add_sized(
size,
Label::new(format!(
"Height [{}-{}]:",
APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16
)),
);
ui.add_sized(
size,
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.add_sized(
size,
Label::new(format!("Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")),
);
ui.add_sized(
size,
Slider::new(&mut self.selected_scale, APP_MIN_SCALE..=APP_MAX_SCALE)
.step_by(0.1),
)
.on_hover_text(GUPAX_SCALE);
ui.add_sized(size, ProgressBar::new(lock2!(update, prog).round() / 100.0));
});
});
ui.style_mut().override_text_style = Some(egui::TextStyle::Button);
ui.separator();
// Width/Height locks
debug!("Gupaxx Tab | Rendering bool buttons");
ui.horizontal(|ui| {
use Ratio::*;
let width = (size.x / 4.0) - (SPACE * 1.5);
ui.group(|ui| {
let width = (size.x - SPACE * 17.0) / 8.0;
let height = if self.simple {
size.y / 10.0
} else {
size.y / 15.0
};
let size = vec2(width, height);
ui.style_mut().override_text_style = Some(egui::TextStyle::Small);
ui.add_sized(size, Checkbox::new(&mut self.auto_update, "Auto-Update"))
.on_hover_text(GUPAX_AUTO_UPDATE);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.bundled, "Bundle"))
.on_hover_text(GUPAX_BUNDLED_UPDATE);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool"))
.on_hover_text(GUPAX_AUTO_P2POOL);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig"))
.on_hover_text(GUPAX_AUTO_XMRIG);
ui.separator();
ui.add_sized(
size,
Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig-Proxy"),
)
.on_hover_text(GUPAX_AUTO_XMRIG_PROXY);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.auto_xvb, "Auto-XvB"))
.on_hover_text(GUPAX_AUTO_XVB);
ui.separator();
ui.add_sized(
size,
Checkbox::new(&mut self.ask_before_quit, "Confirm quit"),
)
.on_hover_text(GUPAX_ASK_BEFORE_QUIT);
ui.separator();
ui.add_sized(
size,
Checkbox::new(&mut self.save_before_quit, "Save on quit"),
)
.on_hover_text(GUPAX_SAVE_BEFORE_QUIT);
});
});
if self.simple {
return;
}
debug!("Gupaxx Tab | Rendering P2Pool/XMRig path selection");
// P2Pool/XMRig binary path selection
let height = size.y / 28.0;
let text_edit = (ui.available_width() / 10.0) - SPACE;
ui.group(|ui| {
ui.add_sized(
[ui.available_width(), height / 2.0],
Label::new(
RichText::new("P2Pool/XMRig/XMRig-Proxy PATHs")
.underline()
.color(LIGHT_GRAY),
),
)
.on_hover_text("Gupaxx is online");
ui.separator();
ui.horizontal(|ui| {
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);
} 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);
} else if !crate::components::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);
} else {
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.set_enabled(!lock!(file_window).thread);
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
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.horizontal(|ui| {
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);
} 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);
} else if !crate::components::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);
} else {
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.set_enabled(!lock!(file_window).thread);
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
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.horizontal(|ui| {
if self.xmrig_proxy_path.is_empty() {
ui.add_sized(
[text_edit, height],
Label::new(
RichText::new(" XMRig-Proxy Binary Path ").color(LIGHT_GRAY),
),
)
.on_hover_text(XMRIG_PROXY_PATH_EMPTY);
} else if !Self::path_is_file(&self.xmrig_proxy_path) {
ui.add_sized(
[text_edit, height],
Label::new(RichText::new(" XMRig-Proxy Binary Path ❌").color(RED)),
)
.on_hover_text(XMRIG_PROXY_PATH_NOT_FILE);
} else if !crate::components::update::check_xp_path(&self.xmrig_proxy_path) {
ui.add_sized(
[text_edit, height],
Label::new(RichText::new(" XMRig-Proxy Binary Path ❌").color(RED)),
)
.on_hover_text(XMRIG_PROXY_PATH_NOT_VALID);
} else {
ui.add_sized(
[text_edit, height],
Label::new(RichText::new(" XMRig-Proxy Binary Path ✔").color(GREEN)),
)
.on_hover_text(XMRIG_PROXY_PATH_OK);
}
ui.spacing_mut().text_edit_width = ui.available_width() - SPACE;
ui.set_enabled(!lock!(file_window).thread);
if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() {
Self::spawn_file_window_thread(file_window, FileType::XmrigProxy);
}
ui.add_sized(
[ui.available_width(), height],
TextEdit::singleline(&mut self.xmrig_proxy_path),
)
.on_hover_text(GUPAX_PATH_XMRIG_PROXY);
});
});
let mut guard = lock!(file_window);
if guard.picked_p2pool {
self.p2pool_path.clone_from(&guard.p2pool_path);
guard.picked_p2pool = false;
}
if guard.picked_xmrig {
self.xmrig_path.clone_from(&guard.xmrig_path);
guard.picked_xmrig = false;
}
drop(guard);
let height = ui.available_height() / 6.0;
// Saved [Tab]
debug!("Gupaxx Tab | Rendering [Tab] selector");
ui.group(|ui| {
let width = (size.x / 6.0) - (SPACE * 1.93);
let size = vec2(width, height);
if ui
.add_sized(
size,
SelectableLabel::new(self.ratio == Width, "Lock to width"),
)
.on_hover_text(GUPAX_LOCK_WIDTH)
.clicked()
{
self.ratio = Width;
}
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();
if ui
.add_sized(
size,
SelectableLabel::new(self.ratio == Height, "Lock to height"),
)
.on_hover_text(GUPAX_LOCK_HEIGHT)
.clicked()
{
self.ratio = Height;
}
ui.horizontal(|ui| {
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::About, "About"))
.on_hover_text(GUPAX_TAB_ABOUT)
.clicked()
{
self.tab = Tab::About;
}
ui.separator();
if ui
.add_sized(
size,
SelectableLabel::new(self.tab == Tab::Status, "Status"),
)
.on_hover_text(GUPAX_TAB_STATUS)
.clicked()
{
self.tab = Tab::Status;
}
ui.separator();
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::Gupax, "Gupaxx"))
.on_hover_text(GUPAX_TAB_GUPAX)
.clicked()
{
self.tab = Tab::Gupax;
}
ui.separator();
if ui
.add_sized(
size,
SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"),
)
.on_hover_text(GUPAX_TAB_P2POOL)
.clicked()
{
self.tab = Tab::P2pool;
}
ui.separator();
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig"))
.on_hover_text(GUPAX_TAB_XMRIG)
.clicked()
{
self.tab = Tab::Xmrig;
}
if ui
.add_sized(size, SelectableLabel::new(self.tab == Tab::Xvb, "XvB"))
.on_hover_text(GUPAX_TAB_XVB)
.clicked()
{
self.tab = Tab::Xvb;
}
})
});
// Gupax App resolution sliders
debug!("Gupaxx Tab | Rendering resolution sliders");
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.separator();
if ui
.add_sized(size, SelectableLabel::new(self.ratio == None, "No lock"))
.on_hover_text(GUPAX_NO_LOCK)
.clicked()
{
self.ratio = None;
}
if ui
.add_sized(size, 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));
}
})
ui.vertical(|ui| {
let width = size.x / 10.0;
ui.spacing_mut().icon_width = width / 25.0;
ui.spacing_mut().slider_width = width * 7.6;
match self.ratio {
Ratio::None => (),
Ratio::Width => {
let width = self.selected_width as f64;
let height = (width / 1.333).round();
self.selected_height = height as u16;
}
Ratio::Height => {
let height = self.selected_height as f64;
let width = (height * 1.333).round();
self.selected_width = width as u16;
}
}
let height = height / 3.5;
let size = vec2(width, height);
ui.horizontal(|ui| {
ui.set_enabled(self.ratio != Ratio::Height);
ui.add_sized(
size,
Label::new(format!(
" Width [{}-{}]:",
APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16
)),
);
ui.add_sized(
size,
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.set_enabled(self.ratio != Ratio::Width);
ui.add_sized(
size,
Label::new(format!(
"Height [{}-{}]:",
APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16
)),
);
ui.add_sized(
size,
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.add_sized(
size,
Label::new(format!("Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")),
);
ui.add_sized(
size,
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.separator();
// Width/Height locks
ui.horizontal(|ui| {
use Ratio::*;
let width = (size.x / 4.0) - (SPACE * 1.5);
let size = vec2(width, height);
if ui
.add_sized(
size,
SelectableLabel::new(self.ratio == Width, "Lock to width"),
)
.on_hover_text(GUPAX_LOCK_WIDTH)
.clicked()
{
self.ratio = Width;
}
ui.separator();
if ui
.add_sized(
size,
SelectableLabel::new(self.ratio == Height, "Lock to height"),
)
.on_hover_text(GUPAX_LOCK_HEIGHT)
.clicked()
{
self.ratio = Height;
}
ui.separator();
if ui
.add_sized(size, SelectableLabel::new(self.ratio == None, "No lock"))
.on_hover_text(GUPAX_NO_LOCK)
.clicked()
{
self.ratio = None;
}
if ui
.add_sized(size, 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));
}
})
});
});
}
}

View file

@ -11,8 +11,10 @@ mod gupax;
mod p2pool;
mod status;
mod xmrig;
mod xmrig_proxy;
mod xvb;
impl crate::app::App {
#[allow(clippy::too_many_arguments)]
pub fn middle_panel(
&mut self,
ctx: &egui::Context,
@ -20,6 +22,7 @@ impl crate::app::App {
key: KeyPressed,
p2pool_is_alive: bool,
xmrig_is_alive: bool,
xmrig_proxy_is_alive: bool,
xvb_is_alive: bool,
) {
// Middle panel, contents of the [Tab]
@ -41,11 +44,13 @@ impl crate::app::App {
let distro = false;
let p2pool_gui_len = lock!(self.p2pool_api).output.len();
let xmrig_gui_len = lock!(self.xmrig_api).output.len();
let xmrig_proxy_gui_len = lock!(self.xmrig_proxy_api).output.len();
let gupax_p2pool_api = lock!(self.gupax_p2pool_api);
let debug_info = format!(
"Gupax version: {}\n
Bundled P2Pool version: {}\n
Bundled XMRig version: {}\n
Bundled XMRig-Proxy version: {}\n
Gupax uptime: {} seconds\n
Selected resolution: {}x{}\n
Internal resolution: {}x{}\n
@ -64,8 +69,10 @@ OS Data PATH: {}\n
Gupax PATH: {}\n
P2Pool PATH: {}\n
XMRig PATH: {}\n
XMRig-Proxy PATH: {}\n
P2Pool console byte length: {}\n
XMRig console byte length: {}\n
XMRig-Proxy console byte length: {}\n
------------------------------------------ P2POOL IMAGE ------------------------------------------
{:#?}\n
------------------------------------------ XMRIG IMAGE ------------------------------------------
@ -84,6 +91,7 @@ path_xmr: {:#?}\n
GUPAX_VERSION,
P2POOL_VERSION,
XMRIG_VERSION,
XMRIG_PROXY_VERSION,
self.now.elapsed().as_secs_f32(),
self.state.gupax.selected_width,
self.state.gupax.selected_height,
@ -104,8 +112,10 @@ path_xmr: {:#?}\n
self.exe,
self.state.gupax.absolute_p2pool_path.display(),
self.state.gupax.absolute_xmrig_path.display(),
self.state.gupax.absolute_xp_path.display(),
p2pool_gui_len,
xmrig_gui_len,
xmrig_proxy_gui_len,
lock!(self.p2pool_img),
lock!(self.xmrig_img),
gupax_p2pool_api.payout,
@ -146,7 +156,7 @@ path_xmr: {:#?}\n
}
Tab::Status => {
debug!("App | Entering [Status] Tab");
crate::disk::state::Status::show(&mut self.state.status, &self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.xvb_api,&self.p2pool_img, &self.xmrig_img, p2pool_is_alive, xmrig_is_alive, xvb_is_alive, self.max_threads, &self.gupax_p2pool_api, &self.benchmarks, self.size, ctx, ui);
crate::disk::state::Status::show(&mut self.state.status, &self.pub_sys, &self.p2pool_api, &self.xmrig_api,&self.xmrig_proxy_api, &self.xvb_api,&self.p2pool_img, &self.xmrig_img, p2pool_is_alive, xmrig_is_alive, xmrig_proxy_is_alive,xvb_is_alive, self.max_threads, &self.gupax_p2pool_api, &self.benchmarks, self.size, ctx, ui);
}
Tab::Gupax => {
debug!("App | Entering [Gupax] Tab");
@ -160,6 +170,10 @@ path_xmr: {:#?}\n
debug!("App | Entering [XMRig] Tab");
crate::disk::state::Xmrig::show(&mut self.state.xmrig, &mut self.pool_vec, &self.xmrig, &self.xmrig_api, &mut self.xmrig_stdin, self.size, ctx, ui);
}
Tab::XmrigProxy => {
debug!("App | Entering [XMRig-Proxy] Tab");
crate::disk::state::XmrigProxy::show(&mut self.state.xmrig_proxy, &self.xmrig_proxy, &mut self.pool_vec, &self.xmrig_proxy_api, &mut self.xmrig_proxy_stdin, self.size, ui);
}
Tab::Xvb => {
debug!("App | Entering [XvB] Tab");
crate::disk::state::Xvb::show(&mut self.state.xvb, self.size, &self.state.p2pool.address, ctx, ui, &self.xvb_api, &self.xmrig_api, lock!(self.xvb).state == ProcessState::Alive);

View file

@ -1,6 +1,6 @@
use std::sync::{Arc, Mutex};
use crate::{app::Benchmark, disk::state::Status, helper::xmrig::PubXmrigApi};
use crate::{app::Benchmark, disk::state::Status, helper::xrig::xmrig::PubXmrigApi};
use egui::{Hyperlink, ProgressBar, ScrollArea, Spinner, Vec2};
use egui_extras::{Column, TableBuilder};
use readable::num::{Float, Percent, Unsigned};

View file

@ -22,7 +22,10 @@ use crate::{
disk::{gupax_p2pool_api::GupaxP2poolApi, state::Status, status::*},
helper::{
p2pool::{ImgP2pool, PubP2poolApi},
xmrig::{ImgXmrig, PubXmrigApi},
xrig::{
xmrig::{ImgXmrig, PubXmrigApi},
xmrig_proxy::PubXmrigProxyApi,
},
xvb::PubXvbApi,
Sys,
},
@ -41,11 +44,13 @@ impl Status {
sys: &Arc<Mutex<Sys>>,
p2pool_api: &Arc<Mutex<PubP2poolApi>>,
xmrig_api: &Arc<Mutex<PubXmrigApi>>,
xmrig_proxy_api: &Arc<Mutex<PubXmrigProxyApi>>,
xvb_api: &Arc<Mutex<PubXvbApi>>,
p2pool_img: &Arc<Mutex<ImgP2pool>>,
xmrig_img: &Arc<Mutex<ImgXmrig>>,
p2pool_alive: bool,
xmrig_alive: bool,
xmrig_proxy_alive: bool,
xvb_alive: bool,
max_threads: usize,
gupax_p2pool_api: &Arc<Mutex<GupaxP2poolApi>>,
@ -65,6 +70,8 @@ impl Status {
p2pool_img,
xmrig_alive,
xmrig_api,
xmrig_proxy_alive,
xmrig_proxy_api,
xmrig_img,
xvb_alive,
xvb_api,

View file

@ -4,7 +4,8 @@ use std::sync::{Arc, Mutex};
use crate::disk::state::Status;
use crate::helper::p2pool::{ImgP2pool, PubP2poolApi};
use crate::helper::xmrig::{ImgXmrig, PubXmrigApi};
use crate::helper::xrig::xmrig::{ImgXmrig, PubXmrigApi};
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
use crate::helper::xvb::{rounds::XvbRound, PubXvbApi};
use crate::helper::Sys;
use crate::utils::macros::lock;
@ -25,50 +26,59 @@ impl Status {
p2pool_img: &Arc<Mutex<ImgP2pool>>,
xmrig_alive: bool,
xmrig_api: &Arc<Mutex<PubXmrigApi>>,
xmrig_proxy_alive: bool,
xmrig_proxy_api: &Arc<Mutex<PubXmrigProxyApi>>,
xmrig_img: &Arc<Mutex<ImgXmrig>>,
xvb_alive: bool,
xvb_api: &Arc<Mutex<PubXvbApi>>,
max_threads: usize,
) {
let width = (size.x / 4.0) - (SPACE * 1.7500);
let min_height = size.y - SPACE;
// set fixed text size, temporary solution before refactoring text/widget size.
let width = (size.x / 5.0) - (SPACE * 1.7500);
let height = size.y / 25.0;
// height must be height - top - bottom - space * 2 - space of text
let size: Vec2 = [width, height].into();
let min_height = size.y - SPACE;
// min width must allow to display text without wrapping.
let size_text = ui.text_style_height(&TextStyle::Body);
// ui.spacing_mut().item_spacing = Vec2::new(2.0, 2.0);
let min_width = size_text * 14.0;
let min_size: Vec2 = [min_width, min_height].into();
ui.horizontal(|ui| {
// [Gupax]
gupax(ui, min_height, width, height, sys);
// [P2Pool]
p2pool(
ui,
min_height,
width,
height,
p2pool_alive,
p2pool_api,
p2pool_img,
);
// [XMRig]
xmrig(
ui,
min_height,
width,
height,
xmrig_alive,
xmrig_api,
xmrig_img,
max_threads,
);
// [XvB]
xvb(ui, min_height, width, height, xvb_alive, xvb_api);
ScrollArea::horizontal().show(ui, |ui| {
ui.set_min_height(min_height * 34.2);
// [Gupax]
gupax(ui, min_size, size, sys);
// [P2Pool]
p2pool(ui, min_size, size, p2pool_alive, p2pool_api, p2pool_img);
// [XMRig]
xmrig(
ui,
min_size,
size,
xmrig_alive,
xmrig_api,
xmrig_img,
max_threads,
);
//[XMRig-Proxy]
xmrig_proxy(ui, min_size, size, xmrig_proxy_alive, xmrig_proxy_api);
// [XvB]
xvb(ui, min_size, size, xvb_alive, xvb_api);
})
});
}
}
fn gupax(ui: &mut Ui, min_height: f32, width: f32, height: f32, sys: &Arc<Mutex<Sys>>) {
fn gupax(ui: &mut Ui, min_size: Vec2, size: Vec2, sys: &Arc<Mutex<Sys>>) {
ui.group(|ui| {
ui.vertical(|ui| {
ui.set_min_size(min_size);
ui.set_min_height(min_size.y * 34.0);
debug!("Status Tab | Rendering [Gupaxx]");
ui.set_min_height(min_height);
// ui.set_min_size([min_size.x, min_size.y / 2.0].into());
ui.add_sized(
[width, height],
size,
Label::new(
RichText::new("[Gupaxx]")
.color(LIGHT_GRAY)
@ -78,50 +88,41 @@ fn gupax(ui: &mut Ui, min_height: f32, width: f32, height: f32, sys: &Arc<Mutex<
.on_hover_text("Gupaxx is online");
let sys = lock!(sys);
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Uptime").underline().color(BONE)),
)
.on_hover_text(STATUS_GUPAX_UPTIME);
ui.add_sized([width, height], Label::new(sys.gupax_uptime.to_string()));
ui.add_sized(size, Label::new(sys.gupax_uptime.to_string()));
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Gupaxx CPU").underline().color(BONE)),
)
.on_hover_text(STATUS_GUPAX_CPU_USAGE);
ui.add_sized([width, height], Label::new(sys.gupax_cpu_usage.to_string()));
ui.add_sized(size, Label::new(sys.gupax_cpu_usage.to_string()));
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Gupaxx Memory").underline().color(BONE)),
)
.on_hover_text(STATUS_GUPAX_MEMORY_USAGE);
ui.add_sized(size, Label::new(sys.gupax_memory_used_mb.to_string()));
ui.add_sized(
[width, height],
Label::new(sys.gupax_memory_used_mb.to_string()),
);
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("System CPU").underline().color(BONE)),
)
.on_hover_text(STATUS_GUPAX_SYSTEM_CPU_USAGE);
ui.add_sized(size, Label::new(sys.system_cpu_usage.to_string()));
ui.add_sized(
[width, height],
Label::new(sys.system_cpu_usage.to_string()),
);
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("System Memory").underline().color(BONE)),
)
.on_hover_text(STATUS_GUPAX_SYSTEM_MEMORY);
ui.add_sized([width, height], Label::new(sys.system_memory.to_string()));
ui.add_sized(size, Label::new(sys.system_memory.to_string()));
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("System CPU Model").underline().color(BONE)),
)
.on_hover_text(STATUS_GUPAX_SYSTEM_CPU_MODEL);
ui.add_sized(
[width, height],
Label::new(sys.system_cpu_model.to_string()),
);
ui.add_sized(size, Label::new(sys.system_cpu_model.to_string()));
drop(sys);
})
});
@ -129,154 +130,215 @@ fn gupax(ui: &mut Ui, min_height: f32, width: f32, height: f32, sys: &Arc<Mutex<
fn p2pool(
ui: &mut Ui,
min_height: f32,
width: f32,
height: f32,
min_size: Vec2,
size: Vec2,
p2pool_alive: bool,
p2pool_api: &Arc<Mutex<PubP2poolApi>>,
p2pool_img: &Arc<Mutex<ImgP2pool>>,
) {
ui.group(|ui| {
ui.vertical(|ui| {
debug!("Status Tab | Rendering [P2Pool]");
ui.set_enabled(p2pool_alive);
ui.set_min_height(min_height);
ScrollArea::vertical().show(ui, |ui| {
ui.set_min_height(min_size.y * 34.0);
ui.set_min_size(min_size);
debug!("Status Tab | Rendering [P2Pool]");
ui.set_enabled(p2pool_alive);
ui.add_sized(
size,
Label::new(
RichText::new("[P2Pool]")
.color(LIGHT_GRAY)
.text_style(TextStyle::Name("MonospaceLarge".into())),
),
)
.on_hover_text("P2Pool is online")
.on_disabled_hover_text("P2Pool is offline");
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
let size = [size.x, size.y / 1.4];
let api = lock!(p2pool_api);
ui.add_sized(
size,
Label::new(RichText::new("Uptime").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_UPTIME);
ui.add_sized(size, Label::new(format!("{}", api.uptime)));
ui.add_sized(
size,
Label::new(RichText::new("Current Shares").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_CURRENT_SHARES);
ui.add_sized(size, Label::new(api.sidechain_shares.to_string()));
ui.add_sized(
size,
Label::new(RichText::new("Shares Found").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_SHARES);
ui.add_sized(
size,
Label::new(
(if let Some(s) = api.shares_found {
s.to_string()
} else {
UNKNOWN_DATA.to_string()
})
.to_string(),
),
);
ui.add_sized(
size,
Label::new(RichText::new("Payouts").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_PAYOUTS);
ui.add_sized(size, Label::new(format!("Total: {}", api.payouts)));
ui.add_sized(
size,
Label::new(format!(
"[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]",
api.payouts_hour, api.payouts_day, api.payouts_month
)),
);
ui.add_sized(
size,
Label::new(RichText::new("XMR Mined").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_XMR);
ui.add_sized(size, Label::new(format!("Total: {:.13} XMR", api.xmr)));
ui.add_sized(
size,
Label::new(format!(
"[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]",
api.xmr_hour, api.xmr_day, api.xmr_month
)),
);
ui.add_sized(
size,
Label::new(
RichText::new("Hashrate (15m/1h/24h)")
.underline()
.color(BONE),
),
)
.on_hover_text(STATUS_P2POOL_HASHRATE);
ui.add_sized(
size,
Label::new(format!(
"[{} H/s] [{} H/s] [{} H/s]",
api.hashrate_15m, api.hashrate_1h, api.hashrate_24h
)),
);
ui.add_sized(
size,
Label::new(RichText::new("Miners Connected").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_CONNECTIONS);
ui.add_sized(size, Label::new(format!("{}", api.connections)));
ui.add_sized(
size,
Label::new(RichText::new("Effort").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_EFFORT);
ui.add_sized(
size,
Label::new(format!(
"[Average: {}] [Current: {}]",
api.average_effort, api.current_effort
)),
);
let img = lock!(p2pool_img);
ui.add_sized(
size,
Label::new(RichText::new("Monero Node").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_MONERO_NODE);
ui.add_sized(
size,
Label::new(format!(
"[IP: {}]\n[RPC: {}] [ZMQ: {}]",
&img.host, &img.rpc, &img.zmq
)),
);
ui.add_sized(
size,
Label::new(RichText::new("Sidechain").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_POOL);
ui.add_sized(size, Label::new(&img.mini));
ui.add_sized(
size,
Label::new(RichText::new("Address").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_ADDRESS);
ui.add_sized(size, Label::new(&img.address));
drop(img);
drop(api);
})
})
});
}
#[allow(clippy::too_many_arguments)]
fn xmrig_proxy(
ui: &mut Ui,
min_size: Vec2,
size: Vec2,
xmrig_proxy_alive: bool,
xmrig_proxy_api: &Arc<Mutex<PubXmrigProxyApi>>,
) {
ui.group(|ui| {
ui.vertical(|ui| {
ui.set_min_height(min_size.y * 34.0);
debug!("Status Tab | Rendering [XMRig-Proxy]");
ui.set_enabled(xmrig_proxy_alive);
ui.set_min_size(min_size);
ui.add_sized(
[width, height],
size,
Label::new(
RichText::new("[P2Pool]")
RichText::new("[XMRig-Proxy]")
.color(LIGHT_GRAY)
.text_style(TextStyle::Name("MonospaceLarge".into())),
),
)
.on_hover_text("P2Pool is online")
.on_disabled_hover_text("P2Pool is offline");
ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into()));
let height = height / 1.4;
let api = lock!(p2pool_api);
.on_hover_text("XMRig-Proxy is online")
.on_disabled_hover_text("XMRig-Proxy is offline");
let api = lock!(xmrig_proxy_api);
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Uptime").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_UPTIME);
ui.add_sized([width, height], Label::new(format!("{}", api.uptime)));
.on_hover_text(STATUS_XMRIG_PROXY_UPTIME);
ui.add_sized(size, Label::new(UptimeFull::from(api.uptime).as_str()));
ui.add_sized(
[width, height],
Label::new(RichText::new("Current Shares").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_CURRENT_SHARES);
ui.add_sized(
[width, height],
Label::new(api.sidechain_shares.to_string()),
);
ui.add_sized(
[width, height],
Label::new(RichText::new("Shares Found").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_SHARES);
ui.add_sized(
[width, height],
size,
Label::new(
(if let Some(s) = api.shares_found {
s.to_string()
} else {
UNKNOWN_DATA.to_string()
})
.to_string(),
),
);
ui.add_sized(
[width, height],
Label::new(RichText::new("Payouts").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_PAYOUTS);
ui.add_sized(
[width, height],
Label::new(format!("Total: {}", api.payouts)),
);
ui.add_sized(
[width, height],
Label::new(format!(
"[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]",
api.payouts_hour, api.payouts_day, api.payouts_month
)),
);
ui.add_sized(
[width, height],
Label::new(RichText::new("XMR Mined").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_XMR);
ui.add_sized(
[width, height],
Label::new(format!("Total: {:.13} XMR", api.xmr)),
);
ui.add_sized(
[width, height],
Label::new(format!(
"[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]",
api.xmr_hour, api.xmr_day, api.xmr_month
)),
);
ui.add_sized(
[width, height],
Label::new(
RichText::new("Hashrate (15m/1h/24h)")
RichText::new("Hashrate\n(1m/10m/1h/12h/24h)")
.underline()
.color(BONE),
),
)
.on_hover_text(STATUS_P2POOL_HASHRATE);
.on_hover_text(STATUS_XMRIG_PROXY_HASHRATE);
ui.add_sized(
[width, height],
size,
Label::new(format!(
"[{} H/s] [{} H/s] [{} H/s]",
api.hashrate_15m, api.hashrate_1h, api.hashrate_24h
"[{} H/s] [{} H/s]\n[{} H/s] [{} H/s] [{} H/s]",
api.hashrate_1m,
api.hashrate_10m,
api.hashrate_1h,
api.hashrate_12h,
api.hashrate_24h
)),
);
ui.add_sized(
[width, height],
Label::new(RichText::new("Miners Connected").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_CONNECTIONS);
ui.add_sized([width, height], Label::new(format!("{}", api.connections)));
ui.add_sized(
[width, height],
Label::new(RichText::new("Effort").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_EFFORT);
ui.add_sized(
[width, height],
size,
Label::new(format!(
"[Average: {}] [Current: {}]",
api.average_effort, api.current_effort
)),
);
let img = lock!(p2pool_img);
ui.add_sized(
[width, height],
Label::new(RichText::new("Monero Node").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_MONERO_NODE);
ui.add_sized(
[width, height],
Label::new(format!(
"[IP: {}]\n[RPC: {}] [ZMQ: {}]",
&img.host, &img.rpc, &img.zmq
"[Accepted: {}]\n[Rejected: {}]",
api.accepted, api.rejected
)),
);
ui.add_sized(
[width, height],
Label::new(RichText::new("Sidechain").underline().color(BONE)),
size,
Label::new(RichText::new("Pool").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_POOL);
ui.add_sized([width, height], Label::new(&img.mini));
ui.add_sized(
[width, height],
Label::new(RichText::new("Address").underline().color(BONE)),
)
.on_hover_text(STATUS_P2POOL_ADDRESS);
ui.add_sized([width, height], Label::new(&img.address));
drop(img);
.on_hover_text(STATUS_XMRIG_PROXY_POOL);
ui.add_sized(size, Label::new(api.node.to_string()));
drop(api);
})
});
@ -284,21 +346,23 @@ fn p2pool(
#[allow(clippy::too_many_arguments)]
fn xmrig(
ui: &mut Ui,
min_height: f32,
width: f32,
height: f32,
min_size: Vec2,
size: Vec2,
xmrig_alive: bool,
xmrig_api: &Arc<Mutex<PubXmrigApi>>,
xmrig_img: &Arc<Mutex<ImgXmrig>>,
max_threads: usize,
) {
ui.group(|ui| {
// ScrollArea::vertical().show(ui, |ui| {
ui.vertical(|ui| {
ui.set_min_height(min_size.y * 34.0);
ui.spacing_mut().item_spacing = Vec2::new(2.0, 2.0);
debug!("Status Tab | Rendering [XMRig]");
ui.set_enabled(xmrig_alive);
ui.set_min_height(min_height);
// ui.set_min_size(min_size);
ui.add_sized(
[width, height],
size,
Label::new(
RichText::new("[XMRig]")
.color(LIGHT_GRAY)
@ -309,91 +373,74 @@ fn xmrig(
.on_disabled_hover_text("XMRig is offline");
let api = lock!(xmrig_api);
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Uptime").underline().color(BONE)),
)
.on_hover_text(STATUS_XMRIG_UPTIME);
ui.add_sized(size, Label::new(UptimeFull::from(api.uptime).as_str()));
ui.add_sized(size, Label::new(api.resources.to_string()));
ui.add_sized(
[width, height],
Label::new(UptimeFull::from(api.uptime).as_str()),
);
ui.add_sized(
[width, height],
size,
Label::new(
RichText::new("CPU Load (10s/60s/15m)")
.underline()
.color(BONE),
),
)
.on_hover_text(STATUS_XMRIG_CPU);
ui.add_sized([width, height], Label::new(api.resources.to_string()));
ui.add_sized(
[width, height],
Label::new(
RichText::new("Hashrate (10s/60s/15m)")
RichText::new("Hashrate\n(10s/1m/15m)")
.underline()
.color(BONE),
),
)
.on_hover_text(STATUS_XMRIG_HASHRATE);
ui.add_sized([width, height], Label::new(api.hashrate.to_string()));
ui.add_sized(size, Label::new(api.hashrate.to_string()));
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Difficulty").underline().color(BONE)),
)
.on_hover_text(STATUS_XMRIG_DIFFICULTY);
ui.add_sized([width, height], Label::new(api.diff.to_string()));
ui.add_sized(size, Label::new(api.diff.to_string()));
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Shares").underline().color(BONE)),
)
.on_hover_text(STATUS_XMRIG_SHARES);
ui.add_sized(
[width, height],
size,
Label::new(format!(
"[Accepted: {}] [Rejected: {}]",
"[Accepted: {}]\n[Rejected: {}]",
api.accepted, api.rejected
)),
);
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Pool").underline().color(BONE)),
)
.on_hover_text(STATUS_XMRIG_POOL);
ui.add_sized([width, height], Label::new(api.node.to_string()));
ui.add_sized(size, Label::new(api.node.to_string()));
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Threads").underline().color(BONE)),
)
.on_hover_text(STATUS_XMRIG_THREADS);
ui.add_sized(
[width, height],
size,
Label::new(format!("{}/{}", &lock!(xmrig_img).threads, max_threads)),
);
drop(api);
})
// })
});
}
fn xvb(
ui: &mut Ui,
min_height: f32,
width: f32,
height: f32,
xvb_alive: bool,
xvb_api: &Arc<Mutex<PubXvbApi>>,
) {
fn xvb(ui: &mut Ui, min_size: Vec2, size: Vec2, xvb_alive: bool, xvb_api: &Arc<Mutex<PubXvbApi>>) {
//
let api = &lock!(xvb_api).stats_pub;
let enabled = xvb_alive;
ui.group(|ui| {
ScrollArea::vertical().show(ui, |ui| {
ui.set_min_height(min_size.y * 34.0);
ui.vertical(|ui| {
debug!("Status Tab | Rendering [XvB]");
ui.set_enabled(enabled); // for now there is no API ping or /health, so we verify if the field reward_yearly is empty or not.
ui.set_min_height(min_height);
// ui.set_min_size(min_size);
ui.add_sized(
[width, height],
size,
Label::new(
RichText::new("[XvB Raffle]")
.color(LIGHT_GRAY)
@ -404,14 +451,14 @@ fn xvb(
.on_disabled_hover_text("No data received from XvB API");
// [Round Type]
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Round Type").underline().color(BONE)),
)
.on_hover_text(STATUS_XVB_ROUND_TYPE);
ui.add_sized([width, height], Label::new(api.round_type.to_string()));
ui.add_sized(size, Label::new(api.round_type.to_string()));
// [Time Remaining]
ui.add_sized(
[width, height],
size,
Label::new(
RichText::new("Round Time Remaining")
.underline()
@ -419,18 +466,15 @@ fn xvb(
),
)
.on_hover_text(STATUS_XVB_TIME_REMAIN);
ui.add_sized(
[width, height],
Label::new(format!("{} minutes", api.time_remain)),
);
ui.add_sized(size, Label::new(format!("{} minutes", api.time_remain)));
// Donated Hashrate
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Bonus Hashrate").underline().color(BONE)),
)
.on_hover_text(STATUS_XVB_DONATED_HR);
ui.add_sized(
[width, height],
size,
Label::new(format!(
"{}kH/s\n+\n{}kH/s\ndonated by\n{} donors\n with\n{} miners",
api.bonus_hr, api.donate_hr, api.donate_miners, api.donate_workers
@ -438,12 +482,12 @@ fn xvb(
);
// Players
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Players").underline().color(BONE)),
)
.on_hover_text(STATUS_XVB_PLAYERS);
ui.add_sized(
[width, height],
size,
Label::new(format!(
"[Registered: {}]\n[Playing: {}]",
api.players, api.players_round
@ -451,28 +495,28 @@ fn xvb(
);
// Winner
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Winner").underline().color(BONE)),
)
.on_hover_text(STATUS_XVB_WINNER);
ui.add_sized([width, height], Label::new(&api.winner));
ui.add_sized(size, Label::new(&api.winner));
// Share effort
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Share Effort").underline().color(BONE)),
)
.on_hover_text(STATUS_XVB_SHARE);
ui.add_sized([width, height], Label::new(api.share_effort.to_string()));
ui.add_sized(size, Label::new(api.share_effort.to_string()));
// Block reward
ui.add_sized(
[width, height],
size,
Label::new(RichText::new("Block Reward").underline().color(BONE)),
)
.on_hover_text(STATUS_XVB_BLOCK_REWARD);
ui.add_sized([width, height], Label::new(api.block_reward.to_string()));
ui.add_sized(size, Label::new(api.block_reward.to_string()));
// reward yearly
ui.add_sized(
[width, height],
size,
Label::new(
RichText::new("Est. Reward (Yearly)")
.underline()
@ -481,10 +525,10 @@ fn xvb(
)
.on_hover_text(STATUS_XVB_YEARLY);
if api.reward_yearly.is_empty() {
ui.add_sized([width, height], Label::new("No information".to_string()));
ui.add_sized(size, Label::new("No information".to_string()));
} else {
ui.add_sized(
[width, height],
size,
Label::new(format!(
"{}: {} XMR\n{}: {} XMR\n{}: {} XMR\n{}: {} XMR\n{}: {} XMR",
XvbRound::Vip,
@ -500,7 +544,7 @@ fn xvb(
)),
);
}
});
})
// by round
});
});

View file

@ -17,7 +17,7 @@
use crate::disk::pool::Pool;
use crate::disk::state::Xmrig;
use crate::helper::xmrig::PubXmrigApi;
use crate::helper::xrig::xmrig::PubXmrigApi;
use crate::helper::Process;
use crate::regex::{num_lines, REGEXES};
use crate::utils::regex::Regexes;

View file

@ -0,0 +1,428 @@
use egui::{vec2, Button, Checkbox, ComboBox, Label, RichText, SelectableLabel, TextEdit, Vec2};
use std::sync::{Arc, Mutex};
use egui::TextStyle::{self, Name};
use log::{debug, info};
use crate::disk::pool::Pool;
use crate::disk::state::XmrigProxy;
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
use crate::helper::Process;
use crate::regex::{num_lines, REGEXES};
use crate::utils::constants::DARK_GRAY;
use crate::utils::macros::lock;
use crate::{
GREEN, LIGHT_GRAY, LIST_ADD, LIST_CLEAR, LIST_DELETE, LIST_SAVE, RED, SPACE, XMRIG_API_IP,
XMRIG_API_PORT, XMRIG_IP, XMRIG_KEEPALIVE, XMRIG_NAME, XMRIG_PORT, XMRIG_PROXY_ARGUMENTS,
XMRIG_PROXY_INPUT, XMRIG_PROXY_REDIRECT, XMRIG_PROXY_URL, XMRIG_RIG, XMRIG_TLS,
};
impl XmrigProxy {
#[inline(always)] // called once
pub fn show(
&mut self,
process: &Arc<Mutex<Process>>,
pool_vec: &mut Vec<(String, Pool)>,
api: &Arc<Mutex<PubXmrigProxyApi>>,
buffer: &mut String,
size: Vec2,
ui: &mut egui::Ui,
) {
let width = size.x;
let height = size.y;
let space_h = height / 48.0;
let text_edit = size.y / 25.0;
ui.vertical_centered(|ui| {
ui.add_space(space_h);
ui.style_mut().override_text_style = Some(TextStyle::Heading);
ui.hyperlink_to("XMRig-Proxy", XMRIG_PROXY_URL);
ui.style_mut().override_text_style = Some(TextStyle::Body);
ui.add(Label::new("High performant proxy for your miners"));
ui.add_space(space_h);
});
// console output for log
debug!("XvB Tab | Rendering [Console]");
ui.group(|ui| {
let text = &lock!(api).output;
let nb_lines = num_lines(text);
let height = size.y / 2.8;
let width = size.x - (space_h / 2.0);
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
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, _| {
.show_rows(
ui,
ui.text_style_height(&TextStyle::Name("MonospaceSmall".into())),
nb_lines,
|ui, row_range| {
for i in row_range {
if let Some(line) = text.lines().nth(i) {
ui.label(line);
}
}
},
);
});
});
//---------------------------------------------------------------------------------------------------- [Advanced] Console
if !self.simple {
ui.separator();
let response = ui
.add_sized(
[width, text_edit],
TextEdit::hint_text(
TextEdit::singleline(buffer),
r#"Commands: [h]ashrate, [c]onnections, [v]erbose, [w]orkers"#,
),
)
.on_hover_text(XMRIG_PROXY_INPUT);
// 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)) {
response.request_focus(); // Get focus back
let buffer = std::mem::take(buffer); // Take buffer
let mut process = lock!(process); // Lock
if process.is_alive() {
process.input.push(buffer);
} // Push only if alive
}
//---------------------------------------------------------------------------------------------------- Arguments
debug!("XMRig Tab | Rendering [Arguments]");
ui.group(|ui| {
ui.horizontal(|ui| {
let width = (size.x / 10.0) - SPACE;
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_PROXY_ARGUMENTS);
self.arguments.truncate(1024);
})
});
ui.set_enabled(self.arguments.is_empty());
ui.add_space(space_h);
ui.style_mut().spacing.icon_width_inner = width / 45.0;
ui.style_mut().spacing.icon_width = width / 35.0;
ui.style_mut().spacing.icon_spacing = space_h;
ui.checkbox(
&mut self.redirect_local_xmrig,
"Auto Redirect local Xmrig to Xmrig-Proxy",
)
.on_hover_text(XMRIG_PROXY_REDIRECT);
// idea
// need to warn the user if local firewall is blocking port
// need to warn the user if NAT is blocking port
// need to show local ip address
// need to show public ip
debug!("XMRig Tab | Rendering [Pool List] elements");
let width = ui.available_width() - 10.0;
let mut incorrect_input = false; // This will disable [Add/Delete] on bad input
// [Pool IP/Port]
ui.horizontal(|ui| {
ui.group(|ui| {
let width = width/10.0;
ui.vertical(|ui| {
ui.spacing_mut().text_edit_width = width*3.32;
ui.horizontal(|ui| {
let text;
let color;
let len = format!("{:02}", self.name.len());
if self.name.is_empty() {
text = format!("Name [ {}/30 ]", len);
color = LIGHT_GRAY;
incorrect_input = true;
} else if REGEXES.name.is_match(&self.name) {
text = format!("Name [ {}/30 ]✔", len);
color = GREEN;
} else {
text = format!("Name [ {}/30 ]❌", len);
color = RED;
incorrect_input = true;
}
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
ui.text_edit_singleline(&mut self.name).on_hover_text(XMRIG_NAME);
self.name.truncate(30);
});
ui.horizontal(|ui| {
let text;
let color;
let len = format!("{:03}", self.p2pool_ip.len());
if self.p2pool_ip.is_empty() {
text = format!(" IP [{}/255]", len);
color = LIGHT_GRAY;
incorrect_input = true;
} else if self.p2pool_ip == "localhost" || REGEXES.ipv4.is_match(&self.p2pool_ip) || REGEXES.domain.is_match(&self.p2pool_ip) {
text = format!(" IP [{}/255]✔", len);
color = GREEN;
} else {
text = format!(" IP [{}/255]❌", len);
color = RED;
incorrect_input = true;
}
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
ui.text_edit_singleline(&mut self.p2pool_ip).on_hover_text(XMRIG_IP);
self.p2pool_ip.truncate(255);
});
ui.horizontal(|ui| {
let text;
let color;
let len = self.p2pool_port.len();
if self.p2pool_port.is_empty() {
text = format!("Port [ {}/5 ]", len);
color = LIGHT_GRAY;
incorrect_input = true;
} else if REGEXES.port.is_match(&self.p2pool_port) {
text = format!("Port [ {}/5 ]✔", len);
color = GREEN;
} else {
text = format!("Port [ {}/5 ]❌", len);
color = RED;
incorrect_input = true;
}
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
ui.text_edit_singleline(&mut self.p2pool_port).on_hover_text(XMRIG_PORT);
self.p2pool_port.truncate(5);
});
ui.horizontal(|ui| {
let text;
let color;
let len = format!("{:02}", self.rig.len());
if self.rig.is_empty() {
text = format!(" Rig [ {}/30 ]", len);
color = LIGHT_GRAY;
} else if REGEXES.name.is_match(&self.rig) {
text = format!(" Rig [ {}/30 ]✔", len);
color = GREEN;
} else {
text = format!(" Rig [ {}/30 ]❌", len);
color = RED;
incorrect_input = true;
}
ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color)));
ui.text_edit_singleline(&mut self.rig).on_hover_text(XMRIG_RIG);
self.rig.truncate(30);
});
});
ui.vertical(|ui| {
let width = ui.available_width();
ui.add_space(1.0);
// [Manual node selection]
ui.spacing_mut().slider_width = width - 8.0;
ui.spacing_mut().icon_width = width / 25.0;
// [Node List]
debug!("XMRig Tab | Rendering [Node List] ComboBox");
let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name));
ComboBox::from_id_source("manual_pool").selected_text(text).width(width).show_ui(ui, |ui| {
for (n, (name, pool)) in pool_vec.iter().enumerate() {
let text = format!("{}. {}\n IP: {}\n Port: {}\n Rig: {}", n+1, name, pool.ip, pool.port, pool.rig);
if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() {
self.selected_index = n;
let pool = pool.clone();
self.selected_name.clone_from(name);
self.selected_rig.clone_from(&pool.rig);
self.selected_ip.clone_from(&pool.ip);
self.selected_port.clone_from(&pool.port);
self.name.clone_from(name);
self.rig = pool.rig;
self.p2pool_ip = pool.ip;
self.p2pool_port = pool.port;
}
}
});
// [Add/Save]
let pool_vec_len = pool_vec.len();
let mut exists = false;
let mut save_diff = true;
let mut existing_index = 0;
for (name, pool) in pool_vec.iter() {
if *name == self.name {
exists = true;
if self.rig == pool.rig && self.p2pool_ip == pool.ip && self.p2pool_port == pool.port {
save_diff = false;
}
break
}
existing_index += 1;
}
ui.horizontal(|ui| {
let text = if exists { LIST_SAVE } else { LIST_ADD };
let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", text, self.selected_index+1, self.selected_name, pool_vec_len);
// If the pool already exists, show [Save] and mutate the already existing pool
if exists {
ui.set_enabled(!incorrect_input && save_diff);
if ui.add_sized([width, text_edit], Button::new("Save")).on_hover_text(text).clicked() {
let pool = Pool {
rig: self.rig.clone(),
ip: self.p2pool_ip.clone(),
port: self.p2pool_port.clone(),
};
pool_vec[existing_index].1 = pool;
self.selected_name.clone_from(&self.name);
self.selected_rig.clone_from(&self.rig);
self.selected_ip.clone_from(&self.p2pool_ip);
self.selected_port.clone_from(&self.p2pool_port);
info!("Node | S | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", existing_index+1, self.name, self.p2pool_ip, self.p2pool_port, self.rig);
}
// Else, add to the list
} else {
ui.set_enabled(!incorrect_input && pool_vec_len < 1000);
if ui.add_sized([width, text_edit], Button::new("Add")).on_hover_text(text).clicked() {
let pool = Pool {
rig: self.rig.clone(),
ip: self.p2pool_ip.clone(),
port: self.p2pool_port.clone(),
};
pool_vec.push((self.name.clone(), pool));
self.selected_index = pool_vec_len;
self.selected_name.clone_from(&self.name);
self.selected_rig.clone_from(&self.rig);
self.selected_ip.clone_from(&self.p2pool_ip);
self.selected_port.clone_from(&self.p2pool_port);
info!("Node | A | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", pool_vec_len, self.name, self.p2pool_ip, self.p2pool_port, self.rig);
}
}
});
// [Delete]
ui.horizontal(|ui| {
ui.set_enabled(pool_vec_len > 1);
let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", LIST_DELETE, self.selected_index+1, self.selected_name, pool_vec_len);
if ui.add_sized([width, text_edit], Button::new("Delete")).on_hover_text(text).clicked() {
let new_name;
let new_pool;
match self.selected_index {
0 => {
new_name = pool_vec[1].0.clone();
new_pool = pool_vec[1].1.clone();
pool_vec.remove(0);
}
_ => {
pool_vec.remove(self.selected_index);
self.selected_index -= 1;
new_name = pool_vec[self.selected_index].0.clone();
new_pool = pool_vec[self.selected_index].1.clone();
}
};
self.selected_name.clone_from(&new_name);
self.selected_rig.clone_from(&new_pool.rig);
self.selected_ip.clone_from(&new_pool.ip);
self.selected_port.clone_from(&new_pool.port);
self.name = new_name;
self.rig = new_pool.rig;
self.p2pool_ip = new_pool.ip;
self.p2pool_port = new_pool.port;
info!("Node | D | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig\"{}\"]", self.selected_index, self.selected_name, self.selected_ip, self.selected_port, self.selected_rig);
}
});
ui.horizontal(|ui| {
ui.set_enabled(!self.name.is_empty() || !self.p2pool_ip.is_empty() || !self.p2pool_port.is_empty());
if ui.add_sized([width, text_edit], Button::new("Clear")).on_hover_text(LIST_CLEAR).clicked() {
self.name.clear();
self.rig.clear();
self.p2pool_ip.clear();
self.p2pool_port.clear();
}
});
});
});
});
ui.add_space(5.0);
debug!("XMRig Tab | Rendering [API] TextEdits");
// [HTTP API IP/Port]
ui.group(|ui| {
ui.horizontal(|ui| {
ui.vertical(|ui| {
let width = width / 10.0;
ui.spacing_mut().text_edit_width = width * 2.39;
// HTTP API
ui.horizontal(|ui| {
let text;
let color;
let len = format!("{:03}", self.api_ip.len());
if self.api_ip.is_empty() {
text = format!("HTTP API IP [{}/255]", len);
color = LIGHT_GRAY;
incorrect_input = true;
} 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);
color = GREEN;
} else {
text = format!("HTTP API IP [{}/255]❌", len);
color = RED;
incorrect_input = true;
}
ui.add_sized(
[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);
});
ui.horizontal(|ui| {
let text;
let color;
let len = self.api_port.len();
if self.api_port.is_empty() {
text = format!("HTTP API Port [ {}/5 ]", len);
color = LIGHT_GRAY;
incorrect_input = true;
} else if REGEXES.port.is_match(&self.api_port) {
text = format!("HTTP API Port [ {}/5 ]✔", len);
color = GREEN;
} else {
text = format!("HTTP API Port [ {}/5 ]❌", len);
color = RED;
incorrect_input = true;
}
ui.add_sized(
[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);
});
});
ui.separator();
debug!("XMRig Tab | Rendering [TLS/Keepalive] buttons");
ui.vertical(|ui| {
// TLS/Keepalive
ui.horizontal(|ui| {
let width = (ui.available_width() / 2.0) - 11.0;
let height = text_edit * 2.0;
let size = vec2(width, height);
// let mut style = (*ctx.style()).clone();
// style.spacing.icon_width_inner = width / 8.0;
// style.spacing.icon_width = width / 6.0;
// style.spacing.icon_spacing = 20.0;
// ctx.set_style(style);
ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection"))
.on_hover_text(XMRIG_TLS);
ui.separator();
ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive"))
.on_hover_text(XMRIG_KEEPALIVE);
});
});
});
});
}
}
}

View file

@ -7,7 +7,7 @@ use readable::num::Float;
use readable::up::Uptime;
use crate::disk::state::{ManualDonationLevel, ManualDonationMetric, XvbMode};
use crate::helper::xmrig::PubXmrigApi;
use crate::helper::xrig::xmrig::PubXmrigApi;
use crate::helper::xvb::priv_stats::RuntimeMode;
use crate::helper::xvb::PubXvbApi;
use crate::regex::num_lines;

View file

@ -8,7 +8,7 @@ impl crate::app::App {
pub fn top_panel(&mut self, ctx: &egui::Context) {
debug!("App | Rendering TOP tabs");
TopBottomPanel::top("top").show(ctx, |ui| {
let width = (self.size.x - (SPACE * 11.0)) / 6.0;
let width = (self.size.x - (SPACE * 16.0)) / 7.0;
let height = self.size.y / 15.0;
ui.add_space(4.0);
ui.horizontal(|ui| {
@ -63,6 +63,16 @@ impl crate::app::App {
self.tab = Tab::Xmrig;
}
ui.separator();
if ui
.add_sized(
[width, height],
SelectableLabel::new(self.tab == Tab::XmrigProxy, "Proxy"),
)
.clicked()
{
self.tab = Tab::XmrigProxy;
}
ui.separator();
if ui
.add_sized(
[width, height],

View file

@ -1,5 +1,3 @@
use std::process::exit;
use clap::crate_authors;
use clap::crate_description;
use clap::crate_name;
@ -9,6 +7,7 @@ use clap::Subcommand;
use log::debug;
use log::info;
use log::warn;
use std::process::exit;
use crate::app::App;
use crate::miscs::print_disk_file;
@ -29,6 +28,8 @@ use crate::resets::reset_state;
pub struct Cli {
#[command(subcommand)]
pub info: Option<GupaxxData>,
#[clap(long, short, action)]
pub logfile: bool,
}
#[derive(Subcommand)]

View file

@ -31,11 +31,13 @@ use std::{
// The opened file picker is started in a new
// thread so main() needs to be in sync.
pub struct FileWindow {
pub thread: bool, // Is there already a FileWindow thread?
pub picked_p2pool: bool, // Did the user pick a path for p2pool?
pub picked_xmrig: bool, // Did the user pick a path for xmrig?
pub p2pool_path: String, // The picked p2pool path
pub xmrig_path: String, // The picked p2pool path
pub thread: bool, // Is there already a FileWindow thread?
pub picked_p2pool: bool, // Did the user pick a path for p2pool?
pub picked_xmrig: bool, // Did the user pick a path for xmrig?
pub picked_xp: bool, // Did the user pick a path for xmrig-proxy?
pub p2pool_path: String, // The picked p2pool path
pub xmrig_path: String, // The picked xmrig path
pub xmrig_proxy_path: String, // The picked xmrig-proxy path
}
impl FileWindow {
@ -44,8 +46,10 @@ impl FileWindow {
thread: false,
picked_p2pool: false,
picked_xmrig: false,
picked_xp: false,
p2pool_path: String::new(),
xmrig_path: String::new(),
xmrig_proxy_path: String::new(),
})
}
}
@ -54,6 +58,7 @@ impl FileWindow {
pub enum FileType {
P2pool,
Xmrig,
XmrigProxy,
}
//---------------------------------------------------------------------------------------------------- Ratio Lock
@ -83,6 +88,7 @@ impl Gupax {
let name = match file_type {
P2pool => "P2Pool",
Xmrig => "XMRig",
XmrigProxy => "XMRigProxy",
};
let file_window = file_window.clone();
lock!(file_window).thread = true;
@ -102,6 +108,10 @@ impl Gupax {
lock!(file_window).xmrig_path = path.display().to_string();
lock!(file_window).picked_xmrig = true;
}
XmrigProxy => {
lock!(file_window).xmrig_proxy_path = path.display().to_string();
lock!(file_window).picked_xp = true;
}
};
}
None => info!("Gupaxx | No path selected for {}", name),

View file

@ -28,7 +28,7 @@ use std::time::{Duration, Instant};
// Remote Monero Nodes with ZMQ enabled.
// The format is an array of tuples consisting of: (IP, LOCATION, RPC_PORT, ZMQ_PORT)
pub const REMOTE_NODES: [(&str, &str, &str, &str); 15] = [
pub const REMOTE_NODES: [(&str, &str, &str, &str); 14] = [
("monero.10z.com.ar", "Argentina", "18089", "18084"),
("monero1.heitechsoft.com", "Canada", "18081", "18084"),
("node.monerodevs.org", "Canada", "18089", "18084"),
@ -42,7 +42,6 @@ pub const REMOTE_NODES: [(&str, &str, &str, &str); 15] = [
("sf.xmr.support", "United States", "18081", "18083"),
("xmrbandwagon.hopto.org", "United States", "18081", "18084"),
("xmr.spotlightsound.com", "United States", "18081", "18084"),
("xmrnode.facspro.net", "United States", "18089", "18084"),
("node.richfowler.net", "United States", "18089", "18084"),
];

View file

@ -62,6 +62,7 @@ cfg_if::cfg_if! {
pub(super) const GUPAX_BINARY: &str = "gupaxx";
pub(super) const P2POOL_BINARY: &str = "p2pool";
pub(super) const XMRIG_BINARY: &str = "xmrig";
pub(super) const XMRIG_PROXY_BINARY: &str = "xmrig-proxy";
}
}
cfg_if::cfg_if! {
@ -71,6 +72,7 @@ cfg_if::cfg_if! {
pub(super) const GUPAX_BINARY: &str = "Gupaxx.exe";
pub(super) const P2POOL_BINARY: &str = "p2pool.exe";
pub(super) const XMRIG_BINARY: &str = "xmrig.exe";
pub(super) const XMRIG_PROXY_BINARY: &str = "xmrig-proxy.exe";
} else if #[cfg(target_os = "linux")] {
pub(super) const OS_TARGET: &str = "linux";
pub(super) const ARCHIVE_EXT: &str = "tar.gz";
@ -165,6 +167,20 @@ pub fn check_xmrig_path(path: &str) -> bool {
};
path == XMRIG_BINARY
}
pub fn check_xp_path(path: &str) -> bool {
let path = match crate::disk::into_absolute_path(path.to_string()) {
Ok(p) => p,
Err(_) => return false,
};
let path = match path.file_name() {
Some(p) => p,
None => {
error!("Couldn't get XMRig-Proxy file name");
return false;
}
};
path == XMRIG_PROXY_BINARY
}
//---------------------------------------------------------------------------------------------------- Update struct/impl
// Contains values needed during update
@ -183,6 +199,7 @@ pub struct Update {
pub path_gupax: String, // Full path to current gupax
pub path_p2pool: String, // Full path to current p2pool
pub path_xmrig: String, // Full path to current xmrig
pub path_xp: String, // Full path to current xmrig
pub updating: Arc<Mutex<bool>>, // Is an update in progress?
pub prog: Arc<Mutex<f32>>, // Holds the 0-100% progress bar number
pub msg: Arc<Mutex<String>>, // Message to display on [Gupax] tab while updating
@ -190,11 +207,17 @@ pub struct Update {
impl Update {
// Takes in current paths from [State]
pub fn new(path_gupax: String, path_p2pool: PathBuf, path_xmrig: PathBuf) -> Self {
pub fn new(
path_gupax: String,
path_p2pool: PathBuf,
path_xmrig: PathBuf,
path_xp: PathBuf,
) -> Self {
Self {
path_gupax,
path_p2pool: path_p2pool.display().to_string(),
path_xmrig: path_xmrig.display().to_string(),
path_xp: path_xp.display().to_string(),
updating: arc_mut!(false),
prog: arc_mut!(0.0),
msg: arc_mut!(MSG_NONE.to_string()),
@ -242,7 +265,7 @@ impl Update {
error!("Update | This is the [Linux distro] version of Gupax, updates are disabled");
#[cfg(feature = "distro")]
return;
// verify validity of absolute path for p2pool and xmrig only if we want to update them.
// verify validity of absolute path for p2pool, xmrig and xmrig-proxy only if we want to update them.
if lock!(og).gupax.bundled {
// Check P2Pool path for safety
// Attempt relative to absolute path
@ -276,8 +299,24 @@ impl Update {
return;
}
};
// Check XMRig-Proxy path for safety
let xmrig_proxy_path = match into_absolute_path(gupax.xmrig_proxy_path.clone()) {
Ok(p) => p,
Err(e) => {
error_state.set(
format!(
"Provided XMRig-Proxy path could not be turned into an absolute path: {}",
e
),
ErrorFerris::Error,
ErrorButtons::Okay,
);
return;
}
};
lock!(update).path_p2pool = p2pool_path.display().to_string();
lock!(update).path_xmrig = xmrig_path.display().to_string();
lock!(update).path_xp = xmrig_proxy_path.display().to_string();
}
// Clone before thread spawn
@ -471,7 +510,7 @@ impl Update {
info!("Update | Extract ... OK ... {}%", *lock2!(update, prog));
//---------------------------------------------------------------------------------------------------- Upgrade
// if bundled, directories p2pool and xmrig will exist.
// if bundled, directories p2pool, xmrig and xmrig-proxy will exist.
// if not, only gupaxx binary will be present.
// 1. Walk directories
//
@ -496,6 +535,7 @@ impl Update {
GUPAX_BINARY => lock!(update).path_gupax.clone(),
P2POOL_BINARY => lock!(update).path_p2pool.clone(),
XMRIG_BINARY => lock!(update).path_xmrig.clone(),
XMRIG_PROXY_BINARY => lock!(update).path_xp.clone(),
_ => continue,
};
found = true;
@ -511,6 +551,7 @@ impl Update {
GUPAX_BINARY => tmp_dir.clone() + "gupaxx_old.exe",
P2POOL_BINARY => tmp_dir.clone() + "p2pool_old.exe",
XMRIG_BINARY => tmp_dir.clone() + "xmrig_old.exe",
XMRIG_PROXY_BINARY => tmp_dir.clone() + "xmrig-proxy_old.exe",
_ => continue,
};
info!(
@ -525,8 +566,10 @@ impl Update {
entry.path().display(),
path.display()
);
// if bundled, create directory for p2pool and xmrig if not present
if lock!(og).gupax.bundled && (name == P2POOL_BINARY || name == XMRIG_BINARY) {
// if bundled, create directory for p2pool, xmrig and xmrig-proxy if not present
if lock!(og).gupax.bundled
&& (name == P2POOL_BINARY || name == XMRIG_BINARY || name == XMRIG_PROXY_BINARY)
{
std::fs::create_dir_all(
path.parent()
.ok_or_else(|| anyhow!(format!("{} path failed", name)))?,

View file

@ -40,8 +40,12 @@ pub const DEFAULT_P2POOL_PATH: &str = r"P2Pool\p2pool.exe";
pub const DEFAULT_P2POOL_PATH: &str = "p2pool/p2pool";
#[cfg(target_os = "windows")]
pub const DEFAULT_XMRIG_PATH: &str = r"XMRig\xmrig.exe";
#[cfg(target_os = "windows")]
pub const DEFAULT_XMRIG_PROXY_PATH: &str = r"XMRig-Proxy\xmrig-proxy.exe";
#[cfg(target_os = "macos")]
pub const DEFAULT_XMRIG_PATH: &str = "xmrig/xmrig";
#[cfg(target_os = "macos")]
pub const DEFAULT_XMRIG_PROXY_PATH: &str = "xmrig-proxy/xmrig-proxy";
// Default to [/usr/bin/] for Linux distro builds.
#[cfg(target_os = "linux")]
@ -51,8 +55,14 @@ pub const DEFAULT_P2POOL_PATH: &str = "p2pool/p2pool";
#[cfg(not(feature = "distro"))]
pub const DEFAULT_XMRIG_PATH: &str = "xmrig/xmrig";
#[cfg(target_os = "linux")]
#[cfg(not(feature = "distro"))]
pub const DEFAULT_XMRIG_PROXY_PATH: &str = "xmrig-proxy/xmrig-proxy";
#[cfg(target_os = "linux")]
#[cfg(feature = "distro")]
pub const DEFAULT_P2POOL_PATH: &str = "/usr/bin/p2pool";
#[cfg(target_os = "linux")]
#[cfg(feature = "distro")]
pub const DEFAULT_XMRIG_PATH: &str = "/usr/bin/xmrig";
#[cfg(target_os = "linux")]
#[cfg(feature = "distro")]
pub const DEFAULT_XMRIG_PROXY_PATH: &str = "/usr/bin/xmrig-proxy";

View file

@ -20,6 +20,7 @@ impl State {
p2pool: P2pool::default(),
xmrig: Xmrig::with_threads(max_threads, current_threads),
xvb: Xvb::default(),
xmrig_proxy: XmrigProxy::default(),
version: arc_mut!(Version::default()),
}
}
@ -27,6 +28,7 @@ impl State {
pub fn update_absolute_path(&mut self) -> Result<(), TomlError> {
self.gupax.absolute_p2pool_path = into_absolute_path(self.gupax.p2pool_path.clone())?;
self.gupax.absolute_xmrig_path = into_absolute_path(self.gupax.xmrig_path.clone())?;
self.gupax.absolute_xp_path = into_absolute_path(self.gupax.xmrig_proxy_path.clone())?;
Ok(())
}
@ -108,6 +110,7 @@ impl State {
// Convert path to absolute
self.gupax.absolute_p2pool_path = into_absolute_path(self.gupax.p2pool_path.clone())?;
self.gupax.absolute_xmrig_path = into_absolute_path(self.gupax.xmrig_path.clone())?;
self.gupax.absolute_xp_path = into_absolute_path(self.gupax.xmrig_proxy_path.clone())?;
let string = match toml::ser::to_string(&self) {
Ok(string) => {
info!("State | Parse ... OK");
@ -158,6 +161,7 @@ pub struct State {
pub gupax: Gupax,
pub p2pool: P2pool,
pub xmrig: Xmrig,
pub xmrig_proxy: XmrigProxy,
pub xvb: Xvb,
pub version: Arc<Mutex<Version>>,
}
@ -178,14 +182,17 @@ pub struct Gupax {
pub auto_update: bool,
pub auto_p2pool: bool,
pub auto_xmrig: bool,
pub auto_xp: bool,
pub auto_xvb: bool,
// pub auto_monero: bool,
pub ask_before_quit: bool,
pub save_before_quit: bool,
pub p2pool_path: String,
pub xmrig_path: String,
pub xmrig_proxy_path: String,
pub absolute_p2pool_path: PathBuf,
pub absolute_xmrig_path: PathBuf,
pub absolute_xp_path: PathBuf,
pub selected_width: u16,
pub selected_height: u16,
pub selected_scale: f32,
@ -243,6 +250,64 @@ pub struct Xmrig {
pub token: String,
}
// present for future.
#[derive(Clone, Deserialize, Serialize, Debug)]
pub struct XmrigProxy {
pub simple: bool,
pub arguments: String,
pub simple_rig: String,
pub tls: bool,
pub keepalive: bool,
pub address: String,
pub name: String,
pub rig: String,
pub ip: String,
pub port: String,
pub api_ip: String,
pub api_port: String,
pub p2pool_ip: String,
pub p2pool_port: String,
pub selected_index: usize,
pub selected_name: String,
pub selected_rig: String,
pub selected_ip: String,
pub selected_port: String,
pub token: String,
pub redirect_local_xmrig: bool,
}
impl Default for XmrigProxy {
fn default() -> Self {
XmrigProxy {
simple: true,
arguments: Default::default(),
token: thread_rng()
.sample_iter(Alphanumeric)
.take(16)
.map(char::from)
.collect(),
redirect_local_xmrig: true,
address: String::with_capacity(96),
name: "Local P2Pool".to_string(),
rig: GUPAX_VERSION_UNDERSCORE.to_string(),
simple_rig: String::with_capacity(30),
ip: "0.0.0.0".to_string(),
port: "3355".to_string(),
p2pool_ip: "localhost".to_string(),
p2pool_port: "3333".to_string(),
selected_index: 0,
selected_name: "Local P2Pool".to_string(),
selected_ip: "localhost".to_string(),
selected_rig: GUPAX_VERSION_UNDERSCORE.to_string(),
selected_port: "3333".to_string(),
api_ip: "localhost".to_string(),
api_port: "18089".to_string(),
tls: false,
keepalive: false,
}
}
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct Xvb {
pub simple: bool,
@ -349,13 +414,16 @@ impl Default for Gupax {
auto_update: false,
auto_p2pool: false,
auto_xmrig: false,
auto_xp: false,
auto_xvb: false,
ask_before_quit: true,
save_before_quit: true,
p2pool_path: DEFAULT_P2POOL_PATH.to_string(),
xmrig_path: DEFAULT_XMRIG_PATH.to_string(),
xmrig_proxy_path: DEFAULT_XMRIG_PROXY_PATH.to_string(),
absolute_p2pool_path: into_absolute_path(DEFAULT_P2POOL_PATH.to_string()).unwrap(),
absolute_xmrig_path: into_absolute_path(DEFAULT_XMRIG_PATH.to_string()).unwrap(),
absolute_xp_path: into_absolute_path(DEFAULT_XMRIG_PROXY_PATH.to_string()).unwrap(),
selected_width: APP_DEFAULT_WIDTH as u16,
selected_height: APP_DEFAULT_HEIGHT as u16,
selected_scale: APP_DEFAULT_SCALE,

View file

@ -32,12 +32,15 @@ mod test {
auto_p2pool = false
auto_xmrig = false
auto_xvb = false
auto_xp = false
ask_before_quit = true
save_before_quit = true
p2pool_path = "p2pool/p2pool"
xmrig_path = "xmrig/xmrig"
xmrig_proxy_path = "xmrig-proxy/xmrig-proxy"
absolute_p2pool_path = "/home/hinto/p2pool/p2pool"
absolute_xmrig_path = "/home/hinto/xmrig/xmrig"
absolute_xp_path = "/home/hinto/xmrig/xmrig-proxy/xmrig-proxy"
selected_width = 1280
selected_height = 960
selected_scale = 0.0
@ -98,6 +101,29 @@ mod test {
selected_port = "3333"
token = "testtoken"
[xmrig_proxy]
simple = true
arguments = ""
address = ""
simple_rig = ""
tls = false
name = "linux"
rig = "Gupaxx"
keepalive = false
ip = "localhost"
port = "30948"
api_ip = "localhost"
api_port = "18088"
p2pool_ip = "localhost"
p2pool_port = "18088"
token = "testtoken"
selected_index = 1
selected_name = "linux"
selected_rig = "Gupaxx"
selected_ip = "192.168.1.122"
selected_port = "3333"
redirect_local_xmrig = true
[xvb]
simple = true
simple_hero_mode = true
@ -109,6 +135,7 @@ mod test {
token = ""
hero = false
node = "Europe"
[version]
gupax = "v1.3.0"
p2pool = "v2.5"

View file

@ -34,12 +34,16 @@
// piping their stdout/stderr/stdin, accessing their APIs (HTTP + disk files), etc.
//---------------------------------------------------------------------------------------------------- Import
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
use crate::helper::{
p2pool::{ImgP2pool, PubP2poolApi},
xmrig::{ImgXmrig, PubXmrigApi},
xrig::{xmrig::ImgXmrig, xmrig::PubXmrigApi},
};
use crate::{constants::*, disk::gupax_p2pool_api::GupaxP2poolApi, human::*, macros::*};
use log::*;
use portable_pty::Child;
use readable::up::Uptime;
use std::fmt::Write;
use std::path::Path;
use std::{
path::PathBuf,
@ -51,7 +55,7 @@ use std::{
use self::xvb::{nodes::XvbNode, PubXvbApi};
pub mod p2pool;
pub mod tests;
pub mod xmrig;
pub mod xrig;
pub mod xvb;
//---------------------------------------------------------------------------------------------------- Constants
@ -73,15 +77,18 @@ pub struct Helper {
pub pub_sys: Arc<Mutex<Sys>>, // The public API for [sysinfo] that the [Status] tab reads from
pub p2pool: Arc<Mutex<Process>>, // P2Pool process state
pub xmrig: Arc<Mutex<Process>>, // XMRig process state
pub xmrig_proxy: Arc<Mutex<Process>>, // XMRig process state
pub xvb: Arc<Mutex<Process>>, // XvB process state
pub gui_api_p2pool: Arc<Mutex<PubP2poolApi>>, // P2Pool API state (for GUI thread)
pub gui_api_xmrig: Arc<Mutex<PubXmrigApi>>, // XMRig API state (for GUI thread)
pub gui_api_xp: Arc<Mutex<PubXmrigProxyApi>>, // XMRig-Proxy API state (for GUI thread)
pub gui_api_xvb: Arc<Mutex<PubXvbApi>>, // XMRig API state (for GUI thread)
pub img_p2pool: Arc<Mutex<ImgP2pool>>, // A static "image" of the data P2Pool started with
pub img_xmrig: Arc<Mutex<ImgXmrig>>, // A static "image" of the data XMRig started with
pub_api_p2pool: Arc<Mutex<PubP2poolApi>>, // P2Pool API state (for Helper/P2Pool thread)
pub_api_xmrig: Arc<Mutex<PubXmrigApi>>, // XMRig API state (for Helper/XMRig thread)
pub_api_xvb: Arc<Mutex<PubXvbApi>>, // XvB API state (for Helper/XvB thread)
pub_api_xp: Arc<Mutex<PubXmrigProxyApi>>, // XMRig-Proxy API state (for Helper/XMRig-Proxy thread)
pub_api_xvb: Arc<Mutex<PubXvbApi>>, // XvB API state (for Helper/XvB thread)
pub gupax_p2pool_api: Arc<Mutex<GupaxP2poolApi>>, //
}
@ -242,6 +249,7 @@ impl Default for ProcessSignal {
pub enum ProcessName {
P2pool,
Xmrig,
XmrigProxy,
Xvb,
}
@ -260,6 +268,7 @@ impl std::fmt::Display for ProcessName {
match *self {
ProcessName::P2pool => write!(f, "P2Pool"),
ProcessName::Xmrig => write!(f, "XMRig"),
ProcessName::XmrigProxy => write!(f, "XMRig-Proxy"),
ProcessName::Xvb => write!(f, "XvB"),
}
}
@ -274,10 +283,12 @@ impl Helper {
pub_sys: Arc<Mutex<Sys>>,
p2pool: Arc<Mutex<Process>>,
xmrig: Arc<Mutex<Process>>,
xmrig_proxy: Arc<Mutex<Process>>,
xvb: Arc<Mutex<Process>>,
gui_api_p2pool: Arc<Mutex<PubP2poolApi>>,
gui_api_xmrig: Arc<Mutex<PubXmrigApi>>,
gui_api_xvb: Arc<Mutex<PubXvbApi>>,
gui_api_xp: Arc<Mutex<PubXmrigProxyApi>>,
img_p2pool: Arc<Mutex<ImgP2pool>>,
img_xmrig: Arc<Mutex<ImgXmrig>>,
gupax_p2pool_api: Arc<Mutex<GupaxP2poolApi>>,
@ -288,14 +299,17 @@ impl Helper {
uptime: HumanTime::into_human(instant.elapsed()),
pub_api_p2pool: arc_mut!(PubP2poolApi::new()),
pub_api_xmrig: arc_mut!(PubXmrigApi::new()),
pub_api_xp: arc_mut!(PubXmrigProxyApi::new()),
pub_api_xvb: arc_mut!(PubXvbApi::new()),
// These are created when initializing [App], since it needs a handle to it as well
p2pool,
xmrig,
xmrig_proxy,
xvb,
gui_api_p2pool,
gui_api_xmrig,
gui_api_xvb,
gui_api_xp,
img_p2pool,
img_xmrig,
gupax_p2pool_api,
@ -407,13 +421,16 @@ impl Helper {
let lock = lock!(helper);
let p2pool = Arc::clone(&lock.p2pool);
let xmrig = Arc::clone(&lock.xmrig);
let xmrig_proxy = Arc::clone(&lock.xmrig_proxy);
let xvb = Arc::clone(&lock.xvb);
let pub_sys = Arc::clone(&lock.pub_sys);
let gui_api_p2pool = Arc::clone(&lock.gui_api_p2pool);
let gui_api_xmrig = Arc::clone(&lock.gui_api_xmrig);
let gui_api_xp = Arc::clone(&lock.gui_api_xp);
let gui_api_xvb = Arc::clone(&lock.gui_api_xvb);
let pub_api_p2pool = Arc::clone(&lock.pub_api_p2pool);
let pub_api_xmrig = Arc::clone(&lock.pub_api_xmrig);
let pub_api_xp = Arc::clone(&lock.pub_api_xp);
let pub_api_xvb = Arc::clone(&lock.pub_api_xvb);
drop(lock);
@ -434,27 +451,33 @@ impl Helper {
// 2. Lock... EVERYTHING!
let mut lock = lock!(helper);
debug!("Helper | Locking (1/11) ... [helper]");
debug!("Helper | Locking (1/12) ... [helper]");
let p2pool = lock!(p2pool);
debug!("Helper | Locking (2/11) ... [p2pool]");
debug!("Helper | Locking (2/12) ... [p2pool]");
let xmrig = lock!(xmrig);
debug!("Helper | Locking (3/11) ... [xmrig]");
debug!("Helper | Locking (3/12) ... [xmrig]");
let xmrig_proxy = lock!(xmrig_proxy);
debug!("Helper | Locking (3/12) ... [xmrig_proxy]");
let xvb = lock!(xvb);
debug!("Helper | Locking (4/11) ... [xvb]");
debug!("Helper | Locking (4/12) ... [xvb]");
let mut lock_pub_sys = lock!(pub_sys);
debug!("Helper | Locking (5/11) ... [pub_sys]");
debug!("Helper | Locking (5/12) ... [pub_sys]");
let mut gui_api_p2pool = lock!(gui_api_p2pool);
debug!("Helper | Locking (6/11) ... [gui_api_p2pool]");
debug!("Helper | Locking (6/12) ... [gui_api_p2pool]");
let mut gui_api_xmrig = lock!(gui_api_xmrig);
debug!("Helper | Locking (7/11) ... [gui_api_xmrig]");
debug!("Helper | Locking (7/12) ... [gui_api_xmrig]");
let mut gui_api_xp = lock!(gui_api_xp);
debug!("Helper | Locking (7/12) ... [gui_api_xp]");
let mut gui_api_xvb = lock!(gui_api_xvb);
debug!("Helper | Locking (8/11) ... [gui_api_xvb]");
debug!("Helper | Locking (8/12) ... [gui_api_xvb]");
let mut pub_api_p2pool = lock!(pub_api_p2pool);
debug!("Helper | Locking (9/11) ... [pub_api_p2pool]");
debug!("Helper | Locking (9/12) ... [pub_api_p2pool]");
let mut pub_api_xmrig = lock!(pub_api_xmrig);
debug!("Helper | Locking (10/11) ... [pub_api_xmrig]");
debug!("Helper | Locking (10/12) ... [pub_api_xmrig]");
let mut pub_api_xp = lock!(pub_api_xp);
debug!("Helper | Locking (11/12) ... [pub_api_xp]");
let mut pub_api_xvb = lock!(pub_api_xvb);
debug!("Helper | Locking (11/11) ... [pub_api_xvb]");
debug!("Helper | Locking (12/12) ... [pub_api_xvb]");
// Calculate Gupax's uptime always.
lock.uptime = HumanTime::into_human(lock.instant.elapsed());
// If [P2Pool] is alive...
@ -471,6 +494,13 @@ impl Helper {
} else {
debug!("Helper | XMRig is dead! Skipping...");
}
// If [XMRig-Proxy] is alive...
if xmrig_proxy.is_alive() {
debug!("Helper | XMRig-Proxy is alive! Running [combine_gui_pub_api()]");
PubXmrigProxyApi::combine_gui_pub_api(&mut gui_api_xp, &mut pub_api_xp);
} else {
debug!("Helper | XMRig-Proxy is dead! Skipping...");
}
// If [XvB] is alive...
if xvb.is_alive() {
debug!("Helper | XvB is alive! Running [combine_gui_pub_api()]");
@ -497,27 +527,33 @@ impl Helper {
// 3. Drop... (almost) EVERYTHING... IN REVERSE!
drop(lock_pub_sys);
debug!("Helper | Unlocking (1/11) ... [pub_sys]");
debug!("Helper | Unlocking (1/12) ... [pub_sys]");
drop(xvb);
debug!("Helper | Unlocking (2/11) ... [xvb]");
debug!("Helper | Unlocking (2/12) ... [xvb]");
drop(xmrig_proxy);
debug!("Helper | Unlocking (3/12) ... [xmrig_proxy]");
drop(xmrig);
debug!("Helper | Unlocking (3/11) ... [xmrig]");
debug!("Helper | Unlocking (3/12) ... [xmrig]");
drop(p2pool);
debug!("Helper | Unlocking (4/11) ... [p2pool]");
debug!("Helper | Unlocking (4/12) ... [p2pool]");
drop(pub_api_xvb);
debug!("Helper | Unlocking (5/11) ... [pub_api_xvb]");
debug!("Helper | Unlocking (5/12) ... [pub_api_xvb]");
drop(pub_api_xp);
debug!("Helper | Unlocking (6/12) ... [pub_api_xp]");
drop(pub_api_xmrig);
debug!("Helper | Unlocking (6/11) ... [pub_api_xmrig]");
debug!("Helper | Unlocking (6/12) ... [pub_api_xmrig]");
drop(pub_api_p2pool);
debug!("Helper | Unlocking (7/11) ... [pub_api_p2pool]");
debug!("Helper | Unlocking (7/12) ... [pub_api_p2pool]");
drop(gui_api_xvb);
debug!("Helper | Unlocking (8/11) ... [gui_api_xvb]");
debug!("Helper | Unlocking (8/12) ... [gui_api_xvb]");
drop(gui_api_xp);
debug!("Helper | Unlocking (9/12) ... [gui_api_xp]");
drop(gui_api_xmrig);
debug!("Helper | Unlocking (9/11) ... [gui_api_xmrig]");
debug!("Helper | Unlocking (10/12) ... [gui_api_xmrig]");
drop(gui_api_p2pool);
debug!("Helper | Unlocking (10/11) ... [gui_api_p2pool]");
debug!("Helper | Unlocking (11/12) ... [gui_api_p2pool]");
drop(lock);
debug!("Helper | Unlocking (11/11) ... [helper]");
debug!("Helper | Unlocking (12/12) ... [helper]");
// 4. Calculate if we should sleep or not.
// If we should sleep, how long?
@ -537,3 +573,200 @@ impl Helper {
});
}
}
// common functions inside watchdog thread
fn check_died(
child_pty: &Arc<Mutex<Box<dyn Child + Sync + Send>>>,
process: &mut Process,
start: &Instant,
gui_api_output_raw: &mut String,
) -> bool {
// Check if the process secretly died without us knowing :)
if let Ok(Some(code)) = lock!(child_pty).try_wait() {
debug!(
"{} Watchdog | Process secretly died on us! Getting exit status...",
process.name
);
let exit_status = match code.success() {
true => {
process.state = ProcessState::Dead;
"Successful"
}
false => {
process.state = ProcessState::Failed;
"Failed"
}
};
let uptime = Uptime::from(start.elapsed());
info!(
"{} | Stopped ... Uptime was: [{}], Exit status: [{}]",
process.name, uptime, exit_status
);
if let Err(e) = writeln!(
*gui_api_output_raw,
"{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
process.name, HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE
) {
error!(
"{} Watchdog | GUI Uptime/Exit status write failed: {}",
process.name, e
);
}
process.signal = ProcessSignal::None;
debug!(
"{} Watchdog | Secret dead process reap OK, breaking",
process.name
);
return true;
}
false
}
fn check_user_input(process: &Arc<Mutex<Process>>, stdin: &mut Box<dyn std::io::Write + Send>) {
let mut lock = lock!(process);
if !lock.input.is_empty() {
let input = std::mem::take(&mut lock.input);
for line in input {
if line.is_empty() {
continue;
}
debug!(
"{} Watchdog | User input not empty, writing to STDIN: [{}]",
lock.name, line
);
#[cfg(target_os = "windows")]
if let Err(e) = write!(stdin, "{}\r\n", line) {
error!("{} Watchdog | STDIN error: {}", lock.name, e);
}
#[cfg(target_family = "unix")]
if let Err(e) = writeln!(stdin, "{}", line) {
error!("{} Watchdog | STDIN error: {}", lock.name, e);
}
// Flush.
if let Err(e) = stdin.flush() {
error!("{} Watchdog | STDIN flush error: {}", lock.name, e);
}
}
}
}
fn signal_end(
process: &Arc<Mutex<Process>>,
child_pty: &Arc<Mutex<Box<dyn Child + Sync + Send>>>,
start: &Instant,
gui_api_output_raw: &mut String,
) -> bool {
if lock!(process).signal == ProcessSignal::Stop {
debug!("{} Watchdog | Stop SIGNAL caught", lock!(process).name);
// This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool)
if let Err(e) = lock!(child_pty).kill() {
error!("{} Watchdog | Kill error: {}", lock!(process).name, e);
}
// Wait to get the exit status
let exit_status = match lock!(child_pty).wait() {
Ok(e) => {
if e.success() {
lock!(process).state = ProcessState::Dead;
"Successful"
} else {
lock!(process).state = ProcessState::Failed;
"Failed"
}
}
_ => {
lock!(process).state = ProcessState::Failed;
"Unknown Error"
}
};
let uptime = HumanTime::into_human(start.elapsed());
info!(
"{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]",
lock!(process).name,
uptime,
exit_status
);
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
if let Err(e) = writeln!(
gui_api_output_raw,
"{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
lock!(process).name,
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) {
error!(
"{} Watchdog | GUI Uptime/Exit status write failed: {}",
lock!(process).name,
e
);
}
lock!(process).signal = ProcessSignal::None;
debug!(
"{} Watchdog | Stop SIGNAL done, breaking",
lock!(process).name,
);
return true;
// Check RESTART
} else if lock!(process).signal == ProcessSignal::Restart {
debug!("{} Watchdog | Restart SIGNAL caught", lock!(process).name,);
// This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool)
if let Err(e) = lock!(child_pty).kill() {
error!("{} Watchdog | Kill error: {}", lock!(process).name, e);
}
// Wait to get the exit status
let exit_status = match lock!(child_pty).wait() {
Ok(e) => {
if e.success() {
"Successful"
} else {
"Failed"
}
}
_ => "Unknown Error",
};
let uptime = HumanTime::into_human(start.elapsed());
info!(
"{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]",
lock!(process).name,
uptime,
exit_status
);
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
if let Err(e) = writeln!(
gui_api_output_raw,
"{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
lock!(process).name,
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) {
error!(
"{} Watchdog | GUI Uptime/Exit status write failed: {}",
lock!(process).name,
e
);
}
lock!(process).state = ProcessState::Waiting;
debug!(
"{} Watchdog | Restart SIGNAL done, breaking",
lock!(process).name,
);
return true;
}
false
}
async fn sleep_end_loop(now: Instant, name: ProcessName) {
// Sleep (only if 999ms hasn't passed)
let elapsed = now.elapsed().as_millis();
// Since logic goes off if less than 1000, casting should be safe
if elapsed < 999 {
let sleep = (999 - elapsed) as u64;
debug!(
"{} Watchdog | END OF LOOP - Sleeping for [{}]ms...",
name, sleep
);
tokio::time::sleep(Duration::from_millis(sleep)).await;
} else {
debug!("{} Watchdog | END OF LOOP - Not sleeping!", name);
}
}

View file

@ -2,6 +2,10 @@ use super::Helper;
use super::Process;
use crate::components::node::RemoteNode;
use crate::disk::state::P2pool;
use crate::helper::check_died;
use crate::helper::check_user_input;
use crate::helper::signal_end;
use crate::helper::sleep_end_loop;
use crate::helper::ProcessName;
use crate::helper::ProcessSignal;
use crate::helper::ProcessState;
@ -213,7 +217,6 @@ impl Helper {
);
});
}
// Takes in a 95-char Monero address, returns the first and last
// 8 characters separated with dots like so: [4abcdefg...abcdefgh]
pub fn head_tail_of_monero_address(address: &str) -> String {
@ -394,6 +397,7 @@ impl Helper {
#[cold]
#[inline(never)]
// The P2Pool watchdog. Spawns 1 OS thread for reading a PTY (STDOUT+STDERR), and combines the [Child] with a PTY so STDIN actually works.
// or if P2Pool simple is false and extern is true, only prints data from stratum api.
#[allow(clippy::too_many_arguments)]
#[allow(clippy::await_holding_lock)]
#[tokio::main]
@ -496,165 +500,21 @@ impl Helper {
lock!(gui_api).tick_status += 1;
// Check if the process is secretly died without us knowing :)
if let Ok(Some(code)) = lock!(child_pty).try_wait() {
debug!("P2Pool Watchdog | Process secretly died! Getting exit status");
let exit_status = match code.success() {
true => {
lock!(process).state = ProcessState::Dead;
"Successful"
}
false => {
lock!(process).state = ProcessState::Failed;
"Failed"
}
};
let uptime = HumanTime::into_human(start.elapsed());
info!(
"P2Pool Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]",
uptime, exit_status
);
// This is written directly into the GUI, because sometimes the 900ms event loop can't catch it.
if let Err(e) = writeln!(
lock!(gui_api).output,
"{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) {
error!(
"P2Pool Watchdog | GUI Uptime/Exit status write failed: {}",
e
);
}
lock!(process).signal = ProcessSignal::None;
debug!("P2Pool Watchdog | Secret dead process reap OK, breaking");
if check_died(
&child_pty,
&mut lock!(process),
&start,
&mut lock!(gui_api).output,
) {
break;
}
// Check SIGNAL
if lock!(process).signal == ProcessSignal::Stop {
debug!("P2Pool Watchdog | Stop SIGNAL caught");
// This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool)
if let Err(e) = lock!(child_pty).kill() {
error!("P2Pool Watchdog | Kill error: {}", e);
}
// Wait to get the exit status
let exit_status = match lock!(child_pty).wait() {
Ok(e) => {
if e.success() {
lock!(process).state = ProcessState::Dead;
"Successful"
} else {
lock!(process).state = ProcessState::Failed;
"Failed"
}
}
_ => {
lock!(process).state = ProcessState::Failed;
"Unknown Error"
}
};
let uptime = HumanTime::into_human(start.elapsed());
info!(
"P2Pool Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]",
uptime, exit_status
);
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
if let Err(e) = writeln!(
lock!(gui_api).output,
"{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) {
error!(
"P2Pool Watchdog | GUI Uptime/Exit status write failed: {}",
e
);
}
lock!(process).signal = ProcessSignal::None;
debug!("P2Pool Watchdog | Stop SIGNAL done, breaking");
break;
// Check RESTART
} else if lock!(process).signal == ProcessSignal::Restart {
debug!("P2Pool Watchdog | Restart SIGNAL caught");
// This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool)
if let Err(e) = lock!(child_pty).kill() {
error!("P2Pool Watchdog | Kill error: {}", e);
}
// Wait to get the exit status
let exit_status = match lock!(child_pty).wait() {
Ok(e) => {
if e.success() {
"Successful"
} else {
"Failed"
}
}
_ => "Unknown Error",
};
let uptime = HumanTime::into_human(start.elapsed());
info!(
"P2Pool Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]",
uptime, exit_status
);
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
if let Err(e) = writeln!(
lock!(gui_api).output,
"{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) {
error!(
"P2Pool Watchdog | GUI Uptime/Exit status write failed: {}",
e
);
}
lock!(process).state = ProcessState::Waiting;
debug!("P2Pool Watchdog | Restart SIGNAL done, breaking");
if signal_end(&process, &child_pty, &start, &mut lock!(gui_api).output) {
break;
}
// Check vector of user input
let mut lock = lock!(process);
if !lock.input.is_empty() {
let input = std::mem::take(&mut lock.input);
for line in input {
if line.is_empty() {
continue;
}
debug!(
"P2Pool Watchdog | User input not empty, writing to STDIN: [{}]",
line
);
// Windows terminals (or at least the PTY abstraction I'm using, portable_pty)
// requires a [\r\n] to end a line, whereas Unix is okay with just a [\n].
//
// I have literally read all of [portable_pty]'s source code, dug into Win32 APIs,
// even rewrote some of the actual PTY code in order to understand why STDIN doesn't work
// on Windows. It's because of a fucking missing [\r]. Another reason to hate Windows :D
//
// XMRig did actually work before though, since it reads STDIN directly without needing a newline.
#[cfg(target_os = "windows")]
if let Err(e) = write!(stdin, "{}\r\n", line) {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
#[cfg(target_family = "unix")]
if let Err(e) = writeln!(stdin, "{}", line) {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
// Flush.
if let Err(e) = stdin.flush() {
error!("P2Pool Watchdog | STDIN flush error: {}", e);
}
}
}
drop(lock);
check_user_input(&process, &mut stdin);
// Check if logs need resetting
debug!("P2Pool Watchdog | Attempting GUI log reset check");
let mut lock = lock!(gui_api);
@ -716,25 +576,14 @@ impl Helper {
}
// Sleep (only if 900ms hasn't passed)
let elapsed = now.elapsed().as_millis();
if first_loop {
first_loop = false;
}
// Since logic goes off if less than 1000, casting should be safe
if elapsed < 900 {
let sleep = (900 - elapsed) as u64;
debug!(
"P2Pool Watchdog | END OF LOOP - Tick: [{}/60] - Sleeping for [{}]ms...",
lock!(gui_api).tick,
sleep
);
tokio::time::sleep(Duration::from_millis(sleep)).await;
} else {
debug!(
"P2Pool Watchdog | END OF LOOP - Tick: [{}/60] Not sleeping!",
lock!(gui_api).tick
);
}
sleep_end_loop(now, ProcessName::P2pool).await;
debug!(
"P2Pool Watchdog | END OF LOOP - Tick: [{}/60]",
lock!(gui_api).tick,
);
}
// 5. If loop broke, we must be done here.

View file

@ -475,7 +475,7 @@ Uptime = 0h 2m 4s
},
"hugepages": true
}"#;
use crate::helper::xmrig::PrivXmrigApi;
use crate::helper::xrig::xmrig::PrivXmrigApi;
let priv_api = serde_json::from_str::<PrivXmrigApi>(data).unwrap();
let json = serde_json::ser::to_string_pretty(&priv_api).unwrap();
println!("{}", json);
@ -512,7 +512,7 @@ Uptime = 0h 2m 4s
use crate::{
disk::state::P2pool,
helper::{p2pool::PubP2poolApi, xmrig::PubXmrigApi, xvb::rounds::XvbRound},
helper::{p2pool::PubP2poolApi, xrig::xmrig::PubXmrigApi, xvb::rounds::XvbRound},
macros::lock,
XVB_TIME_ALGO,
};

63
src/helper/xrig/mod.rs Normal file
View file

@ -0,0 +1,63 @@
use crate::helper::XvbNode;
use anyhow::anyhow;
use anyhow::Result;
use log::info;
use reqwest::header::AUTHORIZATION;
use reqwest::Client;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
pub mod xmrig;
pub mod xmrig_proxy;
// update config of xmrig or xmrig-proxy
pub async fn update_xmrig_config(
client: &Client,
api_uri: &str,
token: &str,
node: &XvbNode,
address: &str,
rig: &str,
) -> Result<()> {
// get config
let request = client
.get(api_uri)
.header(AUTHORIZATION, ["Bearer ", token].concat());
let mut config = request.send().await?.json::<Value>().await?;
// modify node configuration
let uri = [node.url(), ":".to_string(), node.port()].concat();
info!("replace xmrig config with node {}", uri);
*config
.pointer_mut("/pools/0/url")
.ok_or_else(|| anyhow!("pools/0/url does not exist in xmrig config"))? = uri.into();
*config
.pointer_mut("/pools/0/user")
.ok_or_else(|| anyhow!("pools/0/user does not exist in xmrig config"))? = node
.user(&address.chars().take(8).collect::<String>())
.into();
*config
.pointer_mut("/pools/0/rig-id")
.ok_or_else(|| anyhow!("pools/0/rig-id does not exist in xmrig config"))? = rig.into();
*config
.pointer_mut("/pools/0/tls")
.ok_or_else(|| anyhow!("pools/0/tls does not exist in xmrig config"))? = node.tls().into();
*config
.pointer_mut("/pools/0/keepalive")
.ok_or_else(|| anyhow!("pools/0/keepalive does not exist in xmrig config"))? =
node.keepalive().into();
// send new config
client
.put(api_uri)
.header("Authorization", ["Bearer ", token].concat())
.header("Content-Type", "application/json")
.timeout(std::time::Duration::from_secs(5))
.body(config.to_string())
.send()
.await?;
anyhow::Ok(())
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
struct Hashrate {
total: [Option<f32>; 3],
}

View file

@ -1,17 +1,20 @@
use crate::helper::{ProcessName, ProcessSignal, ProcessState};
use crate::helper::xrig::update_xmrig_config;
use crate::helper::{check_died, check_user_input, sleep_end_loop, Process};
use crate::helper::{Helper, ProcessName, ProcessSignal, ProcessState};
use crate::helper::{PubXvbApi, XvbNode};
use crate::miscs::output_console;
use crate::regex::{contains_error, contains_usepool, detect_new_node_xmrig, XMRIG_REGEX};
use crate::utils::human::HumanNumber;
use crate::utils::sudo::SudoState;
use crate::{constants::*, macros::*};
use anyhow::{anyhow, Result};
use enclose::enclose;
use log::*;
use portable_pty::Child;
use readable::num::Unsigned;
use readable::up::Uptime;
use reqwest::header::AUTHORIZATION;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::path::Path;
use std::{
fmt::Write,
@ -23,17 +26,17 @@ use std::{
};
use tokio::spawn;
use super::xvb::nodes::XvbNode;
use super::xvb::PubXvbApi;
use super::{Helper, Process};
use super::Hashrate;
impl Helper {
#[cold]
#[inline(never)]
async fn read_pty_xmrig(
pub async fn read_pty_xmrig(
output_parse: Arc<Mutex<String>>,
output_pub: Arc<Mutex<String>>,
reader: Box<dyn std::io::Read + Send>,
process_xvb: Arc<Mutex<Process>>,
process_xp: Arc<Mutex<Process>>,
pub_api_xvb: &Arc<Mutex<PubXvbApi>>,
) {
use std::io::BufRead;
@ -59,27 +62,30 @@ impl Helper {
while let Some(Ok(line)) = stdout.next() {
// need to verify if node still working
// for that need to catch "connect error"
if contains_error(&line) {
let current_node = lock!(pub_api_xvb).current_node;
if let Some(current_node) = current_node {
// updating current node to None, will stop sending signal of FailedNode until new node is set
// send signal to update node.
warn!("XMRig PTY Parse | node is offline, sending signal to update nodes.");
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(current_node);
lock!(pub_api_xvb).current_node = None;
// only check if xvb process is used and xmrig-proxy is not.
if lock!(process_xvb).is_alive() && !lock!(process_xp).is_alive() {
if contains_error(&line) {
let current_node = lock!(pub_api_xvb).current_node;
if let Some(current_node) = current_node {
// updating current node to None, will stop sending signal of FailedNode until new node is set
// send signal to update node.
warn!("XMRig PTY Parse | node is offline, sending signal to update nodes.");
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(current_node);
lock!(pub_api_xvb).current_node = None;
}
}
}
if contains_usepool(&line) {
info!("XMRig PTY Parse | new pool detected");
// need to update current node because it was updated.
// if custom node made by user, it is not supported because algo is deciding which node to use.
let node = detect_new_node_xmrig(&line);
if node.is_none() {
error!("XMRig PTY Parse | node is not understood, switching to backup.");
// update with default will choose which XvB to prefer. Will update XvB to use p2pool.
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(XvbNode::default());
if contains_usepool(&line) {
info!("XMRig PTY Parse | new pool detected");
// need to update current node because it was updated.
// if custom node made by user, it is not supported because algo is deciding which node to use.
let node = detect_new_node_xmrig(&line);
if node.is_none() {
error!("XMRig PTY Parse | node is not understood, switching to backup.");
// update with default will choose which XvB to prefer. Will update XvB to use p2pool.
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(XvbNode::default());
}
lock!(pub_api_xvb).current_node = node;
}
lock!(pub_api_xvb).current_node = node;
}
// println!("{}", line); // For debugging.
if let Err(e) = writeln!(lock!(output_parse), "{}", line) {
@ -178,6 +184,7 @@ impl Helper {
let gui_api = Arc::clone(&lock!(helper).gui_api_xmrig);
let pub_api = Arc::clone(&lock!(helper).pub_api_xmrig);
let process_xvb = Arc::clone(&lock!(helper).xvb);
let process_xp = Arc::clone(&lock!(helper).xmrig_proxy);
let path = path.to_path_buf();
let token = state.token.clone();
let img_xmrig = Arc::clone(&lock!(helper).img_xmrig);
@ -193,6 +200,7 @@ impl Helper {
api_ip_port,
&token,
process_xvb,
process_xp,
&img_xmrig,
&pub_api_xvb,
);
@ -374,6 +382,7 @@ impl Helper {
mut api_ip_port: String,
token: &str,
process_xvb: Arc<Mutex<Process>>,
process_xp: Arc<Mutex<Process>>,
img_xmrig: &Arc<Mutex<ImgXmrig>>,
pub_api_xvb: &Arc<Mutex<PubXvbApi>>,
) {
@ -393,8 +402,8 @@ impl Helper {
let reader = pair.master.try_clone_reader().unwrap(); // Get STDOUT/STDERR before moving the PTY
let output_parse = Arc::clone(&lock!(process).output_parse);
let output_pub = Arc::clone(&lock!(process).output_pub);
spawn(enclose!((pub_api_xvb) async move {
Self::read_pty_xmrig(output_parse, output_pub, reader, process_xvb, &pub_api_xvb).await;
spawn(enclose!((pub_api_xvb, process_xp) async move {
Self::read_pty_xmrig(output_parse, output_pub, reader, process_xvb, process_xp, &pub_api_xvb).await;
}));
// 1b. Create command
debug!("XMRig | Creating command...");
@ -449,7 +458,7 @@ impl Helper {
if !api_ip_port.ends_with('/') {
api_ip_port.push('/');
}
"http://".to_owned() + &api_ip_port + XMRIG_API_URI
"http://".to_owned() + &api_ip_port + XMRIG_API_SUMMARY_URI
};
info!("XMRig | Final API URI: {}", api_uri);
@ -462,156 +471,48 @@ impl Helper {
info!("XMRig | Entering watchdog mode... woof!");
// needs xmrig to be in belownormal priority or else Gupaxx will be in trouble if it does not have enough cpu time.
#[cfg(target_os = "windows")]
std::process::Command::new("cmd")
.args(["/c", "/q", "wmic"])
.args([
"process",
"where",
"name='xmrig.exe'",
"CALL",
"setpriority",
"below normal",
])
.spawn()
.expect("failure to execute command wmic");
{
use std::os::windows::process::CommandExt;
std::process::Command::new("cmd")
.creation_flags(0x08000000)
.args(["/c", "wmic"])
.args([
"process",
"where",
"name='xmrig.exe'",
"CALL",
"setpriority",
"below normal",
])
.spawn()
.expect("failure to execute command wmic");
}
loop {
// Set timer
let now = Instant::now();
debug!("XMRig Watchdog | ----------- Start of loop -----------");
// Check if the process secretly died without us knowing :)
if let Ok(Some(code)) = lock!(child_pty).try_wait() {
debug!("XMRig Watchdog | Process secretly died on us! Getting exit status...");
let exit_status = match code.success() {
true => {
lock!(process).state = ProcessState::Dead;
"Successful"
}
false => {
lock!(process).state = ProcessState::Failed;
"Failed"
}
};
let uptime = Uptime::from(start.elapsed());
info!(
"XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]",
uptime, exit_status
);
if let Err(e) = writeln!(
lock!(gui_api).output,
"{}\nXMRig stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) {
error!(
"XMRig Watchdog | GUI Uptime/Exit status write failed: {}",
e
);
}
lock!(process).signal = ProcessSignal::None;
debug!("XMRig Watchdog | Secret dead process reap OK, breaking");
if check_died(
&child_pty,
&mut lock!(process),
&start,
&mut lock!(gui_api).output,
) {
break;
}
// Stop on [Stop/Restart] SIGNAL
let signal = lock!(process).signal;
if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart {
debug!("XMRig Watchdog | Stop/Restart SIGNAL caught");
// macOS requires [sudo] again to kill [XMRig]
if cfg!(target_os = "macos") {
// If we're at this point, that means the user has
// entered their [sudo] pass again, after we wiped it.
// So, we should be able to find it in our [Arc<Mutex<SudoState>>].
Self::sudo_kill(lock!(child_pty).process_id().unwrap(), &sudo);
// And... wipe it again (only if we're stopping full).
// If we're restarting, the next start will wipe it for us.
if signal != ProcessSignal::Restart {
SudoState::wipe(&sudo);
}
} else if let Err(e) = lock!(child_pty).kill() {
error!("XMRig Watchdog | Kill error: {}", e);
}
let exit_status = match lock!(child_pty).wait() {
Ok(e) => {
let mut process = lock!(process);
if e.success() {
if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Dead;
}
"Successful"
} else {
if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Failed;
}
"Failed"
}
}
_ => {
let mut process = lock!(process);
if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Failed;
}
"Unknown Error"
}
};
let uptime = Uptime::from(start.elapsed());
info!(
"XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]",
uptime, exit_status
);
if let Err(e) = writeln!(
lock!(gui_api).output,
"{}\nXMRig stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) {
error!(
"XMRig Watchdog | GUI Uptime/Exit status write failed: {}",
e
);
}
let mut process = lock!(process);
match process.signal {
ProcessSignal::Stop => process.signal = ProcessSignal::None,
ProcessSignal::Restart => process.state = ProcessState::Waiting,
_ => (),
}
debug!("XMRig Watchdog | Stop/Restart SIGNAL done, breaking");
if Self::xmrig_signal_end(
&process,
&child_pty,
&start,
&mut lock!(gui_api).output,
&sudo,
) {
break;
}
// Check vector of user input
{
let mut lock = lock!(process);
if !lock.input.is_empty() {
let input = std::mem::take(&mut lock.input);
for line in input {
if line.is_empty() {
continue;
}
debug!(
"XMRig Watchdog | User input not empty, writing to STDIN: [{}]",
line
);
#[cfg(target_os = "windows")]
if let Err(e) = write!(stdin, "{}\r\n", line) {
error!("XMRig Watchdog | STDIN error: {}", e);
}
#[cfg(target_family = "unix")]
if let Err(e) = writeln!(stdin, "{}", line) {
error!("XMRig Watchdog | STDIN error: {}", e);
}
// Flush.
if let Err(e) = stdin.flush() {
error!("XMRig Watchdog | STDIN flush error: {}", e);
}
}
}
}
check_user_input(&process, &mut stdin);
// Check if logs need resetting
debug!("XMRig Watchdog | Attempting GUI log reset check");
{
@ -641,25 +542,116 @@ impl Helper {
);
}
}
// Sleep (only if 900ms hasn't passed)
let elapsed = now.elapsed().as_millis();
// Since logic goes off if less than 1000, casting should be safe
if elapsed < 900 {
let sleep = (900 - elapsed) as u64;
debug!(
"XMRig Watchdog | END OF LOOP - Sleeping for [{}]ms...",
sleep
);
tokio::time::sleep(Duration::from_millis(sleep)).await;
} else {
debug!("XMRig Watchdog | END OF LOOP - Not sleeping!");
// if mining on proxy and proxy is not alive, switch back to p2pool node
if lock!(gui_api).node == XvbNode::XmrigProxy.to_string()
&& !lock!(process_xp).is_alive()
{
info!("XMRig Process | redirect xmrig to p2pool since XMRig-Proxy is not alive anymore");
if let Err(err) = update_xmrig_config(
&client,
XMRIG_CONFIG_URL,
token,
&XvbNode::P2pool,
"",
GUPAX_VERSION_UNDERSCORE,
)
.await
{
// show to console error about updating xmrig config
warn!("XMRig Process | Failed request HTTP API Xmrig");
output_console(
&mut lock!(gui_api).output,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
ProcessName::Xmrig,
);
} else {
lock!(gui_api).node = XvbNode::P2pool.to_string();
debug!("XMRig Process | mining on P2Pool pool");
}
}
// Sleep (only if 900ms hasn't passed)
sleep_end_loop(now, ProcessName::Xmrig).await;
}
// 5. If loop broke, we must be done here.
info!("XMRig Watchdog | Watchdog thread exiting... Goodbye!");
}
fn xmrig_signal_end(
process: &Arc<Mutex<Process>>,
child_pty: &Arc<Mutex<Box<dyn Child + Sync + Send>>>,
start: &Instant,
gui_api_output_raw: &mut String,
sudo: &Arc<Mutex<SudoState>>,
) -> bool {
let signal = lock!(process).signal;
if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart {
debug!("XMRig Watchdog | Stop/Restart SIGNAL caught");
// macOS requires [sudo] again to kill [XMRig]
if cfg!(target_os = "macos") {
// If we're at this point, that means the user has
// entered their [sudo] pass again, after we wiped it.
// So, we should be able to find it in our [Arc<Mutex<SudoState>>].
Self::sudo_kill(lock!(child_pty).process_id().unwrap(), sudo);
// And... wipe it again (only if we're stopping full).
// If we're restarting, the next start will wipe it for us.
if signal != ProcessSignal::Restart {
SudoState::wipe(sudo);
}
} else if let Err(e) = lock!(child_pty).kill() {
error!("XMRig Watchdog | Kill error: {}", e);
}
let exit_status = match lock!(child_pty).wait() {
Ok(e) => {
let mut process = lock!(process);
if e.success() {
if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Dead;
}
"Successful"
} else {
if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Failed;
}
"Failed"
}
}
_ => {
let mut process = lock!(process);
if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Failed;
}
"Unknown Error"
}
};
let uptime = Uptime::from(start.elapsed());
info!(
"XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]",
uptime, exit_status
);
if let Err(e) = writeln!(
gui_api_output_raw,
"{}\nXMRig stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE
) {
error!(
"XMRig Watchdog | GUI Uptime/Exit status write failed: {}",
e
);
}
let mut process = lock!(process);
match process.signal {
ProcessSignal::Stop => process.signal = ProcessSignal::None,
ProcessSignal::Restart => process.state = ProcessState::Waiting,
_ => (),
}
debug!("XMRig Watchdog | Stop/Restart SIGNAL done, breaking");
return true;
}
false
}
}
//---------------------------------------------------------------------------------------------------- [ImgXmrig]
#[derive(Debug, Clone)]
@ -726,7 +718,7 @@ impl PubXmrigApi {
}
#[inline]
pub(super) fn combine_gui_pub_api(gui_api: &mut Self, pub_api: &mut Self) {
pub fn combine_gui_pub_api(gui_api: &mut Self, pub_api: &mut Self) {
let output = std::mem::take(&mut gui_api.output);
let node = std::mem::take(&mut gui_api.node);
let buf = std::mem::take(&mut pub_api.output);
@ -742,7 +734,7 @@ impl PubXmrigApi {
// This combines the buffer from the PTY thread [output_pub]
// with the actual [PubApiXmrig] output field.
pub(super) fn update_from_output(
pub fn update_from_output(
public: &Arc<Mutex<Self>>,
output_parse: &Arc<Mutex<String>>,
output_pub: &Arc<Mutex<String>>,
@ -811,7 +803,7 @@ impl PubXmrigApi {
// XMRig doesn't initialize stats at 0 (or 0.0) and instead opts for [null]
// which means some elements need to be wrapped in an [Option] or else serde will [panic!].
#[derive(Debug, Serialize, Deserialize, Clone)]
pub(super) struct PrivXmrigApi {
pub struct PrivXmrigApi {
worker_id: String,
resources: Resources,
connection: Connection,
@ -836,57 +828,6 @@ impl PrivXmrigApi {
.json()
.await?)
}
#[inline]
// // Replace config with new node
pub async fn update_xmrig_config(
client: &Client,
api_uri: &str,
token: &str,
node: &XvbNode,
address: &str,
pub_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
rig: &str,
) -> Result<()> {
// get config
let request = client
.get(api_uri)
.header(AUTHORIZATION, ["Bearer ", token].concat());
let mut config = request.send().await?.json::<Value>().await?;
// modify node configuration
let uri = [node.url(), ":".to_string(), node.port()].concat();
info!("replace xmrig config with node {}", uri);
*config
.pointer_mut("/pools/0/url")
.ok_or_else(|| anyhow!("pools/0/url does not exist in xmrig config"))? = uri.into();
*config
.pointer_mut("/pools/0/user")
.ok_or_else(|| anyhow!("pools/0/user does not exist in xmrig config"))? = node
.user(&address.chars().take(8).collect::<String>())
.into();
*config
.pointer_mut("/pools/0/rig-id")
.ok_or_else(|| anyhow!("pools/0/rig-id does not exist in xmrig config"))? = rig.into();
*config
.pointer_mut("/pools/0/tls")
.ok_or_else(|| anyhow!("pools/0/tls does not exist in xmrig config"))? =
node.tls().into();
*config
.pointer_mut("/pools/0/keepalive")
.ok_or_else(|| anyhow!("pools/0/keepalive does not exist in xmrig config"))? =
node.keepalive().into();
// send new config
client
.put(api_uri)
.header("Authorization", ["Bearer ", token].concat())
.header("Content-Type", "application/json")
.timeout(std::time::Duration::from_secs(5))
.body(config.to_string())
.send()
.await?;
// update process status
lock!(pub_api_xmrig).node = node.to_string();
anyhow::Ok(())
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
@ -900,8 +841,3 @@ struct Connection {
accepted: u128,
rejected: u128,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
struct Hashrate {
total: [Option<f32>; 3],
}

View file

@ -0,0 +1,593 @@
use enclose::enc;
use log::{debug, error, info, warn};
use reqwest::{header::AUTHORIZATION, Client};
use serde::{Deserialize, Serialize};
use std::fmt::Write;
use std::{
path::Path,
sync::{Arc, Mutex},
thread,
time::{Duration, Instant},
};
use tokio::spawn;
use crate::{
disk::state::Xmrig,
helper::{
check_died, check_user_input, signal_end, sleep_end_loop,
xrig::update_xmrig_config,
xvb::{nodes::XvbNode, PubXvbApi},
Helper, Process, ProcessName, ProcessSignal, ProcessState,
},
macros::{arc_mut, lock, lock2, sleep},
miscs::output_console,
regex::{contains_timeout, contains_usepool, detect_new_node_xmrig, XMRIG_REGEX},
GUPAX_VERSION_UNDERSCORE, UNKNOWN_DATA,
};
use crate::{XMRIG_CONFIG_URL, XMRIG_PROXY_SUMMARY_URL};
use super::xmrig::PubXmrigApi;
impl Helper {
// Takes in some [State/XmrigProxy] and parses it to build the actual command arguments.
// Returns the [Vec] of actual arguments,
#[cold]
#[inline(never)]
pub async fn read_pty_xp(
output_parse: Arc<Mutex<String>>,
output_pub: Arc<Mutex<String>>,
reader: Box<dyn std::io::Read + Send>,
process_xvb: Arc<Mutex<Process>>,
pub_api_xvb: &Arc<Mutex<PubXvbApi>>,
) {
use std::io::BufRead;
let mut stdout = std::io::BufReader::new(reader).lines();
// Run a ANSI escape sequence filter for the first few lines.
let mut i = 0;
while let Some(Ok(line)) = stdout.next() {
let line = strip_ansi_escapes::strip_str(line);
if let Err(e) = writeln!(lock!(output_parse), "{}", line) {
error!("XMRig-Proxy PTY Parse | Output error: {}", e);
}
if let Err(e) = writeln!(lock!(output_pub), "{}", line) {
error!("XMRig-Proxy PTY Pub | Output error: {}", e);
}
if i > 7 {
break;
} else {
i += 1;
}
}
while let Some(Ok(line)) = stdout.next() {
// need to verify if node still working
// for that need to catch "connect error"
// only switch nodes of XvB if XvB process is used
if lock!(process_xvb).is_alive() {
if contains_timeout(&line) {
let current_node = lock!(pub_api_xvb).current_node;
if let Some(current_node) = current_node {
// updating current node to None, will stop sending signal of FailedNode until new node is set
// send signal to update node.
warn!(
"XMRig-Proxy PTY Parse | node is offline, sending signal to update nodes."
);
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(current_node);
lock!(pub_api_xvb).current_node = None;
}
}
if contains_usepool(&line) {
info!("XMRig-Proxy PTY Parse | new pool detected");
// need to update current node because it was updated.
// if custom node made by user, it is not supported because algo is deciding which node to use.
let node = detect_new_node_xmrig(&line);
if node.is_none() {
warn!(
"XMRig-Proxy PTY Parse | node is not understood, switching to backup."
);
// update with default will choose which XvB to prefer. Will update XvB to use p2pool.
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(XvbNode::default());
}
lock!(pub_api_xvb).current_node = node;
}
}
// println!("{}", line); // For debugging.
if let Err(e) = writeln!(lock!(output_parse), "{}", line) {
error!("XMRig-Proxy PTY Parse | Output error: {}", e);
}
if let Err(e) = writeln!(lock!(output_pub), "{}", line) {
error!("XMRig-Proxy PTY Pub | Output error: {}", e);
}
}
}
pub fn build_xp_args(
helper: &Arc<Mutex<Self>>,
state: &crate::disk::state::XmrigProxy,
) -> Vec<String> {
let mut args = Vec::with_capacity(500);
let api_ip;
let api_port;
let ip;
let port;
// [Simple]
if state.simple {
// Build the xmrig argument
let rig = if state.simple_rig.is_empty() {
GUPAX_VERSION_UNDERSCORE.to_string()
} else {
state.simple_rig.clone()
}; // Rig name
args.push("-o".to_string());
args.push("127.0.0.1:3333".to_string()); // Local P2Pool (the default)
args.push("-b".to_string());
args.push("0.0.0.0:3355".to_string());
args.push("--user".to_string());
args.push(rig); // Rig name
args.push("--no-color".to_string()); // No color
args.push("--http-host".to_string());
args.push("127.0.0.1".to_string()); // HTTP API IP
args.push("--http-port".to_string());
args.push("18089".to_string()); // HTTP API Port
lock2!(helper, pub_api_xp).node = "127.0.0.1:3333 (Local P2Pool)".to_string();
// [Advanced]
} else {
// XMRig doesn't understand [localhost]
let p2pool_ip = if state.p2pool_ip == "localhost" || state.p2pool_ip.is_empty() {
"127.0.0.1"
} else {
&state.p2pool_ip
};
api_ip = if state.api_ip == "localhost" || state.api_ip.is_empty() {
"127.0.0.1".to_string()
} else {
state.api_ip.to_string()
};
api_port = if state.api_port.is_empty() {
"18089".to_string()
} else {
state.api_port.to_string()
};
ip = if state.api_ip == "localhost" || state.ip.is_empty() {
"0.0.0.0".to_string()
} else {
state.ip.to_string()
};
port = if state.port.is_empty() {
"3355".to_string()
} else {
state.port.to_string()
};
let p2pool_url = format!("{}:{}", p2pool_ip, state.p2pool_port); // Combine IP:Port into one string
let bind_url = format!("{}:{}", ip, port); // Combine IP:Port into one string
args.push("--user".to_string());
args.push(state.address.clone()); // Wallet
args.push("--rig-id".to_string());
args.push(state.rig.to_string()); // Rig ID
args.push("-o".to_string());
args.push(p2pool_url.clone()); // IP/Port
args.push("-b".to_string());
args.push(bind_url.clone()); // IP/Port
args.push("--http-host".to_string());
args.push(api_ip.to_string()); // HTTP API IP
args.push("--http-port".to_string());
args.push(api_port.to_string()); // HTTP API Port
args.push("--no-color".to_string()); // No color escape codes
if state.tls {
args.push("--tls".to_string());
} // TLS
if state.keepalive {
args.push("--keepalive".to_string());
} // Keepalive
lock2!(helper, pub_api_xp).node = p2pool_url;
}
args.push(format!("--http-access-token={}", state.token)); // HTTP API Port
args.push("--http-no-restricted".to_string());
args
}
pub fn stop_xp(helper: &Arc<Mutex<Self>>) {
info!("XMRig-Proxy | Attempting to stop...");
lock2!(helper, xmrig_proxy).signal = ProcessSignal::Stop;
info!("locked signal ok");
lock2!(helper, xmrig_proxy).state = ProcessState::Middle;
info!("locked state ok");
let gui_api = Arc::clone(&lock!(helper).gui_api_xp);
info!("clone gui ok");
let pub_api = Arc::clone(&lock!(helper).pub_api_xp);
info!("clone pub ok");
*lock!(pub_api) = PubXmrigProxyApi::new();
info!("pub api reset ok");
*lock!(gui_api) = PubXmrigProxyApi::new();
info!("gui api reset ok");
}
// The "restart frontend" to a "frontend" function.
// Basically calls to kill the current xmrig-proxy, waits a little, then starts the below function in a a new thread, then exit.
pub fn restart_xp(
helper: &Arc<Mutex<Self>>,
state: &crate::disk::state::XmrigProxy,
state_xmrig: &Xmrig,
path: &Path,
) {
info!("XMRig | Attempting to restart...");
lock2!(helper, xmrig_proxy).state = ProcessState::Middle;
lock2!(helper, xmrig_proxy).signal = ProcessSignal::Restart;
let helper = Arc::clone(helper);
let state = state.clone();
let state_xmrig = state_xmrig.clone();
let path = path.to_path_buf();
// This thread lives to wait, start xmrig_proxy then die.
thread::spawn(move || {
while lock2!(helper, xmrig_proxy).state != ProcessState::Waiting {
warn!("XMRig_proxy | Want to restart but process is still alive, waiting...");
sleep!(1000);
}
// Ok, process is not alive, start the new one!
info!("XMRig_proxy | Old process seems dead, starting new one!");
Self::start_xp(&helper, &state, &state_xmrig, &path);
});
info!("XMRig | Restart ... OK");
}
pub fn start_xp(
helper: &Arc<Mutex<Self>>,
state_proxy: &crate::disk::state::XmrigProxy,
state_xmrig: &Xmrig,
path: &Path,
) {
lock2!(helper, xmrig_proxy).state = ProcessState::Middle;
let args = Self::build_xp_args(helper, state_proxy);
// Print arguments & user settings to console
crate::disk::print_dash(&format!("XMRig-Proxy | Launch arguments: {:#?}", args));
info!("XMRig-Proxy | Using path: [{}]", path.display());
// Spawn watchdog thread
let process = Arc::clone(&lock!(helper).xmrig_proxy);
let gui_api = Arc::clone(&lock!(helper).gui_api_xp);
let pub_api = Arc::clone(&lock!(helper).pub_api_xp);
let process_xvb = Arc::clone(&lock!(helper).xvb);
let process_xmrig = Arc::clone(&lock!(helper).xmrig);
let path = path.to_path_buf();
let token = state_proxy.token.clone();
let state_xmrig = state_xmrig.clone();
let redirect_xmrig = state_proxy.redirect_local_xmrig;
let pub_api_xvb = Arc::clone(&lock!(helper).pub_api_xvb);
let gui_api_xmrig = Arc::clone(&lock!(helper).gui_api_xmrig);
thread::spawn(move || {
Self::spawn_xp_watchdog(
&process,
&gui_api,
&pub_api,
args,
path,
&token,
&state_xmrig,
redirect_xmrig,
process_xvb,
process_xmrig,
&pub_api_xvb,
&gui_api_xmrig,
);
});
}
#[tokio::main]
#[allow(clippy::await_holding_lock)]
#[allow(clippy::too_many_arguments)]
async fn spawn_xp_watchdog(
process: &Arc<Mutex<Process>>,
gui_api: &Arc<Mutex<PubXmrigProxyApi>>,
pub_api: &Arc<Mutex<PubXmrigProxyApi>>,
args: Vec<String>,
path: std::path::PathBuf,
token_proxy: &str,
state_xmrig: &Xmrig,
xmrig_redirect: bool,
process_xvb: Arc<Mutex<Process>>,
process_xmrig: Arc<Mutex<Process>>,
pub_api_xvb: &Arc<Mutex<PubXvbApi>>,
gui_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
) {
lock!(process).start = Instant::now();
// spawn pty
debug!("XMRig-Proxy | Creating PTY...");
let pty = portable_pty::native_pty_system();
let pair = pty
.openpty(portable_pty::PtySize {
rows: 100,
cols: 1000,
pixel_width: 0,
pixel_height: 0,
})
.unwrap();
// 4. Spawn PTY read thread
debug!("XMRig-Proxy | Spawning PTY read thread...");
let reader = pair.master.try_clone_reader().unwrap(); // Get STDOUT/STDERR before moving the PTY
let output_parse = Arc::clone(&lock!(process).output_parse);
let output_pub = Arc::clone(&lock!(process).output_pub);
spawn(enc!((pub_api_xvb, output_parse, output_pub) async move {
Self::read_pty_xp(output_parse, output_pub, reader, process_xvb, &pub_api_xvb).await;
}));
// 1b. Create command
debug!("XMRig-Proxy | Creating command...");
let mut cmd = portable_pty::cmdbuilder::CommandBuilder::new(path.clone());
cmd.args(args);
cmd.cwd(path.as_path().parent().unwrap());
// 1c. Create child
debug!("XMRig-Proxy | Creating child...");
let child_pty = arc_mut!(pair.slave.spawn_command(cmd).unwrap());
drop(pair.slave);
let mut stdin = pair.master.take_writer().unwrap();
// to refactor to let user use his own ports
let api_summary_xp = XMRIG_PROXY_SUMMARY_URL;
let api_config_xmrig = XMRIG_CONFIG_URL;
// set state
let client = Client::new();
lock!(process).state = ProcessState::NotMining;
lock!(process).signal = ProcessSignal::None;
// reset stats
let node = lock!(pub_api).node.to_string();
*lock!(pub_api) = PubXmrigProxyApi::new();
*lock!(gui_api) = PubXmrigProxyApi::new();
lock!(gui_api).node = node;
// loop
let start = lock!(process).start;
debug!("Xmrig-Proxy Watchdog | enabling verbose mode");
#[cfg(target_os = "windows")]
if let Err(e) = write!(stdin, "v\r\n") {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
#[cfg(target_family = "unix")]
if let Err(e) = writeln!(stdin, "v") {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
debug!("Xmrig-Proxy Watchdog | checking connections");
#[cfg(target_os = "windows")]
if let Err(e) = write!(stdin, "c\r\n") {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
#[cfg(target_family = "unix")]
if let Err(e) = writeln!(stdin, "c") {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
info!("XMRig | Entering watchdog mode... woof!");
loop {
let now = Instant::now();
debug!("XMRig-Proxy Watchdog | ----------- Start of loop -----------");
// check state
if check_died(
&child_pty,
&mut lock!(process),
&start,
&mut lock!(gui_api).output,
) {
break;
}
// check signal
if signal_end(process, &child_pty, &start, &mut lock!(gui_api).output) {
break;
}
// check user input
check_user_input(process, &mut stdin);
// get data output/api
// Check if logs need resetting
debug!("XMRig Watchdog | Attempting GUI log reset check");
{
let mut lock = lock!(gui_api);
Self::check_reset_gui_output(&mut lock.output, ProcessName::XmrigProxy);
}
// Always update from output
// todo: check difference with xmrig
debug!("XMRig Watchdog | Starting [update_from_output()]");
PubXmrigProxyApi::update_from_output(
pub_api,
&output_pub,
&output_parse,
start.elapsed(),
process,
);
// update data from api
debug!("XMRig-Proxy Watchdog | Attempting HTTP API request...");
match PrivXmrigProxyApi::request_xp_api(&client, api_summary_xp, token_proxy).await {
Ok(priv_api) => {
debug!("XMRig-Proxy Watchdog | HTTP API request OK, attempting [update_from_priv()]");
PubXmrigProxyApi::update_from_priv(pub_api, priv_api);
}
Err(err) => {
warn!(
"XMRig-Proxy Watchdog | Could not send HTTP API request to: {}\n{}",
api_summary_xp, err
);
}
}
// update xmrig to use xmrig-proxy if option enabled and local xmrig alive
if xmrig_redirect
&& lock!(gui_api_xmrig).node != XvbNode::XmrigProxy.to_string()
&& (lock!(process_xmrig).state == ProcessState::Alive
|| lock!(process_xmrig).state == ProcessState::NotMining)
{
info!("redirect local xmrig instance to xmrig-proxy");
if let Err(err) = update_xmrig_config(
&client,
api_config_xmrig,
&state_xmrig.token,
&XvbNode::XmrigProxy,
"",
GUPAX_VERSION_UNDERSCORE,
)
.await
{
// show to console error about updating xmrig config
warn!("XMRig-Proxy Process | Failed request HTTP API Xmrig");
output_console(
&mut lock!(gui_api).output,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
ProcessName::XmrigProxy,
);
} else {
lock!(gui_api_xmrig).node = XvbNode::XmrigProxy.to_string();
debug!("XMRig-Proxy Process | mining on Xmrig-Proxy pool");
}
}
// do not use more than 1 second for the loop
sleep_end_loop(now, ProcessName::XmrigProxy).await;
}
// 5. If loop broke, we must be done here.
info!("XMRig-Proxy Watchdog | Watchdog thread exiting... Goodbye!");
// sleep
}
}
#[derive(Debug, Clone)]
pub struct PubXmrigProxyApi {
pub output: String,
pub uptime: Duration,
pub accepted: u32,
pub rejected: u32,
pub hashrate_1m: f32,
pub hashrate_10m: f32,
pub hashrate_1h: f32,
pub hashrate_12h: f32,
pub hashrate_24h: f32,
pub node: String,
}
impl Default for PubXmrigProxyApi {
fn default() -> Self {
Self::new()
}
}
impl PubXmrigProxyApi {
pub fn new() -> Self {
Self {
output: String::new(),
uptime: Duration::from_secs(0),
accepted: 0,
rejected: 0,
hashrate_1m: 0.0,
hashrate_10m: 0.0,
hashrate_1h: 0.0,
hashrate_12h: 0.0,
hashrate_24h: 0.0,
node: UNKNOWN_DATA.to_string(),
}
}
pub fn update_from_output(
public: &Arc<Mutex<Self>>,
output_parse: &Arc<Mutex<String>>,
output_pub: &Arc<Mutex<String>>,
elapsed: std::time::Duration,
process: &Arc<Mutex<Process>>,
) {
// 1. Take the process's current output buffer and combine it with Pub (if not empty)
let mut output_pub = lock!(output_pub);
{
let mut public = lock!(public);
if !output_pub.is_empty() {
public.output.push_str(&std::mem::take(&mut *output_pub));
}
// Update uptime
public.uptime = elapsed;
}
// 2. Check for "new job"/"no active...".
let mut output_parse = lock!(output_parse);
if XMRIG_REGEX.new_job.is_match(&output_parse)
|| XMRIG_REGEX.valid_conn.is_match(&output_parse)
{
lock!(process).state = ProcessState::Alive;
} else if XMRIG_REGEX.timeout.is_match(&output_parse)
|| XMRIG_REGEX.invalid_conn.is_match(&output_parse)
|| XMRIG_REGEX.error.is_match(&output_parse)
{
lock!(process).state = ProcessState::NotMining;
}
// 3. Throw away [output_parse]
output_parse.clear();
drop(output_parse);
}
// same method as PubXmrigApi, why not make a trait ?
pub fn combine_gui_pub_api(gui_api: &mut Self, pub_api: &mut Self) {
let output = std::mem::take(&mut gui_api.output);
let node = std::mem::take(&mut gui_api.node);
let buf = std::mem::take(&mut pub_api.output);
*gui_api = Self {
output,
node,
..std::mem::take(pub_api)
};
if !buf.is_empty() {
gui_api.output.push_str(&buf);
}
}
fn update_from_priv(public: &Arc<Mutex<Self>>, private: PrivXmrigProxyApi) {
let mut public = lock!(public);
*public = Self {
accepted: private.results.accepted,
rejected: private.results.rejected,
hashrate_1m: private.hashrate.total[0],
hashrate_10m: private.hashrate.total[1],
hashrate_1h: private.hashrate.total[2],
hashrate_12h: private.hashrate.total[3],
hashrate_24h: private.hashrate.total[4],
..std::mem::take(&mut *public)
}
}
}
#[derive(Deserialize, Serialize)]
pub struct PrivXmrigProxyApi {
hashrate: HashrateProxy,
miners: Miners,
results: Results,
}
#[derive(Deserialize, Serialize)]
struct Results {
accepted: u32,
rejected: u32,
}
#[derive(Deserialize, Serialize)]
struct HashrateProxy {
total: [f32; 6],
}
#[derive(Deserialize, Serialize)]
struct Miners {
now: u16,
max: u16,
}
impl PrivXmrigProxyApi {
#[inline]
// Send an HTTP request to XMRig's API, serialize it into [Self] and return it
async fn request_xp_api(
client: &Client,
api_uri: &str,
token: &str,
) -> std::result::Result<Self, anyhow::Error> {
let request = client
.get(api_uri)
.header(AUTHORIZATION, ["Bearer ", token].concat());
let mut private = request
.timeout(std::time::Duration::from_millis(5000))
.send()
.await?
.json::<PrivXmrigProxyApi>()
.await?;
// every hashrate value of xmrig-proxy is in kH/s, so we put convert it into H/s
for h in &mut private.hashrate.total {
*h *= 1000.0
}
Ok(private)
}
}

View file

@ -1,3 +1,9 @@
use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi;
use crate::helper::xvb::api_url_xmrig;
use crate::helper::xvb::current_controllable_hr;
use crate::helper::ProcessName;
use crate::miscs::output_console;
use crate::miscs::output_console_without_time;
use std::{
sync::{Arc, Mutex},
time::Duration,
@ -10,14 +16,15 @@ use tokio::time::sleep;
use crate::{
helper::{
p2pool::PubP2poolApi,
xmrig::{PrivXmrigApi, PubXmrigApi},
xvb::{
nodes::XvbNode, output_console, output_console_without_time, priv_stats::RuntimeMode,
xrig::{
update_xmrig_config,
xmrig::{PrivXmrigApi, PubXmrigApi},
},
xvb::{nodes::XvbNode, priv_stats::RuntimeMode},
},
macros::lock,
BLOCK_PPLNS_WINDOW_MAIN, BLOCK_PPLNS_WINDOW_MINI, SECOND_PER_BLOCK_P2POOL, XMRIG_CONFIG_URI,
XVB_BUFFER, XVB_ROUND_DONOR_MEGA_MIN_HR, XVB_ROUND_DONOR_MIN_HR, XVB_ROUND_DONOR_VIP_MIN_HR,
BLOCK_PPLNS_WINDOW_MAIN, BLOCK_PPLNS_WINDOW_MINI, SECOND_PER_BLOCK_P2POOL, XVB_BUFFER,
XVB_ROUND_DONOR_MEGA_MIN_HR, XVB_ROUND_DONOR_MIN_HR, XVB_ROUND_DONOR_VIP_MIN_HR,
XVB_ROUND_DONOR_WHALE_MIN_HR, XVB_TIME_ALGO,
};
@ -28,23 +35,27 @@ pub(crate) async fn algorithm(
client: &Client,
gui_api_xvb: &Arc<Mutex<PubXvbApi>>,
gui_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
gui_api_xp: &Arc<Mutex<PubXmrigProxyApi>>,
gui_api_p2pool: &Arc<Mutex<PubP2poolApi>>,
token_xmrig: &str,
state_p2pool: &crate::disk::state::P2pool,
share: u32,
time_donated: &Arc<Mutex<u32>>,
rig: &str,
xp_alive: bool,
) {
let mut algorithm = Algorithm::new(
client,
gui_api_xvb,
gui_api_xmrig,
gui_api_xp,
gui_api_p2pool,
token_xmrig,
state_p2pool,
share,
time_donated,
rig,
xp_alive,
);
algorithm.run().await;
}
@ -53,11 +64,13 @@ struct Algorithm<'a> {
client: &'a Client,
gui_api_xvb: &'a Arc<Mutex<PubXvbApi>>,
gui_api_xmrig: &'a Arc<Mutex<PubXmrigApi>>,
gui_api_xp: &'a Arc<Mutex<PubXmrigProxyApi>>,
gui_api_p2pool: &'a Arc<Mutex<PubP2poolApi>>,
token_xmrig: &'a str,
state_p2pool: &'a crate::disk::state::P2pool,
time_donated: &'a Arc<Mutex<u32>>,
rig: &'a str,
xp_alive: bool,
stats: Stats,
}
@ -78,6 +91,7 @@ struct Stats {
share_min_hashrate: f32,
spareable_hashrate: f32,
spared_time: u32,
api_url: String,
}
impl<'a> Algorithm<'a> {
@ -85,24 +99,18 @@ impl<'a> Algorithm<'a> {
client: &'a Client,
gui_api_xvb: &'a Arc<Mutex<PubXvbApi>>,
gui_api_xmrig: &'a Arc<Mutex<PubXmrigApi>>,
gui_api_xp: &'a Arc<Mutex<PubXmrigProxyApi>>,
gui_api_p2pool: &'a Arc<Mutex<PubP2poolApi>>,
token_xmrig: &'a str,
state_p2pool: &'a crate::disk::state::P2pool,
share: u32,
time_donated: &'a Arc<Mutex<u32>>,
rig: &'a str,
xp_alive: bool,
) -> Self {
info!("XvB Process | Starting Algorithm - Algorithm State:",);
let hashrate_xmrig = {
if lock!(gui_api_xmrig).hashrate_raw_15m > 0.0 {
lock!(gui_api_xmrig).hashrate_raw_15m
} else if lock!(gui_api_xmrig).hashrate_raw_1m > 0.0 {
lock!(gui_api_xmrig).hashrate_raw_1m
} else {
lock!(gui_api_xmrig).hashrate_raw
}
};
let hashrate_xmrig = current_controllable_hr(xp_alive, gui_api_xp, gui_api_xmrig);
let address = state_p2pool.address.clone();
@ -137,6 +145,8 @@ impl<'a> Algorithm<'a> {
let spareable_hashrate = hashrate_xmrig - share_min_hashrate;
let api_url = api_url_xmrig(xp_alive, true);
// TODO consider printing algorithm stats instead of spreadout print statements
let stats = Stats {
share,
@ -154,17 +164,20 @@ impl<'a> Algorithm<'a> {
share_min_hashrate,
spareable_hashrate,
spared_time: u32::default(),
api_url,
};
let mut new_instace = Self {
client,
gui_api_xvb,
gui_api_xmrig,
gui_api_xp,
gui_api_p2pool,
token_xmrig,
state_p2pool,
time_donated,
rig,
xp_alive,
stats,
};
@ -190,37 +203,93 @@ impl<'a> Algorithm<'a> {
self.stats.xvb_1h_avg > self.stats.target_donation_hashrate
}
async fn mine_p2pool(&self) {
if lock!(self.gui_api_xvb).current_node != Some(XvbNode::P2pool) {
info!("Xvb Process | request xmrig to mine on p2pool");
async fn target_p2pool_node(&self) {
let msg_xmrig_or_xp = if self.xp_alive {
"XMRig-Proxy"
} else {
"XMRig"
};
if let Err(err) = PrivXmrigApi::update_xmrig_config(
if lock!(self.gui_api_xvb).current_node != Some(XvbNode::P2pool) {
info!("Xvb Process | request {msg_xmrig_or_xp} to mine on p2pool");
if let Err(err) = update_xmrig_config(
self.client,
XMRIG_CONFIG_URI,
&self.stats.api_url,
self.token_xmrig,
&XvbNode::P2pool,
&self.stats.address,
self.gui_api_xmrig,
self.rig,
)
.await
{
warn!("Xvb Process | Failed request HTTP API Xmrig");
warn!("Xvb Process | Failed request HTTP API {msg_xmrig_or_xp}");
output_console(
self.gui_api_xvb,
&mut lock!(self.gui_api_xvb).output,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
"Failure to update {msg_xmrig_or_xp} config with HTTP API.\nError: {}",
err
),
crate::helper::ProcessName::Xvb,
);
} else {
debug!("Xvb Process | {msg_xmrig_or_xp} mining on p2pool pool");
}
}
output_console(self.gui_api_xvb, "No share in the current PPLNS Window !");
output_console(
self.gui_api_xvb,
"Mining on P2pool for the next ten minutes.",
);
}
async fn target_xvb_node(&self) {
let msg_xmrig_or_xp = if self.xp_alive {
"XMRig-Proxy"
} else {
"XMRig"
};
let node = lock!(self.gui_api_xvb).stats_priv.node;
debug!("Xvb Process | request {msg_xmrig_or_xp} to mine on XvB");
if lock!(self.gui_api_xvb).current_node.is_none()
|| lock!(self.gui_api_xvb)
.current_node
.as_ref()
.is_some_and(|n| n == &XvbNode::P2pool)
{
if let Err(err) = update_xmrig_config(
self.client,
&self.stats.api_url,
self.token_xmrig,
&node,
&self.stats.address,
self.rig,
)
.await
{
// show to console error about updating xmrig config
warn!("Xvb Process | Failed request HTTP API {msg_xmrig_or_xp}");
output_console(
&mut lock!(self.gui_api_xvb).output,
&format!(
"Failure to update {msg_xmrig_or_xp} config with HTTP API.\nError: {}",
err
),
crate::helper::ProcessName::Xvb,
);
} else {
if self.xp_alive {
lock!(self.gui_api_xp).node = node.to_string();
} else {
lock!(self.gui_api_xmrig).node = node.to_string();
}
debug!("Xvb Process | {msg_xmrig_or_xp} mining on XvB pool");
}
}
}
async fn send_all_p2pool(&self) {
self.target_p2pool_node().await;
sleep(Duration::from_secs(XVB_TIME_ALGO.into())).await;
lock!(self.gui_api_xvb)
.p2pool_sent_last_hour_samples
.0
@ -231,40 +300,9 @@ impl<'a> Algorithm<'a> {
.push_back(0.0);
}
async fn mine_xvb(&self) {
let node = lock!(self.gui_api_xvb).stats_priv.node;
async fn send_all_xvb(&self) {
self.target_xvb_node().await;
debug!("Xvb Process | request xmrig to mine on XvB");
if lock!(self.gui_api_xvb).current_node.is_none()
|| lock!(self.gui_api_xvb)
.current_node
.as_ref()
.is_some_and(|n| n == &XvbNode::P2pool)
{
if let Err(err) = PrivXmrigApi::update_xmrig_config(
self.client,
XMRIG_CONFIG_URI,
self.token_xmrig,
&node,
&self.stats.address,
self.gui_api_xmrig,
self.rig,
)
.await
{
// show to console error about updating xmrig config
warn!("Xvb Process | Failed request HTTP API Xmrig");
output_console(
self.gui_api_xvb,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
);
} else {
debug!("Xvb Process | mining on XvB pool");
}
}
sleep(Duration::from_secs(XVB_TIME_ALGO.into())).await;
lock!(self.gui_api_xvb)
@ -278,7 +316,6 @@ impl<'a> Algorithm<'a> {
}
async fn sleep_then_update_node_xmrig(&self) {
let node = lock!(self.gui_api_xvb).stats_priv.node;
debug!(
"Xvb Process | algo sleep for {} while mining on P2pool",
XVB_TIME_ALGO - self.stats.spared_time
@ -290,39 +327,27 @@ impl<'a> Algorithm<'a> {
// only update xmrig config if it is actually mining.
debug!("Xvb Process | request xmrig to mine on XvB");
if lock!(self.gui_api_xvb).current_node.is_none()
|| lock!(self.gui_api_xvb)
.current_node
.as_ref()
.is_some_and(|n| n == &XvbNode::P2pool)
{
if let Err(err) = PrivXmrigApi::update_xmrig_config(
self.client,
XMRIG_CONFIG_URI,
self.token_xmrig,
&node,
&self.stats.address,
self.gui_api_xmrig,
self.rig,
)
.await
{
// show to console error about updating xmrig config
warn!("Xvb Process | Failed request HTTP API Xmrig");
output_console(
self.gui_api_xvb,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
);
} else {
debug!("Xvb Process | mining on XvB pool");
}
}
self.target_xvb_node().await;
// will not quit the process until it is really done.
// xvb process watch this algo handle to see if process is finished or not.
sleep(Duration::from_secs(self.stats.spared_time.into())).await;
lock!(self.gui_api_xvb)
.p2pool_sent_last_hour_samples
.0
.push_back(
self.stats.hashrate_xmrig
* ((XVB_TIME_ALGO as f32 - self.stats.spared_time as f32)
/ XVB_TIME_ALGO as f32),
);
lock!(self.gui_api_xvb)
.xvb_sent_last_hour_samples
.0
.push_back(
self.stats.hashrate_xmrig * (self.stats.spared_time as f32 / XVB_TIME_ALGO as f32),
);
}
fn get_target_donation_hashrate(&self) -> f32 {
@ -384,32 +409,60 @@ impl<'a> Algorithm<'a> {
minimum_hr
}
async fn fulfill_share(&self) {
output_console(
&mut lock!(self.gui_api_xvb).output,
"There are no shares in p2pool\nSending all hashrate to p2pool!",
crate::helper::ProcessName::Xvb,
);
self.send_all_p2pool().await
}
async fn fulfill_xvb_24_avg(&self) {
output_console(
&mut lock!(self.gui_api_xvb).output,
"24H avg XvB target not achieved.\nSending all hashrate to XvB!",
crate::helper::ProcessName::Xvb,
);
*lock!(self.time_donated) = XVB_TIME_ALGO;
self.send_all_xvb().await
}
async fn fulfill_normal_cycles(&self) {
output_console(
&mut lock!(self.gui_api_xvb).output,
"There is a share in p2pool and 24H avg XvB is achieved.\nSending {self.stats.spared_time} to XvB!",
crate::helper::ProcessName::Xvb,
);
*lock!(self.time_donated) = self.stats.spared_time;
self.target_p2pool_node().await;
self.sleep_then_update_node_xmrig().await;
}
async fn run(&mut self) {
// TODO add console output for each step
output_console(
&mut lock!(self.gui_api_xvb).output,
"Algorithm of HR distribution started for the next 10 minutes.",
crate::helper::ProcessName::Xvb,
);
if self.is_share_fulfilled() && self.is_xvb_24h_fulfilled() {
output_console(
self.gui_api_xvb,
"There is a share in p2pool and 24H avg XvB is achieved.",
);
output_console(self.gui_api_xvb, "Calculating donation time for XvB...");
*lock!(self.time_donated) = self.stats.spared_time;
self.mine_p2pool().await;
self.sleep_then_update_node_xmrig().await;
} else if self.is_share_fulfilled() {
output_console(self.gui_api_xvb, "24H avg XvB target not achieved.");
output_console(self.gui_api_xvb, "Sending all hashrate to XvB");
*lock!(self.time_donated) = XVB_TIME_ALGO;
self.mine_xvb().await
if !self.is_share_fulfilled() {
self.fulfill_share().await
} else if !self.is_xvb_24h_fulfilled() {
self.fulfill_xvb_24_avg().await
} else {
output_console(self.gui_api_xvb, "There are no shares in p2pool");
output_console(self.gui_api_xvb, "Sending all hashrate to p2pool");
self.mine_p2pool().await
self.fulfill_normal_cycles().await
}
output_console(self.gui_api_xvb, "")
output_console_without_time(
&mut lock!(self.gui_api_xvb).output,
"",
crate::helper::ProcessName::Xvb,
)
}
}

View file

@ -1,12 +1,15 @@
use crate::helper::xrig::update_xmrig_config;
use crate::helper::xvb::algorithm::algorithm;
use crate::helper::xvb::priv_stats::XvbPrivStats;
use crate::helper::xvb::public_stats::XvbPubStats;
use crate::helper::{sleep_end_loop, ProcessName};
use crate::miscs::output_console;
use crate::{XMRIG_CONFIG_URL, XMRIG_PROXY_CONFIG_URL, XMRIG_PROXY_SUMMARY_URL, XMRIG_SUMMARY_URL};
use bounded_vec_deque::BoundedVecDeque;
use enclose::enc;
use log::{debug, error, info, warn};
use log::{debug, info, warn};
use readable::up::Uptime;
use reqwest::Client;
use std::fmt::Write;
use std::mem;
use std::time::Duration;
use std::{
@ -17,9 +20,8 @@ use tokio::spawn;
use tokio::task::JoinHandle;
use tokio::time::{sleep, Instant};
use crate::helper::xmrig::PrivXmrigApi;
use crate::helper::xvb::rounds::round_type;
use crate::utils::constants::{XMRIG_CONFIG_URI, XVB_PUBLIC_ONLY, XVB_TIME_ALGO};
use crate::utils::constants::{XVB_PUBLIC_ONLY, XVB_TIME_ALGO};
use crate::{
helper::{ProcessSignal, ProcessState},
utils::macros::{lock, lock2, sleep},
@ -28,7 +30,8 @@ use crate::{
use self::nodes::XvbNode;
use super::p2pool::PubP2poolApi;
use super::xmrig::PubXmrigApi;
use super::xrig::xmrig::PubXmrigApi;
use super::xrig::xmrig_proxy::PubXmrigProxyApi;
use super::{Helper, Process};
pub mod algorithm;
@ -49,6 +52,7 @@ impl Helper {
state_xvb: &crate::disk::state::Xvb,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
state_xp: &crate::disk::state::XmrigProxy,
) {
info!("XvB | Attempting to restart...");
lock2!(helper, xvb).signal = ProcessSignal::Restart;
@ -57,6 +61,7 @@ impl Helper {
let state_xvb = state_xvb.clone();
let state_p2pool = state_p2pool.clone();
let state_xmrig = state_xmrig.clone();
let state_xp = state_xp.clone();
// This thread lives to wait, start xmrig then die.
thread::spawn(move || {
while lock2!(helper, xvb).state != ProcessState::Waiting {
@ -65,7 +70,7 @@ impl Helper {
}
// Ok, process is not alive, start the new one!
info!("XvB | Old process seems dead, starting new one!");
Self::start_xvb(&helper, &state_xvb, &state_p2pool, &state_xmrig);
Self::start_xvb(&helper, &state_xvb, &state_p2pool, &state_xmrig, &state_xp);
});
info!("XMRig | Restart ... OK");
}
@ -74,6 +79,7 @@ impl Helper {
state_xvb: &crate::disk::state::Xvb,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
state_xp: &crate::disk::state::XmrigProxy,
) {
// 1. Clone Arc value from Helper
// pub for writing new values that will show up on UI after helper thread update. (every seconds.)
@ -88,8 +94,11 @@ impl Helper {
let process_p2pool = Arc::clone(&lock!(helper).p2pool);
let gui_api_p2pool = Arc::clone(&lock!(helper).gui_api_p2pool);
let process_xmrig = Arc::clone(&lock!(helper).xmrig);
let process_xp = Arc::clone(&lock!(helper).xmrig_proxy);
let gui_api_xmrig = Arc::clone(&lock!(helper).gui_api_xmrig);
let pub_api_xmrig = Arc::clone(&lock!(helper).pub_api_xmrig);
let gui_api_xp = Arc::clone(&lock!(helper).gui_api_xp);
let pub_api_xp = Arc::clone(&lock!(helper).gui_api_xp);
// Reset before printing to output.
// Need to reset because values of stats would stay otherwise which could bring confusion even if panel is with a disabled theme.
// at the start of a process, values must be default.
@ -113,22 +122,28 @@ impl Helper {
// verify if token and address are existent on XvB server
info!("XvB | spawn watchdog");
thread::spawn(enc!((state_xvb, state_p2pool, state_xmrig) move || {
// thread priority, else there are issue on windows but it is also good for other OS
Self::spawn_xvb_watchdog(
&gui_api,
&pub_api,
&process,
&state_xvb,
&state_p2pool,
&state_xmrig,
&gui_api_p2pool,
&process_p2pool,
&gui_api_xmrig,
&pub_api_xmrig,
&process_xmrig,
);
}));
thread::spawn(
enc!((state_xvb, state_p2pool, state_xmrig, state_xmrig,state_xp) move || {
// thread priority, else there are issue on windows but it is also good for other OS
Self::spawn_xvb_watchdog(
&gui_api,
&pub_api,
&process,
&state_xvb,
&state_p2pool,
&state_xmrig,
&state_xp,
&gui_api_p2pool,
&process_p2pool,
&gui_api_xmrig,
&pub_api_xmrig,
&process_xmrig,
&gui_api_xp,
&pub_api_xp,
&process_xp,
);
}),
);
}
// need the helper so we can restart the thread after getting a signal not caused by a restart.
#[allow(clippy::too_many_arguments)]
@ -140,11 +155,15 @@ impl Helper {
state_xvb: &crate::disk::state::Xvb,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
state_xp: &crate::disk::state::XmrigProxy,
gui_api_p2pool: &Arc<Mutex<PubP2poolApi>>,
process_p2pool: &Arc<Mutex<Process>>,
gui_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
pub_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
process_xmrig: &Arc<Mutex<Process>>,
gui_api_xp: &Arc<Mutex<PubXmrigProxyApi>>,
pub_api_xp: &Arc<Mutex<PubXmrigProxyApi>>,
process_xp: &Arc<Mutex<Process>>,
) {
// create uniq client that is going to be used for during the life of the thread.
let client = reqwest::Client::new();
@ -161,12 +180,13 @@ impl Helper {
gui_api,
process_p2pool,
process_xmrig,
process_xp,
process,
state_p2pool,
state_xvb,
)
.await;
let xp_alive = lock!(process_xp).state == ProcessState::Alive;
// uptime for log of signal check ?
let start = lock!(process).start;
// uptime of last run of algo
@ -187,7 +207,7 @@ impl Helper {
loop {
debug!("XvB Watchdog | ----------- Start of loop -----------");
// Set timer of loop
let start_loop = Instant::now();
let start_loop = std::time::Instant::now();
// verify if p2pool and xmrig are running, else XvB must be reloaded with another token/address to start verifying the other process.
check_state_outcauses_xvb(
&client,
@ -195,12 +215,15 @@ impl Helper {
pub_api,
process,
process_xmrig,
process_xp,
process_p2pool,
&mut first_loop,
&handle_algo,
pub_api_xmrig,
pub_api_xp,
state_p2pool,
state_xmrig,
state_xp,
)
.await;
@ -213,8 +236,11 @@ impl Helper {
pub_api,
gui_api,
gui_api_xmrig,
gui_api_xp,
state_p2pool,
state_xmrig,
state_xp,
xp_alive,
) {
info!("XvB Watchdog | Signal has stopped the loop");
break;
@ -247,70 +273,83 @@ impl Helper {
// first_loop is false here but could be changed to true under some conditions.
// will send a stop signal if public stats failed or update data with new one.
*lock!(handle_request) = Some(spawn(
enc!((client, pub_api, gui_api, gui_api_p2pool, gui_api_xmrig, state_xvb, state_p2pool, state_xmrig, process, last_algorithm, retry, handle_algo, time_donated, last_request) async move {
// needs to wait here for public stats to get private stats.
if last_request_expired || first_loop || should_refresh_before_next_algo {
XvbPubStats::update_stats(&client, &gui_api, &pub_api, &process).await;
*lock!(last_request) = Instant::now();
}
// private stats needs valid token and address.
// other stats needs everything to be alive, so just require alive here for now.
// maybe later differentiate to add a way to get private stats without running the algo ?
if lock!(process).state == ProcessState::Alive {
// get current share to know if we are in a round and this is a required data for algo.
let share = lock!(gui_api_p2pool).sidechain_shares;
debug!("XvB | Number of current shares: {}", share);
// private stats can be requested every minute or first loop or if the have almost finished.
if last_request_expired || first_loop || should_refresh_before_next_algo {
debug!("XvB Watchdog | Attempting HTTP private API request...");
// reload private stats, it send a signal if error that will be captured on the upper thread.
XvbPrivStats::update_stats(
&client, &state_p2pool.address, &state_xvb.token, &pub_api, &gui_api, &process,
)
.await;
*lock!(last_request) = Instant::now();
// verify in which round type we are
let round = round_type(share, &pub_api);
// refresh the round we participate in.
debug!("XvB | Round type: {:#?}", round);
lock!(pub_api).stats_priv.round_participate = round;
// verify if we are the winner of the current round
if lock!(pub_api).stats_pub.winner
== Helper::head_tail_of_monero_address(&state_p2pool.address).as_str()
{
lock!(pub_api).stats_priv.win_current = true
enc!((client, pub_api, gui_api, gui_api_p2pool, gui_api_xmrig, gui_api_xp, state_xvb, state_p2pool, state_xmrig, state_xp, process, last_algorithm, retry, handle_algo, time_donated, last_request) async move {
// needs to wait here for public stats to get private stats.
if last_request_expired || first_loop || should_refresh_before_next_algo {
XvbPubStats::update_stats(&client, &gui_api, &pub_api, &process).await;
*lock!(last_request) = Instant::now();
}
}
if (first_loop || *lock!(retry)|| is_algo_finished) && lock!(gui_api_xmrig).hashrate_raw > 0.0 && lock!(process).state == ProcessState::Alive
{
// if algo was started, it must not retry next loop.
*lock!(retry) = false;
// reset instant because algo will start.
*lock!(last_algorithm) = Instant::now();
*lock!(handle_algo) = Some(spawn(enc!((client, gui_api, gui_api_xmrig, state_xmrig, time_donated) async move {
algorithm(
&client,
&gui_api,
&gui_api_xmrig,
&gui_api_p2pool,
&state_xmrig.token,
&state_p2pool,
share,
&time_donated,
&state_xmrig.rig,
).await;
})));
} else {
// if xmrig is still at 0 HR but is alive and algorithm is skipped, recheck first 10s of xmrig inside algorithm next time (in one minute). Don't check if algo failed to start because state was not alive after getting private stats.
// private stats needs valid token and address.
// other stats needs everything to be alive, so just require alive here for now.
// maybe later differentiate to add a way to get private stats without running the algo ?
if lock!(process).state == ProcessState::Alive {
// get current share to know if we are in a round and this is a required data for algo.
let share = lock!(gui_api_p2pool).sidechain_shares;
debug!("XvB | Number of current shares: {}", share);
// private stats can be requested every minute or first loop or if the have almost finished.
if last_request_expired || first_loop || should_refresh_before_next_algo {
debug!("XvB Watchdog | Attempting HTTP private API request...");
// reload private stats, it send a signal if error that will be captured on the upper thread.
XvbPrivStats::update_stats(
&client, &state_p2pool.address, &state_xvb.token, &pub_api, &gui_api, &process,
)
.await;
*lock!(last_request) = Instant::now();
if lock!(gui_api_xmrig).hashrate_raw == 0.0 && lock!(process).state == ProcessState::Alive {
*lock!(retry) = true
// verify in which round type we are
let round = round_type(share, &pub_api);
// refresh the round we participate in.
debug!("XvB | Round type: {:#?}", round);
lock!(pub_api).stats_priv.round_participate = round;
// verify if we are the winner of the current round
if lock!(pub_api).stats_pub.winner
== Helper::head_tail_of_monero_address(&state_p2pool.address).as_str()
{
lock!(pub_api).stats_priv.win_current = true
}
}
let hashrate = current_controllable_hr(xp_alive, &gui_api_xp, &gui_api_xmrig);
if (first_loop || *lock!(retry)|| is_algo_finished) && hashrate > 0.0 && lock!(process).state == ProcessState::Alive
{
// if algo was started, it must not retry next loop.
*lock!(retry) = false;
// reset instant because algo will start.
*lock!(last_algorithm) = Instant::now();
*lock!(handle_algo) = Some(spawn(enc!((client, gui_api, gui_api_xmrig, gui_api_xp, state_xmrig, state_xp, time_donated) async move {
let token_xmrig = if xp_alive {
&state_xp.token
} else {
&state_xmrig.token
};
let rig = if xp_alive {
""
} else {
&state_xmrig.rig
};
algorithm(
&client,
&gui_api,
&gui_api_xmrig,
&gui_api_xp,
&gui_api_p2pool,
token_xmrig,
&state_p2pool,
share,
&time_donated,
rig,
xp_alive
).await;
})));
} else {
// if xmrig is still at 0 HR but is alive and algorithm is skipped, recheck first 10s of xmrig inside algorithm next time (in one minute). Don't check if algo failed to start because state was not alive after getting private stats.
}
}),
if current_controllable_hr(xp_alive, &gui_api_xp, &gui_api_xmrig) == 0.0 && lock!(process).state == ProcessState::Alive {
*lock!(retry) = true
}
}
}
}),
));
}
// if retry is false, next time the message about waiting for xmrig HR can be shown.
@ -320,10 +359,12 @@ impl Helper {
// inform user that algorithm has not yet started because it is waiting for xmrig HR.
// show this message only once before the start of algo
if *lock!(retry) && !msg_retry_done {
output_console(
gui_api,
"Algorithm is waiting for 10 seconds average HR of XMRig.",
);
let msg = if xp_alive {
"Algorithm is waiting for 1 minute average HR of XMRig-Proxy"
} else {
"Algorithm is waiting for 10 seconds average HR of XMRig."
};
output_console(&mut lock!(gui_api).output, msg, ProcessName::Xvb);
msg_retry_done = true;
}
// update indicator (time before switch and mining location) in private stats
@ -346,15 +387,7 @@ impl Helper {
first_loop = false;
}
// Sleep (only if 900ms hasn't passed)
let elapsed = start_loop.elapsed().as_millis();
// Since logic goes off if less than 1000, casting should be safe
if elapsed < 999 {
let sleep = (999 - elapsed) as u64;
debug!("XvB Watchdog | END OF LOOP - Sleeping for [{}]s...", sleep);
tokio::time::sleep(Duration::from_millis(sleep)).await;
} else {
debug!("XMRig Watchdog | END OF LOOP - Not sleeping!");
}
sleep_end_loop(start_loop, ProcessName::Xvb).await;
}
}
}
@ -418,11 +451,13 @@ impl PubXvbApi {
};
}
}
#[allow(clippy::too_many_arguments)]
async fn check_conditions_for_start(
client: &Client,
gui_api: &Arc<Mutex<PubXvbApi>>,
process_p2pool: &Arc<Mutex<Process>>,
process_xmrig: &Arc<Mutex<Process>>,
process_xp: &Arc<Mutex<Process>>,
process_xvb: &Arc<Mutex<Process>>,
state_p2pool: &crate::disk::state::P2pool,
state_xvb: &crate::disk::state::Xvb,
@ -433,7 +468,7 @@ async fn check_conditions_for_start(
info!("XvB | verify address and token");
// send to console: token non existent for address on XvB server
warn!("Xvb | Start ... Partially failed because token and associated address are not existent on XvB server: {}\n", err);
output_console(gui_api, &format!("Token and associated address are not valid on XvB API.\nCheck if you are registered.\nError: {}", err));
output_console(&mut lock!(gui_api).output, &format!("Token and associated address are not valid on XvB API.\nCheck if you are registered.\nError: {}", err), ProcessName::Xvb);
ProcessState::NotMining
} else if lock!(process_p2pool).state != ProcessState::Alive {
info!("XvB | verify p2pool node");
@ -444,15 +479,18 @@ async fn check_conditions_for_start(
} else {
"P2pool process is not running.\nCheck the P2pool Tab"
};
output_console(gui_api, msg);
output_console(&mut lock!(gui_api).output, msg, ProcessName::Xvb);
ProcessState::Syncing
} else if lock!(process_xmrig).state != ProcessState::Alive {
// send to console: p2pool process is not running
warn!("Xvb | Start ... Partially failed because Xmrig instance is not running.");
} else if lock!(process_xmrig).state != ProcessState::Alive
|| lock!(process_xp).state != ProcessState::Alive
{
// send to console: xmrig process is not running
warn!("Xvb | Start ... Partially failed because Xmrig or Xmrig-Proxy instance is not running.");
// output the error to console
output_console(
gui_api,
"XMRig process is not running.\nCheck the Xmrig Tab.",
&mut lock!(gui_api).output,
"XMRig or Xmrig-Proxy process is not running.\nCheck the Xmrig or Xmrig-Proxy Tab. One of them must be running to start the XvB algorithm.",
ProcessName::Xvb,
);
ProcessState::Syncing
} else {
@ -464,8 +502,9 @@ async fn check_conditions_for_start(
// while waiting for xmrig and p2pool or getting right address/token, it can get public stats
info!("XvB | print to console state");
output_console(
gui_api,
&mut lock!(gui_api).output,
&["XvB partially started.\n", XVB_PUBLIC_ONLY].concat(),
ProcessName::Xvb,
);
}
// will update the preferred node for the first loop, even if partially started.
@ -479,81 +518,106 @@ async fn check_state_outcauses_xvb(
pub_api: &Arc<Mutex<PubXvbApi>>,
process: &Arc<Mutex<Process>>,
process_xmrig: &Arc<Mutex<Process>>,
process_xp: &Arc<Mutex<Process>>,
process_p2pool: &Arc<Mutex<Process>>,
first_loop: &mut bool,
handle_algo: &Arc<Mutex<Option<JoinHandle<()>>>>,
pub_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
pub_api_xp: &Arc<Mutex<PubXmrigProxyApi>>,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
state_xp: &crate::disk::state::XmrigProxy,
) {
// will check if the state can stay as it is.
// p2pool and xmrig are alive if ready and running (syncing is not alive).
let state = lock!(process).state;
let xp_is_alive = lock!(process_xp).state == ProcessState::Alive;
let msg_xmrig_or_proxy = if xp_is_alive { "Xmrig-Proxy" } else { "Xmrig" };
// if state is not alive, the algo should stop if it was running and p2pool should be used by xmrig.
if let Some(handle) = lock!(handle_algo).as_ref() {
if state != ProcessState::Alive && !handle.is_finished() {
handle.abort();
output_console(
gui_api,
&mut lock!(gui_api).output,
"XvB process can not completely continue, algorithm of distribution of HR is stopped.",
ProcessName::Xvb
);
// only update xmrig if it is alive and wasn't on p2pool already.
if lock!(gui_api).current_node != Some(XvbNode::P2pool)
&& lock!(process_xmrig).state == ProcessState::Alive
&& (lock!(process_xmrig).state == ProcessState::Alive || xp_is_alive)
{
let token_xmrig = state_xmrig.token.clone();
let token_xmrig = if xp_is_alive {
state_xp.token.clone()
} else {
state_xmrig.token.clone()
};
let address = state_p2pool.address.clone();
let rig = state_xmrig.rig.clone();
spawn(enc!((client, pub_api_xmrig, gui_api) async move {
let rig = if xp_is_alive {
"".to_string()
} else {
state_xmrig.rig.clone()
};
spawn(
enc!((client, pub_api_xmrig, gui_api,pub_api_xp) async move {
let url_api = api_url_xmrig(xp_is_alive, true);
if let Err(err) = update_xmrig_config(
&client,
&url_api,
&token_xmrig,
&XvbNode::P2pool,
&address,
&rig
)
.await
{
// show to console error about updating xmrig config
output_console(
&mut lock!(gui_api).output,
&format!(
"Failure to update {msg_xmrig_or_proxy} config with HTTP API.\nError: {}",
err
),
ProcessName::Xvb
);
} else {
if xp_is_alive {lock!(pub_api_xp).node = XvbNode::P2pool.to_string();} else {lock!(pub_api_xmrig).node = XvbNode::P2pool.to_string();}
if let Err(err) = PrivXmrigApi::update_xmrig_config(
&client,
XMRIG_CONFIG_URI,
&token_xmrig,
&XvbNode::P2pool,
&address,
&pub_api_xmrig,
&rig
)
.await
{
// show to console error about updating xmrig config
output_console(
&gui_api,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
);
} else {
output_console(
&gui_api,
&format!("XvB process can not completely continue, falling back to {}", XvbNode::P2pool),
);
}
output_console(
&mut lock!(gui_api).output,
&format!("XvB process can not completely continue, falling back to {}", XvbNode::P2pool),
ProcessName::Xvb
);
}
}));
}),
);
}
}
}
let is_xmrig_alive = lock!(process_xmrig).state == ProcessState::Alive;
let is_xmrig_alive = lock!(process_xp).state == ProcessState::Alive
|| lock!(process_xmrig).state == ProcessState::Alive;
let is_p2pool_alive = lock!(process_p2pool).state == ProcessState::Alive;
let p2pool_xmrig_alive = is_xmrig_alive && is_p2pool_alive;
// if state is middle because start is not finished yet, it will not do anything.
match state {
ProcessState::Alive if !p2pool_xmrig_alive => {
// they are not both alives, so state will be at syncing and data reset, state of loop also.
info!("XvB | stopped partially because XvB Nodes are not reachable.");
warn!("XvB | stopped partially because P2pool node or xmrig/xmrig-proxy are not reachable.");
// stats must be empty put to default so the UI reflect that XvB private is not running.
reset_data_xvb(pub_api, gui_api);
// request from public API must be executed at next loop, do not wait for 1 minute.
*first_loop = true;
output_console(
gui_api,
"XvB is now partially stopped because p2pool node or xmrig came offline.\nCheck P2pool and Xmrig Tabs",
&mut lock!(gui_api).output,
"XvB is now partially stopped because p2pool node or XMRig/XMRig-Proxy came offline.\nCheck P2pool and Xmrig/Xmrig-Proxy Tabs",
ProcessName::Xvb
);
output_console(gui_api, XVB_PUBLIC_ONLY);
output_console(
&mut lock!(gui_api).output,
XVB_PUBLIC_ONLY,
ProcessName::Xvb,
);
lock!(process).state = ProcessState::Syncing;
}
ProcessState::Syncing if p2pool_xmrig_alive => {
@ -563,8 +627,14 @@ async fn check_state_outcauses_xvb(
reset_data_xvb(pub_api, gui_api);
*first_loop = true;
output_console(
gui_api,
"XvB is now started because p2pool and xmrig came online.",
&mut lock!(gui_api).output,
&[
"XvB is now started because p2pool and ",
msg_xmrig_or_proxy,
" came online.",
]
.concat(),
ProcessName::Xvb,
);
}
ProcessState::Retry => {
@ -583,8 +653,11 @@ fn signal_interrupt(
pub_api: &Arc<Mutex<PubXvbApi>>,
gui_api: &Arc<Mutex<PubXvbApi>>,
gui_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
gui_api_xp: &Arc<Mutex<PubXmrigProxyApi>>,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
state_xp: &crate::disk::state::XmrigProxy,
xp_alive: bool,
) -> bool {
// Check SIGNAL
// check if STOP or RESTART Signal is given.
@ -602,7 +675,11 @@ fn signal_interrupt(
);
// insert the signal into output of XvB
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
output_console(gui_api, "\n\n\nXvB stopped\n\n\n");
output_console(
&mut lock!(gui_api).output,
"\n\n\nXvB stopped\n\n\n",
ProcessName::Xvb,
);
debug!("XvB Watchdog | Stop SIGNAL done, breaking");
lock!(process).signal = ProcessSignal::None;
lock!(process).state = ProcessState::Dead;
@ -623,8 +700,16 @@ fn signal_interrupt(
ProcessSignal::UpdateNodes(node) => {
if lock!(process).state != ProcessState::Waiting {
warn!("received the UpdateNode signal");
let token_xmrig = state_xmrig.token.clone();
let rig = state_xmrig.rig.clone();
let token_xmrig = if xp_alive {
state_xp.token.clone()
} else {
state_xmrig.token.clone()
};
let rig = if xp_alive {
"".to_string()
} else {
state_xmrig.rig.clone()
};
let address = state_p2pool.address.clone();
// check if state is alive. If it is and it is receiving such a signal, it means something a node (XvB or P2Pool) has failed.
// if XvB, xmrig needs to be switch to the other node (both will be checked though to be sure).
@ -637,13 +722,15 @@ fn signal_interrupt(
lock!(process).state = ProcessState::Waiting;
lock!(process).signal = ProcessSignal::None;
spawn(
enc!((node, process, client, gui_api, pub_api, was_alive, address, token_xmrig, gui_api_xmrig) async move {
enc!((node, process, client, gui_api, pub_api, was_alive, address, token_xmrig, gui_api_xmrig, gui_api_xp) async move {
warn!("in spawn of UpdateNodes");
match node {
XvbNode::NorthAmerica|XvbNode::Europe if was_alive => {
warn!("current is XvB node ? update to see which is available");
// a node is failing. We need to first verify if a node is available
XvbNode::update_fastest_node(&client, &gui_api, &pub_api, &process).await;
if lock!(process).state == ProcessState::OfflineNodesAll {
// No available nodes, so launch a process to verify periodicly.
// No available nodes, so launch a process to verify periodically.
sleep(Duration::from_secs(10)).await;
warn!("node fail, set spawn that will retry nodes and update state.");
while lock!(process).state == ProcessState::OfflineNodesAll {
@ -658,29 +745,43 @@ fn signal_interrupt(
},
XvbNode::NorthAmerica|XvbNode::Europe if !was_alive => {
lock!(process).state = ProcessState::Syncing;
// Probably a start. We don't consider XMRig using XvB nodes without algo.
// can update xmrig and check status of state in the same time.
// Need to set XMRig to P2Pool if it wasn't. XMRig should have populated this value at his start.
if lock!(gui_api).current_node != Some(XvbNode::P2pool) {
spawn(enc!((client, token_xmrig, address, gui_api_xmrig, gui_api) async move{
if let Err(err) = PrivXmrigApi::update_xmrig_config(
spawn(enc!((client, token_xmrig, address, gui_api_xmrig, gui_api_xp, gui_api) async move{
let url_api = api_url_xmrig(xp_alive, true);
warn!("update xmrig to use node ?");
if let Err(err) = update_xmrig_config(
&client,
XMRIG_CONFIG_URI,
&url_api,
&token_xmrig,
&XvbNode::P2pool,
&address,
&gui_api_xmrig,
&rig
)
.await {
let msg_xmrig_or_proxy = if xp_alive {
"XMRig-Proxy"
} else {
"XMRig"
};
output_console(
&gui_api,
&mut lock!(gui_api).output,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
"Failure to update {msg_xmrig_or_proxy} config with HTTP API.\nError: {}",
err
),
), ProcessName::Xvb
);
} else {
// update node xmrig
if xp_alive {
lock!(gui_api_xp).node = XvbNode::P2pool.to_string();
} else {
lock!(gui_api_xmrig).node = XvbNode::P2pool.to_string();
};
}
}
));}
@ -712,20 +813,6 @@ fn reset_data_xvb(pub_api: &Arc<Mutex<PubXvbApi>>, gui_api: &Arc<Mutex<PubXvbApi
// lock!(pub_api).output = output;
}
// print date time to console output in same format than xmrig
use chrono::Local;
fn datetimeonsole() -> String {
format!("[{}] ", Local::now().format("%Y-%m-%d %H:%M:%S%.3f"))
}
pub fn output_console(gui_api: &Arc<Mutex<PubXvbApi>>, msg: &str) {
if let Err(e) = writeln!(lock!(gui_api).output, "{}{msg}", datetimeonsole()) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
}
pub fn output_console_without_time(gui_api: &Arc<Mutex<PubXvbApi>>, msg: &str) {
if let Err(e) = writeln!(lock!(gui_api).output, "{msg}") {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
}
fn update_indicator_algo(
is_algo_started_once: bool,
is_algo_finished: bool,
@ -762,3 +849,41 @@ fn update_indicator_algo(
lock!(pub_api).stats_priv.msg_indicator = "Algorithm is not running".to_string();
}
}
// quick temporary function before refactor, but better than repeating this code
// if xp is alive, put true
// to get config url, true. False for summary
pub fn api_url_xmrig(xp: bool, config: bool) -> String {
if xp {
if config {
XMRIG_PROXY_CONFIG_URL.to_string()
} else {
XMRIG_PROXY_SUMMARY_URL.to_string()
}
} else if config {
XMRIG_CONFIG_URL.to_string()
} else {
XMRIG_SUMMARY_URL.to_string()
}
}
// get the current HR of xmrig or xmrig-proxy
// will get a longer average HR since it will be more accurate. Shorter timeframe can induce volatility.
fn current_controllable_hr(
xp_alive: bool,
gui_api_xp: &Arc<Mutex<PubXmrigProxyApi>>,
gui_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
) -> f32 {
if xp_alive {
if lock!(gui_api_xp).hashrate_10m > 0.0 {
lock!(gui_api_xp).hashrate_10m
} else {
lock!(gui_api_xp).hashrate_1m
}
} else if lock!(gui_api_xmrig).hashrate_raw_15m > 0.0 {
lock!(gui_api_xmrig).hashrate_raw_15m
} else if lock!(gui_api_xmrig).hashrate_raw_1m > 0.0 {
lock!(gui_api_xmrig).hashrate_raw_1m
} else {
lock!(gui_api_xmrig).hashrate_raw
}
}

View file

@ -10,7 +10,7 @@ use tokio::spawn;
use crate::{
components::node::{GetInfo, TIMEOUT_NODE_PING},
helper::{xvb::output_console, Process, ProcessState},
helper::{xvb::output_console, Process, ProcessName, ProcessState},
macros::lock,
GUPAX_VERSION_UNDERSCORE, XVB_NODE_EU, XVB_NODE_NA, XVB_NODE_PORT, XVB_NODE_RPC,
};
@ -25,6 +25,8 @@ pub enum XvbNode {
Europe,
#[display(fmt = "Local P2pool")]
P2pool,
#[display(fmt = "Xmrig Proxy")]
XmrigProxy,
}
impl XvbNode {
pub fn url(&self) -> String {
@ -32,12 +34,14 @@ impl XvbNode {
Self::NorthAmerica => String::from(XVB_NODE_NA),
Self::Europe => String::from(XVB_NODE_EU),
Self::P2pool => String::from("127.0.0.1"),
Self::XmrigProxy => String::from("127.0.0.1"),
}
}
pub fn port(&self) -> String {
match self {
Self::NorthAmerica | Self::Europe => String::from(XVB_NODE_PORT),
Self::P2pool => String::from("3333"),
Self::XmrigProxy => String::from("3355"),
}
}
pub fn user(&self, address: &str) -> String {
@ -45,6 +49,7 @@ impl XvbNode {
Self::NorthAmerica => address.chars().take(8).collect(),
Self::Europe => address.chars().take(8).collect(),
Self::P2pool => GUPAX_VERSION_UNDERSCORE.to_string(),
Self::XmrigProxy => GUPAX_VERSION_UNDERSCORE.to_string(),
}
}
pub fn tls(&self) -> bool {
@ -52,6 +57,7 @@ impl XvbNode {
Self::NorthAmerica => true,
Self::Europe => true,
Self::P2pool => false,
Self::XmrigProxy => false,
}
}
pub fn keepalive(&self) -> bool {
@ -59,6 +65,7 @@ impl XvbNode {
Self::NorthAmerica => true,
Self::Europe => true,
Self::P2pool => false,
Self::XmrigProxy => false,
}
}
@ -105,16 +112,18 @@ impl XvbNode {
// if both nodes are dead, then the state of the process must be NodesOffline
info!("XvB node ping, all offline or ping failed, switching back to local p2pool",);
output_console(
gui_api_xvb,
&mut lock!(gui_api_xvb).output,
"XvB node ping, all offline or ping failed, switching back to local p2pool",
ProcessName::Xvb,
);
lock!(process_xvb).state = ProcessState::OfflineNodesAll;
} else {
// if node is up and because update_fastest is used only if token/address is valid, it means XvB process is Alive.
info!("XvB node ping, both online and best is {}", node.url());
output_console(
gui_api_xvb,
&mut lock!(gui_api_xvb).output,
&format!("XvB node ping, {} is selected as the fastest.", node),
ProcessName::Xvb,
);
info!("ProcessState to Syncing after finding joinable node");
// could be used by xmrig who signal that a node is not joignable

View file

@ -11,7 +11,7 @@ use tokio::time::sleep;
use crate::{
disk::state::ManualDonationLevel,
helper::{xvb::output_console, Process, ProcessState},
helper::{xvb::output_console, Process, ProcessName, ProcessState},
macros::lock,
XVB_URL,
};
@ -131,14 +131,16 @@ impl XvbPrivStats {
XVB_URL, err
);
output_console(
gui_api,
&mut lock!(gui_api).output,
&format!("Failure to retrieve private stats from {}", XVB_URL),
ProcessName::Xvb,
);
lock!(process).state = ProcessState::Retry;
// sleep here because it is in a spawn and will not block the user stopping or restarting the service.
output_console(
gui_api,
&mut lock!(gui_api).output,
"Waiting 10 seconds before trying to get stats again.",
ProcessName::Xvb,
);
sleep(Duration::from_secs(10)).await;
}

View file

@ -10,7 +10,7 @@ use serde_this_or_that::as_u64;
use tokio::time::sleep;
use crate::{
helper::{xvb::output_console, Process, ProcessState},
helper::{xvb::output_console, Process, ProcessName, ProcessState},
macros::lock,
XVB_URL_PUBLIC_API,
};
@ -69,8 +69,9 @@ impl XvbPubStats {
if lock!(process).state == ProcessState::Retry {
lock!(process).state = ProcessState::Syncing;
output_console(
gui_api,
&mut lock!(gui_api).output,
"Stats are now working again after last successful request.",
ProcessName::Xvb,
);
}
}
@ -83,19 +84,21 @@ impl XvbPubStats {
// if error already present, no need to print it multiple times.
if lock!(process).state != ProcessState::Retry {
output_console(
gui_api,
&mut lock!(gui_api).output,
&format!(
"Failure to retrieve public stats from {}\nWill retry shortly...",
XVB_URL_PUBLIC_API
),
ProcessName::Xvb,
);
}
// we stop the algo (will be stopped by the check status on next loop) because we can't make the rest work without public stats. (winner in xvb private stats).
lock!(process).state = ProcessState::Retry;
// sleep here because it is in a spawn and will not block the user stopping or restarting the service.
output_console(
gui_api,
&mut lock!(gui_api).output,
"Waiting 10 seconds before trying to get stats again.",
ProcessName::Xvb,
);
sleep(Duration::from_secs(10)).await;
}

View file

@ -16,6 +16,7 @@ use egui::TextStyle::{Body, Button, Heading, Monospace, Name};
use egui::*;
use env_logger::fmt::style::Style;
use env_logger::{Builder, WriteStyle};
use flexi_logger::{FileSpec, Logger};
use log::LevelFilter;
use std::sync::Arc;
use std::time::Instant;
@ -74,18 +75,25 @@ pub fn init_text_styles(ctx: &egui::Context, width: f32, pixels_per_point: f32)
#[cold]
#[inline(never)]
pub fn init_logger(now: Instant) {
let filter_env = std::env::var("RUST_LOG").unwrap_or_else(|_| "INFO".to_string());
let filter = match filter_env.as_str() {
"error" | "Error" | "ERROR" => LevelFilter::Error,
"warn" | "Warn" | "WARN" => LevelFilter::Warn,
"debug" | "Debug" | "DEBUG" => LevelFilter::Debug,
"trace" | "Trace" | "TRACE" => LevelFilter::Trace,
_ => LevelFilter::Info,
};
std::env::set_var("RUST_LOG", format!("off,gupax={}", filter_env));
pub fn init_logger(now: Instant, logfile: bool) {
if logfile {
Logger::try_with_env_or_str("info")
.unwrap()
.log_to_file(FileSpec::default())
.start()
.unwrap();
} else {
let filter_env = std::env::var("RUST_LOG").unwrap_or_else(|_| "INFO".to_string());
let filter = match filter_env.as_str() {
"error" | "Error" | "ERROR" => LevelFilter::Error,
"warn" | "Warn" | "WARN" => LevelFilter::Warn,
"debug" | "Debug" | "DEBUG" => LevelFilter::Debug,
"trace" | "Trace" | "TRACE" => LevelFilter::Trace,
_ => LevelFilter::Info,
};
std::env::set_var("RUST_LOG", format!("off,gupax={}", filter_env));
Builder::new()
Builder::new()
.format(move |buf, record| {
let level = record.level();
let level_style = buf.default_level_style(level);
@ -105,8 +113,9 @@ pub fn init_logger(now: Instant) {
.parse_default_env()
.format_timestamp_millis()
.init();
info!("Log level ... {}", filter);
}
info!("init_logger() ... OK");
info!("Log level ... {}", filter);
}
#[cold]
@ -214,6 +223,23 @@ pub fn init_auto(app: &mut App) {
} else {
info!("Skipping auto-xmrig...");
}
// [Auto-XMRig-Proxy]
if app.state.gupax.auto_xp {
if !Gupax::path_is_file(&app.state.gupax.xmrig_proxy_path) {
warn!("Gupaxx | Xmrig-Proxy path is not a file! Skipping auto-xmrig_proxy...");
} else if !crate::components::update::check_xp_path(&app.state.gupax.xmrig_proxy_path) {
warn!("Gupaxx | Xmrig-Proxy path is not valid! Skipping auto-xmrig_proxy...");
} else {
Helper::start_xp(
&app.helper,
&app.state.xmrig_proxy,
&app.state.xmrig,
&app.state.gupax.absolute_xp_path,
);
}
} else {
info!("Skipping auto-XMRig-Proxy...");
}
// [Auto-XvB]
if app.state.gupax.auto_xvb {
Helper::start_xvb(
@ -221,6 +247,7 @@ pub fn init_auto(app: &mut App) {
&app.state.xvb,
&app.state.p2pool,
&app.state.xmrig,
&app.state.xmrig_proxy,
);
} else {
info!("Skipping auto-xvb...");

View file

@ -62,7 +62,7 @@ fn main() {
crate::panic::set_panic_hook(now);
// Init logger.
init_logger(now);
init_logger(now, args.logfile);
let mut app = App::new(now, args);
init_auto(&mut app);

View file

@ -1,6 +1,7 @@
//---------------------------------------------------------------------------------------------------- Misc functions
// Get absolute [Gupax] binary path
use std::fmt::Write;
#[cold]
#[inline(never)]
pub fn get_exe() -> Result<String, std::io::Error> {
@ -128,7 +129,9 @@ pub fn cmp_f64(a: f64, b: f64) -> std::cmp::Ordering {
// Free functions.
use crate::disk::gupax_p2pool_api::GupaxP2poolApi;
use crate::helper::ProcessName;
use crate::utils::macros::lock;
use chrono::Local;
use log::error;
use log::warn;
use regex::Regex;
@ -155,3 +158,16 @@ pub fn clamp_scale(scale: f32) -> f32 {
// Clamp between valid range.
scale.clamp(APP_MIN_SCALE, APP_MAX_SCALE)
}
pub fn output_console(output: &mut String, msg: &str, p_name: ProcessName) {
if let Err(e) = writeln!(output, "{}{msg}", datetimeonsole()) {
error!("{} Watchdog | GUI status write failed: {}", p_name, e);
}
}
pub fn output_console_without_time(output: &mut String, msg: &str, p_name: ProcessName) {
if let Err(e) = writeln!(output, "{msg}") {
error!("{} Watchdog | GUI status write failed: {}", p_name, e);
}
}
fn datetimeonsole() -> String {
format!("[{}] ", Local::now().format("%Y-%m-%d %H:%M:%S%.3f"))
}

View file

@ -16,8 +16,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pub const GUPAX_VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); // e.g: v1.0.0
pub const P2POOL_VERSION: &str = "v3.10";
pub const P2POOL_VERSION: &str = "v4.0";
pub const XMRIG_VERSION: &str = "v6.21.1";
pub const XMRIG_PROXY_VERSION: &str = "v6.21.1";
pub const COMMIT: &str = env!("COMMIT"); // set in build.rs
// e.g: Gupax_v1_0_0
// Would have been [Gupax_v1.0.0] but P2Pool truncates everything after [.]
@ -85,8 +86,13 @@ pub const P2POOL_API_PATH_LOCAL: &str = "local/stratum";
pub const P2POOL_API_PATH_NETWORK: &str = "network/stats";
#[cfg(target_family = "unix")]
pub const P2POOL_API_PATH_POOL: &str = "pool/stats";
pub const XMRIG_API_URI: &str = "1/summary"; // The default relative URI of XMRig's API
pub const XMRIG_CONFIG_URI: &str = "http://127.0.0.1:18088/1/config"; // The default relative URI of XMRig's API config
pub const XMRIG_API_SUMMARY_URI: &str = "1/summary"; // The default relative URI of XMRig's API summary
// pub const XMRIG_API_CONFIG_URI: &str = "1/config"; // The default relative URI of XMRig's API config
// todo allow user to change the port of the http api for xmrig and xmrig-proxy
pub const XMRIG_CONFIG_URL: &str = "http://127.0.0.1:18088/1/config"; // The default relative URI of XMRig's API config
pub const XMRIG_PROXY_CONFIG_URL: &str = "http://127.0.0.1:18089/1/config"; // The default relative URI of XMRig Proxy's API config
pub const XMRIG_SUMMARY_URL: &str = "http://127.0.0.1:18088/1/summary"; // The default relative URI of XMRig's API config
pub const XMRIG_PROXY_SUMMARY_URL: &str = "http://127.0.0.1:18089/1/summary"; // The default relative URI of XMRig Proxy's API config
// Process state tooltips (online, offline, etc)
pub const P2POOL_ALIVE: &str = "P2Pool is online and fully synchronized";
@ -102,6 +108,35 @@ pub const XMRIG_FAILED: &str = "XMRig is offline and failed when exiting";
pub const XMRIG_MIDDLE: &str = "XMRig is in the middle of (re)starting/stopping";
pub const XMRIG_NOT_MINING: &str = "XMRig is online, but not mining to any pool";
pub const XMRIG_PROXY_ALIVE: &str = "XMRig-Proxy is online and mining";
pub const XMRIG_PROXY_DEAD: &str = "XMRig-Proxy is offline";
pub const XMRIG_PROXY_FAILED: &str = "XMRig-Proxy is offline and failed when exiting";
pub const XMRIG_PROXY_MIDDLE: &str = "XMRig-Proxy is in the middle of (re)starting/stopping";
pub const XMRIG_PROXY_NOT_MINING: &str = "XMRig-Proxy is online, but not mining to any pool";
pub const XMRIG_PROXY_REDIRECT: &str = "point local xmrig instance on this proxy instead of the p2pool instance (recommended if using XvB)";
pub const XMRIG_PROXY_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!
Start XMRig-Proxy with these arguments"#;
pub const XMRIG_PROXY_INPUT: &str = "Send a command to XMRig-Proxy";
pub const XMRIG_PROXY_SIMPLE: &str = r#"Use simple XMRig-Proxy settings:
- Mine to local P2Pool (localhost:3333)
- redirect Xmrig local instance to the proxy
- HTTP API @ localhost:18089"#;
pub const XMRIG_PROXY_ADVANCED: &str = r#"Use advanced XMRig-Proxy settings:
- Terminal input
- disable/enable local xmrig instance redirection
- Overriding command arguments
- Custom HTTP API IP/Port
- TLS setting
- Keepalive setting"#;
pub const XMRIG_PROXY_PATH_NOT_FILE: &str = "XMRig-Proxy binary not found at the given PATH in the Gupaxx tab! To fix: goto the [Gupaxx Advanced] tab, select [Open] and specify where XMRig-Proxy is located.";
pub const XMRIG_PROXY_PATH_NOT_VALID: &str = "XMRig-Proxy binary at the given PATH in the Gupaxx tab doesn't look like XMRig-Proxy! To fix: goto the [Gupaxx Advanced] tab, select [Open] and specify where XMRig-Proxy is located.";
pub const XMRIG_PROXY_PATH_OK: &str = "XMRig-Proxy was found at the given PATH";
pub const XMRIG_PROXY_PATH_EMPTY: &str = "XMRig-Proxy PATH is empty! To fix: goto the [GupaxxAdvanced] tab, select [Open] and specify where XMRig is located.";
pub const STATUS_XMRIG_PROXY_UPTIME: &str = "How long XMRig-Proxy has been online";
pub const STATUS_XMRIG_PROXY_POOL: &str = "The pool XMRig-Proxy is currently mining to";
pub const STATUS_XMRIG_PROXY_HASHRATE: &str = "The average hashrate of XMRig-Proxy";
pub const XVB_ALIVE: &str =
"XvB process is configured and distributing hashrate, XvB node is online";
pub const XVB_DEAD: &str = "XvB process is offline";
@ -186,7 +221,6 @@ pub const STATUS_P2POOL_POOL: &str = "The P2Pool sidechain you're currently conn
pub const STATUS_P2POOL_ADDRESS: &str = "The Monero address P2Pool will send payouts to";
//--
pub const STATUS_XMRIG_UPTIME: &str = "How long XMRig has been online";
pub const STATUS_XMRIG_CPU: &str = "The average CPU load of XMRig. [1.0] represents 1 thread is maxed out, e.g: If you have 8 threads, [4.0] means half your threads are maxed out.";
pub const STATUS_XMRIG_HASHRATE: &str = "The average hashrate of XMRig";
pub const STATUS_XMRIG_DIFFICULTY: &str = "The current difficulty of the job XMRig is working on";
pub const STATUS_XMRIG_SHARES: &str = "The amount of accepted and rejected shares";
@ -278,6 +312,7 @@ pub const GUPAX_ASK_BEFORE_QUIT: &str = "Ask before quitting Gupaxx";
pub const GUPAX_SAVE_BEFORE_QUIT: &str = "Automatically save any changed settings before quitting";
pub const GUPAX_AUTO_P2POOL: &str = "Automatically start P2Pool on Gupaxx startup. If you are using [P2Pool Simple], this will NOT wait for your [Auto-Ping] to finish, it will start P2Pool on the pool you already have selected. This option will fail if your P2Pool settings aren't valid!";
pub const GUPAX_AUTO_XMRIG: &str = "Automatically start XMRig on Gupaxx startup. This option will fail if your XMRig settings aren't valid!";
pub const GUPAX_AUTO_XMRIG_PROXY: &str = "Automatically start XMRig-Proxy on Gupaxx startup.";
pub const GUPAX_AUTO_XVB: &str = "Automatically start XvB on Gupaxx startup. This option will fail if your XvB settings aren't valid!";
pub const GUPAX_ADJUST: &str = "Adjust and set the width/height of the Gupaxx window";
pub const GUPAX_WIDTH: &str = "Set the width of the Gupaxx window";
@ -310,6 +345,7 @@ pub const GUPAX_ADVANCED: &str = r#"Use advanced Gupaxx settings:
pub const GUPAX_SELECT: &str = "Open a file explorer to select a file";
pub const GUPAX_PATH_P2POOL: &str = "The location of the P2Pool binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path";
pub const GUPAX_PATH_XMRIG: &str = "The location of the XMRig binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path";
pub const GUPAX_PATH_XMRIG_PROXY: &str = "The location of the XMRig-Proxy binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path";
// P2Pool
pub const P2POOL_MAIN: &str = "Use the P2Pool main-chain. This P2Pool finds blocks faster, but has a higher difficulty. Suitable for miners with more than 50kH/s";
@ -409,6 +445,7 @@ pub const XMRIG_PATH_NOT_FILE: &str = "XMRig binary not found at the given PATH
pub const XMRIG_PATH_NOT_VALID: &str = "XMRig binary at the given PATH in the Gupaxxtab doesn't look like XMRig! To fix: goto the [Gupaxx Advanced] tab, select [Open] and specify where XMRig is located.";
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 [GupaxxAdvanced] tab, select [Open] and specify where XMRig is located.";
pub const XMRIG_PROXY_URL: &str = "https://github.com/xmrig/xmrig-proxy";
// XvB
pub const XVB_HELP: &str = "You need to register an account by clicking on the link above to get your token with the same p2pool XMR address you use for payment.";

View file

@ -17,7 +17,7 @@
// Some regexes used throughout Gupax.
use log::error;
use log::{error, warn};
use once_cell::sync::Lazy;
use regex::Regex;
@ -116,13 +116,22 @@ impl P2poolRegex {
pub struct XmrigRegex {
pub not_mining: Regex,
pub new_job: Regex,
pub timeout: Regex,
pub valid_conn: Regex,
pub invalid_conn: Regex,
pub error: Regex,
}
impl XmrigRegex {
fn new() -> Self {
Self {
not_mining: Regex::new("no active pools, stop mining").unwrap(),
timeout: Regex::new("timeout").unwrap(),
new_job: Regex::new("new job").unwrap(),
valid_conn: Regex::new("upstreams active: 1").unwrap(),
invalid_conn: Regex::new("error: 1").unwrap(),
// we don't want to include connections status from xmrig-proxy that show the number of errors
error: Regex::new(r"error: \D").unwrap(),
}
}
}
@ -154,11 +163,11 @@ pub fn nb_current_shares(s: &str) -> Option<u32> {
}
pub fn detect_new_node_xmrig(s: &str) -> Option<XvbNode> {
static CURRENT_SHARE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"net use pool (?P<pool>.*?) ").unwrap());
Lazy::new(|| Regex::new(r"use pool (?P<pool>.*?) ").unwrap());
if let Some(c) = CURRENT_SHARE.captures(s) {
if let Some(m) = c.name("pool") {
match m.as_str() {
// if user change address of local p2pool, it could create issue ?
// if user change address of local p2pool, it could create issue
"127.0.0.1:3333" => {
return Some(XvbNode::P2pool);
}
@ -172,7 +181,7 @@ pub fn detect_new_node_xmrig(s: &str) -> Option<XvbNode> {
}
}
}
error!("a line on xmrig console was detected as using a new pool but the syntax was not recognized.");
warn!("a line on xmrig console was detected as using a new pool but the syntax was not recognized or it was not a pool useable for the algorithm.");
None
}
pub fn estimated_hr(s: &str) -> Option<f32> {
@ -210,6 +219,11 @@ pub fn estimated_hr(s: &str) -> Option<f32> {
}
None
}
pub fn contains_timeout(l: &str) -> bool {
static LINE_SHARE: Lazy<Regex> = Lazy::new(|| Regex::new(r"timeout").unwrap());
LINE_SHARE.is_match(l)
}
pub fn contains_error(l: &str) -> bool {
static LINE_SHARE: Lazy<Regex> = Lazy::new(|| Regex::new(r"error").unwrap());
LINE_SHARE.is_match(l)

View file

@ -41,31 +41,57 @@ mv xmrig-6.21.1/xmrig.exe /tmp/${FOLDER}/skel/windows_b/XMRig/xmrig.exe
rm -r xmrig-6.21.1
rm xmrig-6.21.1-msvc-win64.zip
## Download XMRig-Proxy Binaries
wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.21.1/xmrig-proxy-6.21.1-jammy-x64.tar.gz
tar -xf xmrig-proxy-6.21.1-jammy-x64.tar.gz
mv xmrig-proxy-6.21.1/xmrig-proxy /tmp/${FOLDER}/skel/linux_b/xmrig-proxy/xmrig-proxy
rm -r xmrig-proxy-6.21.1
rm xmrig-proxy-6.21.1-jammy-x64.tar.gz
## no release for arm64 mac-osx xmrig-proxy, todo make CI build it.
# download xmrig into directory macos-arm64
# wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.21.1/xmrig-proxy-6.21.1-macos-arm64.tar.gz
# tar -xf xmrig-proxy-6.21.1-macos-arm64.tar.gz
# mv xmrig-proxy-6.21.1/xmrig-proxy /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy
# rm -r xmrig-proxy-6.21.1
# rm xmrig-proxy-6.21.1-macos-arm64.tar.gz
# download xmrig into directory macos-x64
wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.21.1/xmrig-proxy-6.21.1-macos-x64.tar.gz
tar -xf xmrig-proxy-6.21.1-macos-x64.tar.gz
mv xmrig-proxy-6.21.1/xmrig-proxy /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy
rm -r xmrig-proxy-6.21.1
rm xmrig-proxy-6.21.1-macos-x64.tar.gz
# download xmrig into directory windows
wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.21.1/xmrig-proxy-6.21.1-msvc-win64.zip
unzip xmrig-proxy-6.21.1-msvc-win64.zip
mv xmrig-proxy-6.21.1/xmrig-proxy.exe /tmp/${FOLDER}/skel/windows_b/XMRig-Proxy/xmrig-proxy.exe
rm -r xmrig-proxy-6.21.1
rm xmrig-proxy-6.21.1-msvc-win64.zip
## Download P2Pool Binaries
# download p2pool into directory linux
wget https://github.com/SChernykh/p2pool/releases/download/v3.10/p2pool-v3.10-linux-x64.tar.gz
tar -xf p2pool-v3.10-linux-x64.tar.gz
mv p2pool-v3.10-linux-x64/p2pool /tmp/${FOLDER}/skel/linux_b/p2pool/p2pool
rm -r p2pool-v3.10-linux-x64
rm p2pool-v3.10-linux-x64.tar.gz
wget https://github.com/SChernykh/p2pool/releases/download/v4.0/p2pool-v4.0-linux-x64.tar.gz
tar -xf p2pool-v4.0-linux-x64.tar.gz
mv p2pool-v4.0-linux-x64/p2pool /tmp/${FOLDER}/skel/linux_b/p2pool/p2pool
rm -r p2pool-v4.0-linux-x64
rm p2pool-v4.0-linux-x64.tar.gz
# download p2pool into directory macos-arm64
wget https://github.com/SChernykh/p2pool/releases/download/v3.10/p2pool-v3.10-macos-aarch64.tar.gz
tar -xf p2pool-v3.10-macos-aarch64.tar.gz
mv p2pool-v3.10-macos-aarch64/p2pool /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool
rm -r p2pool-v3.10-macos-aarch64
rm p2pool-v3.10-macos-aarch64.tar.gz
wget https://github.com/SChernykh/p2pool/releases/download/v4.0/p2pool-v4.0-macos-aarch64.tar.gz
tar -xf p2pool-v4.0-macos-aarch64.tar.gz
mv p2pool-v4.0-macos-aarch64/p2pool /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool
rm -r p2pool-v4.0-macos-aarch64
rm p2pool-v4.0-macos-aarch64.tar.gz
# download p2pool into directory macos-x64
wget https://github.com/SChernykh/p2pool/releases/download/v3.10/p2pool-v3.10-macos-x64.tar.gz
tar -xf p2pool-v3.10-macos-x64.tar.gz
mv p2pool-v3.10-macos-x64/p2pool /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool
rm -r p2pool-v3.10-macos-x64
rm p2pool-v3.10-macos-x64.tar.gz
wget https://github.com/SChernykh/p2pool/releases/download/v4.0/p2pool-v4.0-macos-x64.tar.gz
tar -xf p2pool-v4.0-macos-x64.tar.gz
mv p2pool-v4.0-macos-x64/p2pool /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool
rm -r p2pool-v4.0-macos-x64
rm p2pool-v4.0-macos-x64.tar.gz
# download p2pool into directory windows
wget https://github.com/SChernykh/p2pool/releases/download/v3.10/p2pool-v3.10-windows-x64.zip
unzip p2pool-v3.10-windows-x64.zip
mv p2pool-v3.10-windows-x64/p2pool.exe /tmp/${FOLDER}/skel/windows_b/P2Pool/p2pool.exe
rm -r p2pool-v3.10-windows-x64
rm p2pool-v3.10-windows-x64.zip
wget https://github.com/SChernykh/p2pool/releases/download/v4.0/p2pool-v4.0-windows-x64.zip
unzip p2pool-v4.0-windows-x64.zip
mv p2pool-v4.0-windows-x64/p2pool.exe /tmp/${FOLDER}/skel/windows_b/P2Pool/p2pool.exe
rm -r p2pool-v4.0-windows-x64
rm p2pool-v4.0-windows-x64.zip
set +ex

View file

@ -33,22 +33,31 @@ title "Linux folder check"
[[ -f linux_b/gupaxx ]]; check "linux_b/gupaxx"
[[ -f linux_b/p2pool/p2pool ]]; check "linux_b/p2pool/p2pool"
[[ -f linux_b/xmrig/xmrig ]]; check "linux_b/xmrig/xmrig"
[[ -f linux_b/xmrig-proxy/xmrig-proxy ]]; check "linux_b/xmrig-proxy/xmrig-proxy"
title "macOS-x64 folder check"
[[ -d macos-x64/Gupaxx.app ]]; check "macos-x64/Gupaxx.app"
[[ -d macos-x64_b/Gupaxx.app ]]; check "macos-x64_b/Gupaxx.app"
[[ -f macos-x64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool ]]; check "macos-x64_b/p2pool/p2pool"
[[ -f macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig ]]; check "macos-x64_b/xmrig/xmrig"
[[ -f macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy ]]; check "macos-x64_b/xmrig-proxy/xmrig-proxy"
title "macOS-arm64 folder check"
[[ -d macos-arm64/Gupaxx.app ]]; check "macos-arm64/Gupaxx.app"
[[ -d macos-arm64_b/Gupaxx.app ]]; check "macos-arm64_b/Gupaxx.app"
[[ -f macos-arm64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool ]]; check "macos-arm64_b/p2pool/p2pool"
[[ -f macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig ]]; check "macos-arm64_b/xmrig/xmrig"
## no macos-arm64 xmrig-proxy released todo
# [[ -f macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy ]]; check "macos-arm64_b/xmrig-proxy/xmrig-proxy"
title "Windows folder check"
[[ -f windows/Gupaxx.exe ]]; check "windows/Gupaxx.exe"
[[ -f windows_b/Gupaxx.exe ]]; check "windows_b/Gupaxx.exe"
[[ -f windows_b/P2Pool/p2pool.exe ]]; check "windows_b/P2Pool/p2pool.exe"
[[ -f windows_b/XMRig/xmrig.exe ]]; check "windows_b/XMRig/xmrig.exe"
[[ -f windows_b/XMRig-Proxy/xmrig-proxy.exe ]]; check "windows_b/XMRig-Proxy/xmrig-proxy.exe"
# Get random date for tar/zip
title "RNG Date"
RNG=$((EPOCHSECONDS-RANDOM*4)); check "RNG ... $RNG"
DATE=$(date -d @${RNG}); check "DATE ... $DATE"
# Tar Linux Bundle
title "Tar Linux"
@ -57,6 +66,7 @@ chmod +x linux/gupaxx
chmod +x linux_b/gupaxx
chmod +x linux_b/p2pool/p2pool
chmod +x linux_b/xmrig/xmrig
chmod +x linux_b/xmrig-proxy/xmrig-proxy
mv linux_b "gupaxx-$NEW_VER-linux-x64-bundle"; check "linux -> gupaxx-$NEW_VER-linux-x64-bundle"
tar -czpf "gupaxx-${NEW_VER}-linux-x64-bundle.tar.gz" "gupaxx-$NEW_VER-linux-x64-bundle" --owner=lm --group=lm ; check "tar linux-bundle"
# Tar Linux Standalone

View file

@ -18,6 +18,7 @@ OLD_VER_NUM="$(grep -m1 "version" Cargo.toml | grep -o "[0-9].[0-9].[0-9]")"
# get p2pool/xmrig version
P2POOL_VERSION="$(grep "P2POOL_VERSION" src/constants.rs | grep -o "\"v[0-9].*\"")"
XMRIG_VERSION="$(grep "XMRIG_VERSION" src/constants.rs | grep -o "\"v[0-9].*\"")"
XMRIG_PROXY_VERSION="$(grep "XMRIG_PROXY_VERSION" src/constants.rs | grep -o "\"v[0-9].*\"")"
# sed change
sed -i "s/$OLD_VER/$1/g" README.md
@ -35,6 +36,7 @@ cat << EOM > CHANGELOG.md.new
## Bundled Versions
* [\`P2Pool ${P2POOL_VERSION//\"/}\`](https://github.com/SChernykh/p2pool/releases/tag/${P2POOL_VERSION//\"/})
* [\`XMRig ${XMRIG_VERSION//\"/}\`](https://github.com/xmrig/xmrig/releases/tag/${XMRIG_VERSION//\"/})
* [\`XMRig_Proxy ${XMRIG_PROXY_VERSION//\"/}\`](https://github.com/xmrig/xmrig-proxy/releases/tag/${XMRIG_PROXY_VERSION//\"/})
---