diff --git a/src/app/eframe_impl.rs b/src/app/eframe_impl.rs index dd4a0aa..b8048cd 100644 --- a/src/app/eframe_impl.rs +++ b/src/app/eframe_impl.rs @@ -52,6 +52,12 @@ impl eframe::App for App { let xvb_is_waiting = xvb.is_waiting(); let xvb_state = xvb.state; drop(xvb); + debug!("App | Locking and collecting Node state..."); + let node = lock!(self.node); + let node_is_alive = node.is_alive(); + let node_is_waiting = node.is_waiting(); + let node_state = node.state; + drop(node); // This sets the top level Ui dimensions. // Used as a reference for other uis. @@ -101,6 +107,7 @@ impl eframe::App for App { self.top_panel(ctx); self.bottom_panel( ctx, + node_state, p2pool_state, xmrig_state, xmrig_proxy_state, @@ -109,10 +116,12 @@ impl eframe::App for App { wants_input, p2pool_is_waiting, xmrig_is_waiting, + node_is_waiting, xmrig_proxy_is_waiting, xvb_is_waiting, p2pool_is_alive, xmrig_is_alive, + node_is_alive, xmrig_proxy_is_alive, xvb_is_alive, ); @@ -124,6 +133,7 @@ impl eframe::App for App { ctx, frame, key, + node_is_alive, p2pool_is_alive, xmrig_is_alive, xmrig_proxy_is_alive, diff --git a/src/app/keys.rs b/src/app/keys.rs index 5927a0e..1984eec 100644 --- a/src/app/keys.rs +++ b/src/app/keys.rs @@ -126,7 +126,8 @@ impl App { Tab::About => self.tab = Tab::Xvb, Tab::Status => self.tab = Tab::About, Tab::Gupax => self.tab = Tab::Status, - Tab::P2pool => self.tab = Tab::Gupax, + Tab::Node => self.tab = Tab::Gupax, + Tab::P2pool => self.tab = Tab::Node, Tab::Xmrig => self.tab = Tab::P2pool, Tab::XmrigProxy => self.tab = Tab::Xmrig, Tab::Xvb => self.tab = Tab::XmrigProxy, @@ -136,7 +137,8 @@ impl App { match self.tab { Tab::About => self.tab = Tab::Status, Tab::Status => self.tab = Tab::Gupax, - Tab::Gupax => self.tab = Tab::P2pool, + Tab::Gupax => self.tab = Tab::Node, + Tab::Node => self.tab = Tab::P2pool, Tab::P2pool => self.tab = Tab::Xmrig, Tab::Xmrig => self.tab = Tab::XmrigProxy, Tab::XmrigProxy => self.tab = Tab::Xvb, @@ -151,6 +153,7 @@ impl App { Submenu::Benchmarks => self.state.status.submenu = Submenu::P2pool, }, Tab::Gupax => flip!(self.state.gupax.simple), + Tab::Node => flip!(self.state.node.simple), Tab::P2pool => flip!(self.state.p2pool.simple), Tab::Xmrig => flip!(self.state.xmrig.simple), Tab::XmrigProxy => flip!(self.state.xmrig_proxy.simple), @@ -170,6 +173,7 @@ impl App { Tab::Xmrig => flip!(self.state.xmrig.simple), Tab::XmrigProxy => flip!(self.state.xmrig_proxy.simple), Tab::Xvb => flip!(self.state.xvb.simple), + Tab::Node => flip!(self.state.node.simple), Tab::About => (), }; } diff --git a/src/app/mod.rs b/src/app/mod.rs index 7e57f80..887d204 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -16,6 +16,7 @@ use crate::disk::state::State; use crate::errors::ErrorButtons; use crate::errors::ErrorFerris; use crate::errors::ErrorState; +use crate::helper::node::PubNodeApi; use crate::helper::p2pool::ImgP2pool; use crate::helper::p2pool::PubP2poolApi; use crate::helper::xrig::xmrig::ImgXmrig; @@ -103,10 +104,12 @@ pub struct App { // This includes the "helper" threads public P2Pool/XMRig's API. pub helper: Arc>, // [Helper] state, mostly for Gupax uptime pub pub_sys: Arc>, // [Sys] state, read by [Status], mutated by [Helper] + pub node: Arc>, // [Node] process state pub p2pool: Arc>, // [P2Pool] process state pub xmrig: Arc>, // [XMRig] process state pub xmrig_proxy: Arc>, // [XMRig-Proxy] process state pub xvb: Arc>, // [Xvb] process state + pub node_api: Arc>, // Public ready-to-print node API made by the "helper" thread pub p2pool_api: Arc>, // Public ready-to-print P2Pool API made by the "helper" thread pub xmrig_api: Arc>, // Public ready-to-print XMRig API made by the "helper" thread pub xmrig_proxy_api: Arc>, // Public ready-to-print XMRigProxy API made by the "helper" thread @@ -114,8 +117,9 @@ pub struct App { pub p2pool_img: Arc>, // A one-time snapshot of what data P2Pool started with pub xmrig_img: Arc>, // A one-time snapshot of what data XMRig started with // STDIN Buffer + pub node_stdin: String, // The buffer between the node console and the [Helper] 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_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>, // This is just a dummy struct on [Windows]. @@ -200,10 +204,16 @@ impl App { String::new(), PathBuf::new() )); + let node = arc_mut!(Process::new( + ProcessName::Node, + String::new(), + PathBuf::new() + )); 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 node_api = arc_mut!(PubNodeApi::new()); let p2pool_img = arc_mut!(ImgP2pool::new()); let xmrig_img = arc_mut!(ImgXmrig::new()); @@ -248,6 +258,7 @@ impl App { String::new(), PathBuf::new(), PathBuf::new(), + PathBuf::new(), PathBuf::new() )), file_window: FileWindow::new(), @@ -265,24 +276,29 @@ impl App { xmrig.clone(), xmrig_proxy.clone(), xvb.clone(), + node.clone(), p2pool_api.clone(), xmrig_api.clone(), xvb_api.clone(), xmrig_proxy_api.clone(), + node_api.clone(), p2pool_img.clone(), xmrig_img.clone(), arc_mut!(GupaxP2poolApi::new()) )), + node, p2pool, xmrig, xmrig_proxy, xvb, + node_api, p2pool_api, xvb_api, xmrig_api, xmrig_proxy_api, p2pool_img, xmrig_img, + node_stdin: String::with_capacity(10), p2pool_stdin: String::with_capacity(10), xmrig_stdin: String::with_capacity(10), xmrig_proxy_stdin: String::with_capacity(10), @@ -555,6 +571,7 @@ impl App { // Apply TOML values to [Update] info!("App Init | Applying TOML values to [Update]..."); + let node_path = og.gupax.absolute_node_path.clone(); let p2pool_path = og.gupax.absolute_p2pool_path.clone(); let xmrig_path = og.gupax.absolute_xmrig_path.clone(); let xmrig_proxy_path = og.gupax.absolute_xp_path.clone(); @@ -562,7 +579,8 @@ impl App { app.exe.clone(), p2pool_path, xmrig_path, - xmrig_proxy_path + xmrig_proxy_path, + node_path )); // Set state version as compiled in version @@ -688,6 +706,7 @@ pub enum Tab { About, Status, Gupax, + Node, P2pool, Xmrig, XmrigProxy, diff --git a/src/app/panels/bottom.rs b/src/app/panels/bottom.rs index 57ffffe..b8245d4 100644 --- a/src/app/panels/bottom.rs +++ b/src/app/panels/bottom.rs @@ -22,6 +22,7 @@ impl crate::app::App { pub fn bottom_panel( &mut self, ctx: &egui::Context, + node_state: ProcessState, p2pool_state: ProcessState, xmrig_state: ProcessState, xmrig_proxy_state: ProcessState, @@ -30,10 +31,12 @@ impl crate::app::App { wants_input: bool, p2pool_is_waiting: bool, xmrig_is_waiting: bool, + node_is_waiting: bool, xmrig_proxy_is_waiting: bool, xvb_is_waiting: bool, p2pool_is_alive: bool, xmrig_is_alive: bool, + node_is_alive: bool, xmrig_proxy_is_alive: bool, xvb_is_alive: bool, ) { @@ -74,6 +77,8 @@ impl crate::app::App { ui.label(self.os); // ui.add_sized(size, Label::new(self.os)); ui.separator(); + status_node(node_state, ui, size); + ui.separator(); status_p2pool(p2pool_state, ui, size); ui.separator(); status_xmrig(xmrig_state, ui, size); @@ -96,6 +101,17 @@ impl crate::app::App { Tab::Gupax => { self.gupax_submenu(ui, height); } + Tab::Node => { + self.node_submenu(ui, size); + self.node_run_actions( + ui, + height, + node_is_waiting, + node_is_alive, + wants_input, + key, + ); + } Tab::P2pool => { self.p2pool_submenu(ui, size); self.p2pool_run_actions( @@ -309,6 +325,30 @@ impl crate::app::App { } }); } + fn node_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.node.simple, "Advanced"), + ) + .on_hover_text(NODE_ADVANCED) + .clicked() + { + self.state.node.simple = false; + } + ui.separator(); + if ui + .add_sized(size, SelectableLabel::new(self.state.node.simple, "Simple")) + .on_hover_text(NODE_SIMPLE) + .clicked() + { + self.state.node.simple = true; + } + }); + } fn p2pool_run_actions( &mut self, ui: &mut Ui, @@ -405,6 +445,95 @@ impl crate::app::App { } }); } + fn node_run_actions( + &mut self, + ui: &mut Ui, + height: f32, + node_is_waiting: bool, + node_is_alive: bool, + wants_input: bool, + key: &KeyPressed, + ) { + ui.group(|ui| { + let width = ((ui.available_width() / 3.0) - 5.0).max(0.0); + let size = vec2(width, height); + if node_is_waiting { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text(NODE_MIDDLE); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text(NODE_MIDDLE); + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text(NODE_MIDDLE); + }); + } else if node_is_alive { + if key.is_up() && !wants_input + || ui + .add_sized(size, Button::new("⟲")) + .on_hover_text("Restart node") + .clicked() + { + let _ = lock!(self.og).update_absolute_path(); + let _ = self.state.update_absolute_path(); + Helper::restart_node( + &self.helper, + &self.state.node, + &self.state.gupax.absolute_node_path, + ); + } + if key.is_down() && !wants_input + || ui + .add_sized(size, Button::new("⏹")) + .on_hover_text("Stop node") + .clicked() + { + Helper::stop_node(&self.helper); + } + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text("Start node"); + }); + } else { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text("Restart node"); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text("Stop node"); + }); + // Check if path is okay before allowing to start. + let mut text = String::new(); + let mut ui_enabled = true; + if !Gupax::path_is_file(&self.state.gupax.node_path) { + ui_enabled = false; + text = format!("Error: {}", NODE_PATH_NOT_FILE); + } else if !crate::components::update::check_node_path(&self.state.gupax.node_path) { + ui_enabled = false; + text = format!("Error: {}", NODE_PATH_NOT_VALID); + } else if process_running(crate::helper::ProcessName::Node) { + ui_enabled = false; + text = format!("Error: {}", PROCESS_OUTSIDE); + } + ui.add_enabled_ui(ui_enabled, |ui| { + 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 Node") + .on_disabled_hover_text(text) + .clicked() + { + let _ = lock!(self.og).update_absolute_path(); + let _ = self.state.update_absolute_path(); + Helper::start_node( + &self.helper, + &self.state.node, + &self.state.gupax.absolute_node_path, + ); + } + }); + } + }); + } fn xmrig_submenu(&mut self, ui: &mut Ui, size: Vec2) { ui.group(|ui| { let width = size.x / 1.5; @@ -790,6 +919,32 @@ fn status_p2pool(state: ProcessState, ui: &mut Ui, size: Vec2) { }; status(ui, color, hover_text, size, "P2pool ⏺"); } +fn status_node(state: ProcessState, ui: &mut Ui, size: Vec2) { + let color; + let hover_text = match state { + Alive => { + color = GREEN; + NODE_ALIVE + } + Dead => { + color = GRAY; + NODE_DEAD + } + Failed => { + color = RED; + NODE_FAILED + } + Syncing => { + color = ORANGE; + NODE_SYNCING + } + Middle | Waiting | NotMining | OfflineNodesAll | Retry => { + color = YELLOW; + NODE_MIDDLE + } + }; + status(ui, color, hover_text, size, "Node ⏺"); +} fn status_xmrig(state: ProcessState, ui: &mut Ui, size: Vec2) { let color; diff --git a/src/app/panels/middle/gupax.rs b/src/app/panels/middle/gupax.rs index fa26cc8..0c121c4 100644 --- a/src/app/panels/middle/gupax.rs +++ b/src/app/panels/middle/gupax.rs @@ -88,43 +88,48 @@ impl Gupax { debug!("Gupaxx Tab | Rendering bool buttons"); ui.horizontal(|ui| { 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_xp, "Auto-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); + egui::ScrollArea::horizontal().show(ui, |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_node, "Auto-Node")) + .on_hover_text(GUPAX_AUTO_NODE); + 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_xp, "Auto-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); + }); }); }); @@ -140,13 +145,51 @@ impl Gupax { ui.add_sized( [ui.available_width(), height / 2.0], Label::new( - RichText::new("P2Pool/XMRig/XMRig-Proxy PATHs") + RichText::new("Node/P2Pool/XMRig/XMRig-Proxy PATHs") .underline() .color(LIGHT_GRAY), ), ) .on_hover_text("Gupaxx is online"); ui.separator(); + ui.horizontal(|ui| { + if self.node_path.is_empty() { + ui.add_sized( + [text_edit, height], + Label::new(RichText::new("Node Binary Path ➖").color(LIGHT_GRAY)), + ) + .on_hover_text(NODE_PATH_EMPTY); + } else if !Self::path_is_file(&self.node_path) { + ui.add_sized( + [text_edit, height], + Label::new(RichText::new("Node Binary Path ❌").color(RED)), + ) + .on_hover_text(NODE_PATH_NOT_FILE); + } else if !crate::components::update::check_node_path(&self.node_path) { + ui.add_sized( + [text_edit, height], + Label::new(RichText::new("Node Binary Path ❌").color(RED)), + ) + .on_hover_text(NODE_PATH_NOT_VALID); + } else { + ui.add_sized( + [text_edit, height], + Label::new(RichText::new("Node Binary Path ✔").color(GREEN)), + ) + .on_hover_text(NODE_PATH_OK); + } + ui.spacing_mut().text_edit_width = ui.available_width() - SPACE; + ui.add_enabled_ui(!lock!(file_window).thread, |ui| { + if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { + Self::spawn_file_window_thread(file_window, FileType::Node); + } + ui.add_sized( + [ui.available_width(), height], + TextEdit::singleline(&mut self.node_path), + ) + .on_hover_text(GUPAX_PATH_NODE); + }); + }); ui.horizontal(|ui| { if self.p2pool_path.is_empty() { ui.add_sized( @@ -189,25 +232,25 @@ impl Gupax { if self.xmrig_path.is_empty() { ui.add_sized( [text_edit, height], - Label::new(RichText::new(" XMRig Binary Path ➖").color(LIGHT_GRAY)), + 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)), + 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)), + 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)), + Label::new(RichText::new("XMRig Binary Path ✔").color(GREEN)), ) .on_hover_text(XMRIG_PATH_OK); } @@ -228,26 +271,26 @@ impl Gupax { ui.add_sized( [text_edit, height], Label::new( - RichText::new(" XMRig-Proxy Binary Path ➖").color(LIGHT_GRAY), + 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)), + 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)), + 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)), + Label::new(RichText::new("XMRig-Proxy Binary Path ✔").color(GREEN)), ) .on_hover_text(XMRIG_PROXY_PATH_OK); } @@ -273,6 +316,14 @@ impl Gupax { self.xmrig_path.clone_from(&guard.xmrig_path); guard.picked_xmrig = false; } + if guard.picked_xp { + self.xmrig_proxy_path.clone_from(&guard.xmrig_proxy_path); + guard.picked_xp = false; + } + if guard.picked_node { + self.node_path.clone_from(&guard.node_path); + guard.picked_node = false; + } drop(guard); let height = ui.available_height() / 6.0; @@ -280,7 +331,7 @@ impl Gupax { // Saved [Tab] debug!("Gupaxx Tab | Rendering [Tab] selector"); ui.group(|ui| { - let width = (size.x / 6.0) - (SPACE * 1.93); + let width = (size.x / 7.0) - (SPACE * 1.93); let size = vec2(width, height); ui.add_sized( [ui.available_width(), height / 2.0], @@ -316,6 +367,14 @@ impl Gupax { self.tab = Tab::Gupax; } ui.separator(); + if ui + .add_sized(size, SelectableLabel::new(self.tab == Tab::Node, "Node")) + .on_hover_text(GUPAX_TAB_NODE) + .clicked() + { + self.tab = Tab::Node; + } + ui.separator(); if ui .add_sized( size, diff --git a/src/app/panels/middle/mod.rs b/src/app/panels/middle/mod.rs index d23ab38..6f67b3d 100644 --- a/src/app/panels/middle/mod.rs +++ b/src/app/panels/middle/mod.rs @@ -8,6 +8,7 @@ use egui::*; use log::debug; mod gupax; +mod node; mod p2pool; mod status; mod xmrig; @@ -20,6 +21,7 @@ impl crate::app::App { ctx: &egui::Context, frame: &mut eframe::Frame, key: KeyPressed, + node_is_alive: bool, p2pool_is_alive: bool, xmrig_is_alive: bool, xmrig_proxy_is_alive: bool, @@ -156,12 +158,16 @@ 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.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); + crate::disk::state::Status::show(&mut self.state.status, &self.pub_sys, &self.node_api, &self.p2pool_api, &self.xmrig_api,&self.xmrig_proxy_api, &self.xvb_api,&self.p2pool_img, &self.xmrig_img, node_is_alive, 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"); crate::disk::state::Gupax::show(&mut self.state.gupax, &self.og, &self.state_path, &self.update, &self.file_window, &mut self.error_state, &self.restart, self.size, frame, ctx, ui); } + Tab::Node=> { + debug!("App | Entering [Node] Tab"); + crate::disk::state::Node::show(&mut self.state.node, &self.node, &self.node_api, &mut self.node_stdin, self.size, &self.file_window, ui); + } Tab::P2pool => { debug!("App | Entering [P2Pool] Tab"); crate::disk::state::P2pool::show(&mut self.state.p2pool, &mut self.node_vec, &self.og, &self.ping, &self.p2pool, &self.p2pool_api, &mut self.p2pool_stdin, self.size, ctx, ui); diff --git a/src/app/panels/middle/node.rs b/src/app/panels/middle/node.rs new file mode 100644 index 0000000..6a169be --- /dev/null +++ b/src/app/panels/middle/node.rs @@ -0,0 +1,407 @@ +use crate::{ + GUPAX_SELECT, NODE_API_BIND, NODE_API_PORT, NODE_ARGUMENTS, NODE_DB_DIR, NODE_DB_PATH_EMPTY, + NODE_DNS_BLOCKLIST, NODE_DNS_CHECKPOINT, NODE_INPUT, NODE_PATH_OK, NODE_PRUNNING, NODE_URL, + NODE_ZMQ_BIND, NODE_ZMQ_PORT, +}; +use egui::{Color32, Label, RichText, Slider, TextEdit, Ui, Vec2}; +use regex::Regex; +use std::sync::{Arc, Mutex}; + +use egui::TextStyle::{self, Name}; +use log::debug; + +use crate::components::gupax::{FileType, FileWindow}; +use crate::disk::state::{Gupax, Node}; +use crate::helper::node::PubNodeApi; +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, P2POOL_IN, P2POOL_LOG, P2POOL_OUT, RED, SPACE}; + +impl Node { + #[inline(always)] // called once + pub fn show( + &mut self, + process: &Arc>, + api: &Arc>, + buffer: &mut String, + size: Vec2, + file_window: &Arc>, + ui: &mut egui::Ui, + ) { + let width = size.x; + let height = size.y; + let space_h = height / 48.0; + let text_height = size.y / 25.0; + let txt_description_width = size.x * 0.1; + egui::ScrollArea::vertical().show(ui, |ui| { + ui.vertical_centered(|ui| { + ui.add_space(space_h); + ui.style_mut().override_text_style = Some(TextStyle::Heading); + ui.hyperlink_to("Monerod", NODE_URL); + ui.style_mut().override_text_style = Some(TextStyle::Body); + ui.add(Label::new("C++ Monero Node")); + ui.add_space(space_h); + }); + // console output for log + debug!("Node 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_height], + TextEdit::hint_text( + TextEdit::singleline(buffer), + r#"Commands: help, status, set_log , diff"#, + ), + ) + .on_hover_text(NODE_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!("Node Tab | Rendering [Arguments]"); + ui.group(|ui| { + ui.horizontal(|ui| { + ui.add_sized( + [txt_description_width, text_height], + Label::new("Command arguments:"), + ); + ui.add_sized( + [ui.available_width(), text_height], + TextEdit::hint_text( + TextEdit::singleline(&mut self.arguments), + r#"--zmq-pub tcp://127.0.0.1:18081"#, + ), + ) + .on_hover_text(NODE_ARGUMENTS); + self.arguments.truncate(1024); + }) + }); + if !self.arguments.is_empty() { + ui.disable(); + } + //---------------------------------------------------------------------------------------------------- Prunned checkbox + 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.pruned, "Prunned") + .on_hover_text(NODE_PRUNNING); + + ui.add_space(space_h); + // 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 + // text edit width is 4x bigger than description. Which makes half of the total width on screen less a space. + + // (width - (width - ui.available_width()) - (ui.spacing().item_spacing.x * 4.5)) + // / 2.0; + ui.horizontal(|ui| { + ui.group(|ui| { + ui.vertical(|ui| { + rpc_bind_field(self, ui, txt_description_width, text_height, width); + rpc_port_field(self, ui, txt_description_width, text_height, width); + ui.add_space(space_h); + zmq_bind_field(self, ui, txt_description_width, text_height, width); + zmq_port_field(self, ui, txt_description_width, text_height, width); + }); + }); + + //---------------------------------------------------------------------------------------------------- In/Out peers + debug!("Node Tab | Rendering sliders elements"); + ui.vertical(|ui| { + ui.group(|ui| { + ui.style_mut().override_text_style = + Some(Name("MonospaceSmall".into())); + ui.horizontal(|ui| { + // ui.label("Out peers [10-450]:"); + ui.add_sized( + [txt_description_width, text_height], + Label::new("Out peers [10-450]:"), + ); + // not sure what's the right calculation to make + ui.style_mut().spacing.slider_width = ui.available_width() + - ui.spacing().item_spacing.x * 4.0 + - ui.spacing().scroll.bar_width + - (SPACE * 2.0); + ui.add(Slider::new(&mut self.out_peers, 10..=450)) + .on_hover_text(P2POOL_OUT); + // ui.add_space(ui.available_width() - 4.0); + }); + ui.horizontal(|ui| { + // ui.label("In peers [10-450]:"); + ui.add_sized( + [txt_description_width, text_height], + Label::new("In peers [10-450]:"), + ); + ui.style_mut().spacing.slider_width = ui.available_width() + - ui.spacing().item_spacing.x * 4.0 + - ui.spacing().scroll.bar_width + - (SPACE * 2.0); + ui.add(Slider::new(&mut self.in_peers, 10..=450)) + .on_hover_text(P2POOL_IN); + }); + ui.horizontal(|ui| { + // ui.label("Log level [ 0-4 ]:"); + ui.add_sized( + [txt_description_width, text_height], + Label::new("Log level [ 0-4 ] :"), + ); + ui.style_mut().spacing.slider_width = ui.available_width() + - ui.spacing().item_spacing.x * 4.0 + - ui.spacing().scroll.bar_width + - (SPACE * 2.0); + ui.add(Slider::new(&mut self.log_level, 0..=4)) + .on_hover_text(P2POOL_LOG); + }); + }); + }); + }); + //---------------------------------------------------------------------------------------------------- DB path + ui.add_space(space_h); + ui.group(|ui| { + path_db_field(self, ui, txt_description_width, text_height, file_window); + }); + ui.add_space(space_h); + debug!("Node Tab | Rendering DNS buttons"); + ui.horizontal(|ui| { + ui.group(|ui| { + ui.checkbox(&mut self.dns_blocklist, "DNS blocklist") + .on_hover_text(NODE_DNS_BLOCKLIST); + ui.separator(); + ui.checkbox(&mut self.disable_dns_checkpoint, "DNS checkpoint") + .on_hover_text(NODE_DNS_CHECKPOINT); + }); + }); + } + }); + } +} + +fn rpc_bind_field( + state: &mut Node, + ui: &mut Ui, + txt_description_width: f32, + text_height: f32, + width: f32, +) { + state_edit_field( + &mut state.api_ip, + ui, + txt_description_width, + text_height, + width, + "RPC BIND IP ", + 255, + NODE_API_BIND, + vec![®EXES.ipv4, ®EXES.domain], + ); +} + +fn rpc_port_field( + state: &mut Node, + ui: &mut Ui, + txt_description_width: f32, + text_height: f32, + width: f32, +) { + state_edit_field( + &mut state.api_port, + ui, + txt_description_width, + text_height, + width, + " RPC PORT ", + 5, + NODE_API_PORT, + vec![®EXES.port], + ); +} +fn zmq_bind_field( + state: &mut Node, + ui: &mut Ui, + txt_description_width: f32, + text_height: f32, + width: f32, +) { + state_edit_field( + &mut state.zmq_ip, + ui, + txt_description_width, + text_height, + width, + "API BIND IP ", + 255, + NODE_ZMQ_BIND, + vec![®EXES.ipv4, ®EXES.domain], + ); +} +fn zmq_port_field( + state: &mut Node, + ui: &mut Ui, + txt_description_width: f32, + text_height: f32, + width: f32, +) { + state_edit_field( + &mut state.zmq_port, + ui, + txt_description_width, + text_height, + width, + " ZMQ PORT ", + 5, + NODE_ZMQ_PORT, + vec![®EXES.port], + ); +} + +fn path_db_field( + state: &mut Node, + ui: &mut Ui, + txt_description_width: f32, + text_height: f32, + file_window: &Arc>, +) { + ui.horizontal(|ui| { + let symbol; + let color; + let hover; + if state.path_db.is_empty() { + symbol = "➖"; + color = LIGHT_GRAY; + hover = NODE_DB_PATH_EMPTY; + } else if !Gupax::path_is_dir(&state.path_db) { + symbol = "❌"; + color = RED; + hover = NODE_DB_DIR; + } else { + symbol = "✔"; + color = GREEN; + hover = NODE_PATH_OK; + } + let text = ["Node Database Directory ", symbol].concat(); + ui.add_sized( + [txt_description_width, text_height], + Label::new(RichText::new(text).color(color)), + ); + ui.spacing_mut().text_edit_width = + ui.available_width() - (ui.spacing().item_spacing.x * 8.0) - SPACE * 2.0; + let window_busy = lock!(file_window).thread; + ui.add_enabled_ui(!window_busy, |ui| { + if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { + Gupax::spawn_file_window_thread(file_window, FileType::NodeDB); + } + ui.text_edit_singleline(&mut state.path_db) + .on_hover_text(hover); + }); + }); + + let mut guard = lock!(file_window); + if guard.picked_nodedb { + state.path_db.clone_from(&guard.nodedb_path); + guard.picked_nodedb = false; + } +} +#[allow(clippy::too_many_arguments)] +fn state_edit_field( + state_field: &mut String, + ui: &mut Ui, + txt_description_width: f32, + text_height: f32, + width: f32, + description: &str, + max_ch: u8, + help_msg: &str, + validations: Vec<&Regex>, +) { + ui.horizontal(|ui| { + let color; + let symbol; + let mut input_validated = true; + let len; + let inside_space; + for v in validations { + if !v.is_match(state_field) { + input_validated = false; + } + } + if state_field.is_empty() { + symbol = "➖"; + color = Color32::LIGHT_GRAY; + } else if input_validated { + symbol = "✔"; + color = Color32::from_rgb(100, 230, 100); + } else { + symbol = "❌"; + color = Color32::from_rgb(230, 50, 50); + } + match max_ch { + x if x >= 100 => { + len = format!("{:03}", state_field.len()); + inside_space = ""; + } + 10..99 => { + len = format!("{:02}", state_field.len()); + inside_space = " "; + } + _ => { + len = format!("{}", state_field.len()); + inside_space = " "; + } + } + let text = format!( + "{}[{}{}/{}{}]{}", + description, inside_space, len, max_ch, inside_space, symbol + ); + ui.add_sized( + [txt_description_width, text_height], + Label::new(RichText::new(text).color(color)), + ); + // allocate the size to leave half of the total width free. + ui.spacing_mut().text_edit_width = (width / 2.0) + - (width - ui.available_width() - ui.spacing().scroll.bar_width) + - ui.spacing().item_spacing.x * 2.5; + ui.text_edit_singleline(state_field).on_hover_text(help_msg); + state_field.truncate(max_ch.into()); + }); +} diff --git a/src/app/panels/middle/status/mod.rs b/src/app/panels/middle/status/mod.rs index a791cda..6501084 100644 --- a/src/app/panels/middle/status/mod.rs +++ b/src/app/panels/middle/status/mod.rs @@ -21,6 +21,7 @@ use crate::{ app::Benchmark, disk::{gupax_p2pool_api::GupaxP2poolApi, state::Status, status::*}, helper::{ + node::PubNodeApi, p2pool::{ImgP2pool, PubP2poolApi}, xrig::{ xmrig::{ImgXmrig, PubXmrigApi}, @@ -42,12 +43,14 @@ impl Status { pub fn show( &mut self, sys: &Arc>, + node_api: &Arc>, p2pool_api: &Arc>, xmrig_api: &Arc>, xmrig_proxy_api: &Arc>, xvb_api: &Arc>, p2pool_img: &Arc>, xmrig_img: &Arc>, + node_alive: bool, p2pool_alive: bool, xmrig_alive: bool, xmrig_proxy_alive: bool, @@ -65,6 +68,8 @@ impl Status { sys, size, ui, + node_alive, + node_api, p2pool_alive, p2pool_api, p2pool_img, diff --git a/src/app/panels/middle/status/processes.rs b/src/app/panels/middle/status/processes.rs index aeaf474..4138988 100644 --- a/src/app/panels/middle/status/processes.rs +++ b/src/app/panels/middle/status/processes.rs @@ -3,6 +3,7 @@ use readable::up::UptimeFull; use std::sync::{Arc, Mutex}; use crate::disk::state::Status; +use crate::helper::node::PubNodeApi; use crate::helper::p2pool::{ImgP2pool, PubP2poolApi}; use crate::helper::xrig::xmrig::{ImgXmrig, PubXmrigApi}; use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi; @@ -21,6 +22,8 @@ impl Status { sys: &Arc>, size: Vec2, ui: &mut egui::Ui, + node_alive: bool, + node_api: &Arc>, p2pool_alive: bool, p2pool_api: &Arc>, p2pool_img: &Arc>, @@ -50,6 +53,8 @@ impl Status { // [Gupax] gupax(ui, min_size, size, sys); + // [Node] + node(ui, min_size, size, node_alive, node_api); // [P2Pool] p2pool(ui, min_size, size, p2pool_alive, p2pool_api, p2pool_img); // [XMRig] @@ -554,3 +559,94 @@ fn xvb(ui: &mut Ui, min_size: Vec2, size: Vec2, xvb_alive: bool, xvb_api: &Arc>, +) { + ui.group(|ui| { + ui.vertical(|ui| { + ui.set_min_height(min_size.y * 34.0); + debug!("Status Tab | Rendering [Node]"); + ui.add_enabled_ui(node_alive, |ui| { + ui.set_min_size(min_size); + ui.add_sized( + size, + Label::new( + RichText::new("[Node]") + .color(LIGHT_GRAY) + .text_style(TextStyle::Name("MonospaceLarge".into())), + ), + ) + .on_hover_text("Node is online") + .on_disabled_hover_text("Node is offline"); + let api = lock!(node_api); + ui.add_sized( + size, + Label::new(RichText::new("Uptime").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_UPTIME); + ui.add_sized(size, Label::new(api.uptime.to_string())); + + ui.add_sized( + size, + Label::new(RichText::new("Block Height").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_BLOCK_HEIGHT); + ui.add_sized(size, Label::new(api.blockheight.to_string())); + ui.add_sized( + size, + Label::new(RichText::new("Network Difficulty").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_DIFFICULTY); + ui.add_sized(size, Label::new(api.difficulty.to_string())); + ui.add_sized( + size, + Label::new(RichText::new("Database size").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_DB_SIZE); + ui.add_sized(size, Label::new(api.database_size.to_owned())); + ui.add_sized( + size, + Label::new(RichText::new("Free space").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_FREESPACE); + ui.add_sized(size, Label::new(api.free_space.to_owned())); + ui.add_sized( + size, + Label::new(RichText::new("Network Type").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_NETTYPE); + ui.add_sized(size, Label::new(api.nettype.to_string())); + ui.add_sized( + size, + Label::new(RichText::new("Outgoing peers").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_OUT); + ui.add_sized(size, Label::new(api.outgoing_connections.to_string())); + ui.add_sized( + size, + Label::new(RichText::new("Incoming peers").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_IN); + ui.add_sized(size, Label::new(api.incoming_connections.to_string())); + ui.add_sized( + size, + Label::new(RichText::new("Synchronized").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_SYNC); + ui.add_sized(size, Label::new(api.synchronized.to_string())); + ui.add_sized( + size, + Label::new(RichText::new("Status").underline().color(BONE)), + ) + .on_hover_text(STATUS_NODE_STATUS); + ui.add_sized(size, Label::new(api.status.to_string())); + drop(api); + }); + }) + }); +} diff --git a/src/app/panels/top.rs b/src/app/panels/top.rs index 0c5a50e..c39960a 100644 --- a/src/app/panels/top.rs +++ b/src/app/panels/top.rs @@ -1,5 +1,5 @@ use egui::TextStyle::Name; -use egui::{SelectableLabel, TopBottomPanel}; +use egui::{ScrollArea, SelectableLabel, TopBottomPanel}; use log::debug; use crate::{app::Tab, utils::constants::SPACE}; @@ -8,82 +8,94 @@ 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 * 16.0)) / 7.0; + let width = (self.size.x - (SPACE * 18.0)) / 8.0; let height = self.size.y / 15.0; ui.add_space(4.0); ui.horizontal(|ui| { - ui.style_mut().override_text_style = Some(Name("Tab".into())); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::About, "About"), - ) - .clicked() - { - self.tab = Tab::About; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::Status, "Status"), - ) - .clicked() - { - self.tab = Tab::Status; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::Gupax, "Gupaxx"), - ) - .clicked() - { - self.tab = Tab::Gupax; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"), - ) - .clicked() - { - self.tab = Tab::P2pool; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig"), - ) - .clicked() - { - 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], - SelectableLabel::new(self.tab == Tab::Xvb, "XvB"), - ) - .clicked() - { - self.tab = Tab::Xvb; - } + ScrollArea::horizontal().show(ui, |ui| { + ui.style_mut().override_text_style = Some(Name("Tab".into())); + if ui + .add_sized( + [width, height], + SelectableLabel::new(self.tab == Tab::About, "About"), + ) + .clicked() + { + self.tab = Tab::About; + } + ui.separator(); + if ui + .add_sized( + [width, height], + SelectableLabel::new(self.tab == Tab::Status, "Status"), + ) + .clicked() + { + self.tab = Tab::Status; + } + ui.separator(); + if ui + .add_sized( + [width, height], + SelectableLabel::new(self.tab == Tab::Gupax, "Gupaxx"), + ) + .clicked() + { + self.tab = Tab::Gupax; + } + ui.separator(); + if ui + .add_sized( + [width, height], + SelectableLabel::new(self.tab == Tab::Node, "Node"), + ) + .clicked() + { + self.tab = Tab::Node; + } + ui.separator(); + if ui + .add_sized( + [width, height], + SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"), + ) + .clicked() + { + self.tab = Tab::P2pool; + } + ui.separator(); + if ui + .add_sized( + [width, height], + SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig"), + ) + .clicked() + { + 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], + SelectableLabel::new(self.tab == Tab::Xvb, "XvB"), + ) + .clicked() + { + self.tab = Tab::Xvb; + } + }); + ui.add_space(4.0); }); - ui.add_space(4.0); }); } } diff --git a/src/components/gupax.rs b/src/components/gupax.rs index 307907e..cc95ef3 100644 --- a/src/components/gupax.rs +++ b/src/components/gupax.rs @@ -35,7 +35,11 @@ pub struct FileWindow { 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 picked_node: bool, // Did the user pick a path for node? + pub picked_nodedb: bool, // Did the user pick a path for node? pub p2pool_path: String, // The picked p2pool path + pub node_path: String, // The picked node path + pub nodedb_path: String, // The picked node path pub xmrig_path: String, // The picked xmrig path pub xmrig_proxy_path: String, // The picked xmrig-proxy path } @@ -47,7 +51,11 @@ impl FileWindow { picked_p2pool: false, picked_xmrig: false, picked_xp: false, + picked_node: false, + picked_nodedb: false, p2pool_path: String::new(), + node_path: String::new(), + nodedb_path: String::new(), xmrig_path: String::new(), xmrig_proxy_path: String::new(), }) @@ -59,6 +67,8 @@ pub enum FileType { P2pool, Xmrig, XmrigProxy, + Node, + NodeDB, } //---------------------------------------------------------------------------------------------------- Ratio Lock @@ -80,6 +90,14 @@ impl Gupax { _ => false, } } + // Checks if a path is a valid path to a directory. + pub fn path_is_dir(path: &str) -> bool { + let path = path.to_string(); + match crate::disk::into_absolute_path(path) { + Ok(path) => path.is_dir(), + _ => false, + } + } #[cold] #[inline(never)] @@ -89,33 +107,48 @@ impl Gupax { P2pool => "P2Pool", Xmrig => "XMRig", XmrigProxy => "XMRigProxy", + Node => "Node", + NodeDB => "Node DB", }; let file_window = file_window.clone(); lock!(file_window).thread = true; thread::spawn(move || { - match rfd::FileDialog::new() - .set_title(format!("Select {} Binary for Gupaxx", name)) - .pick_file() - { - Some(path) => { - info!("Gupaxx | Path selected for {} ... {}", name, path.display()); - match file_type { - P2pool => { - lock!(file_window).p2pool_path = path.display().to_string(); - lock!(file_window).picked_p2pool = true; - } - Xmrig => { - lock!(file_window).xmrig_path = path.display().to_string(); - lock!(file_window).picked_xmrig = true; - } - 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), + let path = match file_type { + NodeDB => rfd::FileDialog::new() + .set_title("Select a directory for the DB of your Node") + .pick_folder(), + _ => rfd::FileDialog::new() + .set_title(format!("Select {} Binary for Gupaxx", name)) + .pick_file(), }; + if let Some(path) = path { + info!("Gupaxx | Path selected for {} ... {}", name, path.display()); + match file_type { + P2pool => { + lock!(file_window).p2pool_path = path.display().to_string(); + lock!(file_window).picked_p2pool = true; + } + Xmrig => { + lock!(file_window).xmrig_path = path.display().to_string(); + lock!(file_window).picked_xmrig = true; + } + XmrigProxy => { + lock!(file_window).xmrig_proxy_path = path.display().to_string(); + lock!(file_window).picked_xp = true; + } + Node => { + lock!(file_window).node_path = path.display().to_string(); + lock!(file_window).picked_node = true; + } + NodeDB => { + lock!(file_window).nodedb_path = path.display().to_string(); + lock!(file_window).picked_nodedb = true; + } + }; + } else { + info!("Gupaxx | No path selected for {}", name); + } + lock!(file_window).thread = false; }); } diff --git a/src/components/update.rs b/src/components/update.rs index fa4c0b4..ec7496b 100644 --- a/src/components/update.rs +++ b/src/components/update.rs @@ -61,6 +61,7 @@ cfg_if::cfg_if! { if #[cfg(target_family = "unix")] { pub(super) const GUPAX_BINARY: &str = "gupaxx"; pub(super) const P2POOL_BINARY: &str = "p2pool"; + pub(super) const NODE_BINARY: &str = "monerod"; pub(super) const XMRIG_BINARY: &str = "xmrig"; pub(super) const XMRIG_PROXY_BINARY: &str = "xmrig-proxy"; } @@ -71,6 +72,7 @@ cfg_if::cfg_if! { pub(super) const ARCHIVE_EXT: &str = "zip"; pub(super) const GUPAX_BINARY: &str = "Gupaxx.exe"; pub(super) const P2POOL_BINARY: &str = "p2pool.exe"; + pub(super) const NODE_BINARY: &str = "monerod.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")] { @@ -153,6 +155,22 @@ pub fn check_p2pool_path(path: &str) -> bool { path == P2POOL_BINARY } +pub fn check_node_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 Node file name"); + return false; + } + }; + + path == NODE_BINARY +} + pub fn check_xmrig_path(path: &str) -> bool { let path = match crate::disk::into_absolute_path(path.to_string()) { Ok(p) => p, @@ -199,7 +217,8 @@ 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 path_xp: String, // Full path to current xmrig-proxy + pub path_node: String, // Full path to current node pub updating: Arc>, // Is an update in progress? pub prog: Arc>, // Holds the 0-100% progress bar number pub msg: Arc>, // Message to display on [Gupax] tab while updating @@ -212,12 +231,14 @@ impl Update { path_p2pool: PathBuf, path_xmrig: PathBuf, path_xp: PathBuf, + path_node: 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(), + path_node: path_node.display().to_string(), updating: arc_mut!(false), prog: arc_mut!(0.0), msg: arc_mut!(MSG_NONE.to_string()), @@ -299,6 +320,21 @@ impl Update { return; } }; + // Check node path for safety + let node_path = match into_absolute_path(gupax.node_path.clone()) { + Ok(p) => p, + Err(e) => { + error_state.set( + format!( + "Provided Node path could not be turned into an absolute path: {}", + e + ), + ErrorFerris::Error, + ErrorButtons::Okay, + ); + return; + } + }; // Check XMRig-Proxy path for safety let xmrig_proxy_path = match into_absolute_path(gupax.xmrig_proxy_path.clone()) { Ok(p) => p, @@ -317,6 +353,7 @@ impl Update { 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(); + lock!(update).path_node = node_path.display().to_string(); } // Clone before thread spawn @@ -536,6 +573,7 @@ impl Update { P2POOL_BINARY => lock!(update).path_p2pool.clone(), XMRIG_BINARY => lock!(update).path_xmrig.clone(), XMRIG_PROXY_BINARY => lock!(update).path_xp.clone(), + NODE_BINARY => lock!(update).path_node.clone(), _ => continue, }; found = true; @@ -552,6 +590,7 @@ impl Update { 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", + NODE_BINARY => tmp_dir.clone() + "monerod_old.exe", _ => continue, }; info!( @@ -568,7 +607,10 @@ impl Update { ); // 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) + && (name == P2POOL_BINARY + || name == XMRIG_BINARY + || name == XMRIG_PROXY_BINARY + || name == NODE_BINARY) { std::fs::create_dir_all( path.parent() diff --git a/src/disk/consts.rs b/src/disk/consts.rs index bdb8730..75d4152 100644 --- a/src/disk/consts.rs +++ b/src/disk/consts.rs @@ -41,11 +41,15 @@ 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_NODE_PATH: &str = r"node\monerod.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"; +#[cfg(target_os = "macos")] +pub const DEFAULT_XMRIG_NODE_PATH: &str = "node/monerod"; // Default to [/usr/bin/] for Linux distro builds. #[cfg(target_os = "linux")] @@ -58,6 +62,9 @@ pub const DEFAULT_XMRIG_PATH: &str = "xmrig/xmrig"; #[cfg(not(feature = "distro"))] pub const DEFAULT_XMRIG_PROXY_PATH: &str = "xmrig-proxy/xmrig-proxy"; #[cfg(target_os = "linux")] +#[cfg(not(feature = "distro"))] +pub const DEFAULT_NODE_PATH: &str = "node/monerod"; +#[cfg(target_os = "linux")] #[cfg(feature = "distro")] pub const DEFAULT_P2POOL_PATH: &str = "/usr/bin/p2pool"; #[cfg(target_os = "linux")] @@ -66,3 +73,6 @@ 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"; +#[cfg(target_os = "linux")] +#[cfg(feature = "distro")] +pub const DEFAULT_NODE_PATH: &str = "/usr/bin/monerod"; diff --git a/src/disk/state.rs b/src/disk/state.rs index 3d2b2b1..c2771fa 100644 --- a/src/disk/state.rs +++ b/src/disk/state.rs @@ -21,6 +21,7 @@ impl State { xmrig: Xmrig::with_threads(max_threads, current_threads), xvb: Xvb::default(), xmrig_proxy: XmrigProxy::default(), + node: Node::default(), version: arc_mut!(Version::default()), } } @@ -29,6 +30,7 @@ impl State { 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())?; + self.gupax.absolute_node_path = into_absolute_path(self.gupax.node_path.clone())?; Ok(()) } @@ -163,6 +165,7 @@ pub struct State { pub xmrig: Xmrig, pub xmrig_proxy: XmrigProxy, pub xvb: Xvb, + pub node: Node, pub version: Arc>, } @@ -181,16 +184,18 @@ pub struct Gupax { pub simple: bool, pub auto_update: bool, pub auto_p2pool: bool, + pub auto_node: 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 node_path: String, pub xmrig_path: String, pub xmrig_proxy_path: String, pub absolute_p2pool_path: PathBuf, + pub absolute_node_path: PathBuf, pub absolute_xmrig_path: PathBuf, pub absolute_xp_path: PathBuf, pub selected_width: u16, @@ -225,6 +230,43 @@ pub struct P2pool { pub selected_zmq: String, } +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] +pub struct Node { + pub simple: bool, + pub api_ip: String, + pub api_port: String, + pub out_peers: u16, + pub in_peers: u16, + pub log_level: u8, + pub arguments: String, + pub zmq_ip: String, + pub zmq_port: String, + pub pruned: bool, + pub dns_blocklist: bool, + pub disable_dns_checkpoint: bool, + pub path_db: String, +} + +impl Default for Node { + fn default() -> Self { + Self { + simple: true, + api_ip: String::from("127.0.0.1"), + api_port: 18081.to_string(), + out_peers: 32, + in_peers: 64, + log_level: 0, + arguments: String::new(), + zmq_ip: String::from("127.0.0.1"), + zmq_port: 18083.to_string(), + pruned: true, + dns_blocklist: true, + disable_dns_checkpoint: true, + path_db: String::new(), + } + } +} + #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] pub struct Xmrig { pub simple: bool, @@ -414,6 +456,7 @@ impl Default for Gupax { simple: true, auto_update: false, auto_p2pool: false, + auto_node: false, auto_xmrig: false, auto_xp: false, auto_xvb: false, @@ -421,10 +464,12 @@ impl Default for Gupax { save_before_quit: true, p2pool_path: DEFAULT_P2POOL_PATH.to_string(), xmrig_path: DEFAULT_XMRIG_PATH.to_string(), + node_path: DEFAULT_NODE_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(), + absolute_node_path: into_absolute_path(DEFAULT_NODE_PATH.to_string()).unwrap(), selected_width: APP_DEFAULT_WIDTH as u16, selected_height: APP_DEFAULT_HEIGHT as u16, selected_scale: APP_DEFAULT_SCALE, diff --git a/src/disk/tests.rs b/src/disk/tests.rs index 004754f..a760f7d 100644 --- a/src/disk/tests.rs +++ b/src/disk/tests.rs @@ -30,6 +30,7 @@ mod test { simple = true auto_update = true auto_p2pool = false + auto_node = false auto_xmrig = false auto_xvb = false auto_xp = false @@ -37,8 +38,10 @@ mod test { save_before_quit = true p2pool_path = "p2pool/p2pool" xmrig_path = "xmrig/xmrig" + node_path = "node/monerod" xmrig_proxy_path = "xmrig-proxy/xmrig-proxy" absolute_p2pool_path = "/home/hinto/p2pool/p2pool" + absolute_node_path = "/home/hinto/node/monerod" absolute_xmrig_path = "/home/hinto/xmrig/xmrig" absolute_xp_path = "/home/hinto/xmrig/xmrig-proxy/xmrig-proxy" selected_width = 1280 @@ -137,10 +140,26 @@ mod test { node = "Europe" p2pool_buffer = 5 + [node] + simple = false + api_ip = "127.0.0.1" + api_port = "18081" + out_peers = 32 + in_peers = 64 + log_level = 0 + arguments = "" + zmq_ip = "127.0.0.1" + zmq_port = "18083" + pruned = true + dns_blocklist = true + disable_dns_checkpoint = true + path_db = "" + [version] gupax = "v1.3.0" p2pool = "v2.5" xmrig = "v6.18.0" + node = "v18.3.4" "#; let state = State::from_str(state).unwrap(); State::to_string(&state).unwrap(); diff --git a/src/helper/mod.rs b/src/helper/mod.rs index eeb21fe..fa5013e 100644 --- a/src/helper/mod.rs +++ b/src/helper/mod.rs @@ -41,6 +41,7 @@ use crate::helper::{ }; use crate::{constants::*, disk::gupax_p2pool_api::GupaxP2poolApi, human::*, macros::*}; use log::*; +use node::PubNodeApi; use portable_pty::Child; use readable::up::Uptime; use std::fmt::Write; @@ -53,6 +54,7 @@ use std::{ }; use self::xvb::{nodes::XvbNode, PubXvbApi}; +pub mod node; pub mod p2pool; pub mod tests; pub mod xrig; @@ -76,6 +78,7 @@ pub struct Helper { pub uptime: HumanTime, // Gupax uptime formatting for humans pub pub_sys: Arc>, // The public API for [sysinfo] that the [Status] tab reads from pub p2pool: Arc>, // P2Pool process state + pub node: Arc>, // P2Pool process state pub xmrig: Arc>, // XMRig process state pub xmrig_proxy: Arc>, // XMRig process state pub xvb: Arc>, // XvB process state @@ -83,11 +86,13 @@ pub struct Helper { pub gui_api_xmrig: Arc>, // XMRig API state (for GUI thread) pub gui_api_xp: Arc>, // XMRig-Proxy API state (for GUI thread) pub gui_api_xvb: Arc>, // XMRig API state (for GUI thread) + pub gui_api_node: Arc>, // Node API state (for GUI thread) pub img_p2pool: Arc>, // A static "image" of the data P2Pool started with pub img_xmrig: Arc>, // A static "image" of the data XMRig started with pub_api_p2pool: Arc>, // P2Pool API state (for Helper/P2Pool thread) pub_api_xmrig: Arc>, // XMRig API state (for Helper/XMRig thread) pub_api_xp: Arc>, // XMRig-Proxy API state (for Helper/XMRig-Proxy thread) + pub_api_node: Arc>, // Node API state (for Helper/Node thread) pub_api_xvb: Arc>, // XvB API state (for Helper/XvB thread) pub gupax_p2pool_api: Arc>, // } @@ -251,6 +256,7 @@ pub enum ProcessName { Xmrig, XmrigProxy, Xvb, + Node, } impl std::fmt::Display for ProcessState { @@ -270,6 +276,7 @@ impl std::fmt::Display for ProcessName { ProcessName::Xmrig => write!(f, "XMRig"), ProcessName::XmrigProxy => write!(f, "XMRig-Proxy"), ProcessName::Xvb => write!(f, "XvB"), + ProcessName::Node => write!(f, "Node"), } } } @@ -285,10 +292,12 @@ impl Helper { xmrig: Arc>, xmrig_proxy: Arc>, xvb: Arc>, + node: Arc>, gui_api_p2pool: Arc>, gui_api_xmrig: Arc>, gui_api_xvb: Arc>, gui_api_xp: Arc>, + gui_api_node: Arc>, img_p2pool: Arc>, img_xmrig: Arc>, gupax_p2pool_api: Arc>, @@ -301,15 +310,18 @@ impl Helper { pub_api_xmrig: arc_mut!(PubXmrigApi::new()), pub_api_xp: arc_mut!(PubXmrigProxyApi::new()), pub_api_xvb: arc_mut!(PubXvbApi::new()), + pub_api_node: arc_mut!(PubNodeApi::new()), // These are created when initializing [App], since it needs a handle to it as well p2pool, xmrig, xmrig_proxy, xvb, + node, gui_api_p2pool, gui_api_xmrig, gui_api_xvb, gui_api_xp, + gui_api_node, img_p2pool, img_xmrig, gupax_p2pool_api, @@ -419,15 +431,18 @@ impl Helper { let helper = Arc::clone(helper); let lock = lock!(helper); + let node = Arc::clone(&lock.node); 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_node = Arc::clone(&lock.gui_api_node); 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_node = Arc::clone(&lock.pub_api_node); 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); @@ -451,35 +466,48 @@ impl Helper { // 2. Lock... EVERYTHING! let mut lock = lock!(helper); - debug!("Helper | Locking (1/12) ... [helper]"); + debug!("Helper | Locking (1/15) ... [helper]"); + let node = lock!(node); + debug!("Helper | Locking (2/15) ... [helper]"); let p2pool = lock!(p2pool); - debug!("Helper | Locking (2/12) ... [p2pool]"); + debug!("Helper | Locking (3/15) ... [p2pool]"); let xmrig = lock!(xmrig); - debug!("Helper | Locking (3/12) ... [xmrig]"); + debug!("Helper | Locking (4/15) ... [xmrig]"); let xmrig_proxy = lock!(xmrig_proxy); - debug!("Helper | Locking (3/12) ... [xmrig_proxy]"); + debug!("Helper | Locking (5/15) ... [xmrig_proxy]"); let xvb = lock!(xvb); - debug!("Helper | Locking (4/12) ... [xvb]"); + debug!("Helper | Locking (6/15) ... [xvb]"); let mut lock_pub_sys = lock!(pub_sys); - debug!("Helper | Locking (5/12) ... [pub_sys]"); + debug!("Helper | Locking (8/15) ... [gui_api_node]"); + let mut gui_api_node = lock!(gui_api_node); + debug!("Helper | Locking (7/15) ... [pub_sys]"); let mut gui_api_p2pool = lock!(gui_api_p2pool); - debug!("Helper | Locking (6/12) ... [gui_api_p2pool]"); + debug!("Helper | Locking (8/15) ... [gui_api_p2pool]"); let mut gui_api_xmrig = lock!(gui_api_xmrig); - debug!("Helper | Locking (7/12) ... [gui_api_xmrig]"); + debug!("Helper | Locking (9/15) ... [gui_api_xmrig]"); let mut gui_api_xp = lock!(gui_api_xp); - debug!("Helper | Locking (7/12) ... [gui_api_xp]"); + debug!("Helper | Locking (10/15) ... [gui_api_xp]"); let mut gui_api_xvb = lock!(gui_api_xvb); - debug!("Helper | Locking (8/12) ... [gui_api_xvb]"); + debug!("Helper | Locking (11/15) ... [gui_api_xvb]"); + let mut pub_api_node = lock!(pub_api_node); + debug!("Helper | Locking (14/15) ... [pub_api_node]"); let mut pub_api_p2pool = lock!(pub_api_p2pool); - debug!("Helper | Locking (9/12) ... [pub_api_p2pool]"); + debug!("Helper | Locking (14/15) ... [pub_api_p2pool]"); let mut pub_api_xmrig = lock!(pub_api_xmrig); - debug!("Helper | Locking (10/12) ... [pub_api_xmrig]"); + debug!("Helper | Locking (13/15) ... [pub_api_xmrig]"); let mut pub_api_xp = lock!(pub_api_xp); - debug!("Helper | Locking (11/12) ... [pub_api_xp]"); + debug!("Helper | Locking (14/15) ... [pub_api_xp]"); let mut pub_api_xvb = lock!(pub_api_xvb); - debug!("Helper | Locking (12/12) ... [pub_api_xvb]"); + debug!("Helper | Locking (15/15) ... [pub_api_xvb]"); // Calculate Gupax's uptime always. lock.uptime = HumanTime::into_human(lock.instant.elapsed()); + // If [Node] is alive... + if node.is_alive() { + debug!("Helper | Node is alive! Running [combine_gui_pub_api()]"); + PubNodeApi::combine_gui_pub_api(&mut gui_api_node, &mut pub_api_node); + } else { + debug!("Helper | Node is dead! Skipping..."); + } // If [P2Pool] is alive... if p2pool.is_alive() { debug!("Helper | P2Pool is alive! Running [combine_gui_pub_api()]"); @@ -539,6 +567,8 @@ impl Helper { debug!("Helper | Unlocking (3/12) ... [xmrig]"); drop(p2pool); debug!("Helper | Unlocking (4/12) ... [p2pool]"); + drop(node); + debug!("Helper | Unlocking (4/12) ... [node]"); drop(pub_api_xvb); debug!("Helper | Unlocking (5/12) ... [pub_api_xvb]"); drop(pub_api_xp); @@ -547,6 +577,8 @@ impl Helper { debug!("Helper | Unlocking (6/12) ... [pub_api_xmrig]"); drop(pub_api_p2pool); debug!("Helper | Unlocking (7/12) ... [pub_api_p2pool]"); + drop(pub_api_node); + debug!("Helper | Unlocking (7/12) ... [node]"); drop(gui_api_xvb); debug!("Helper | Unlocking (8/12) ... [gui_api_xvb]"); drop(gui_api_xp); @@ -555,6 +587,8 @@ impl Helper { debug!("Helper | Unlocking (10/12) ... [gui_api_xmrig]"); drop(gui_api_p2pool); debug!("Helper | Unlocking (11/12) ... [gui_api_p2pool]"); + drop(gui_api_node); + debug!("Helper | Unlocking (11/12) ... [node]"); drop(lock); debug!("Helper | Unlocking (12/12) ... [helper]"); diff --git a/src/helper/node.rs b/src/helper/node.rs new file mode 100644 index 0000000..a71301a --- /dev/null +++ b/src/helper/node.rs @@ -0,0 +1,396 @@ +use std::{ + path::Path, + sync::{Arc, Mutex}, + thread, + time::{Duration, Instant}, +}; + +use enclose::enc; +use log::{debug, error, info, warn}; +use readable::byte::Byte; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use tokio::spawn; + +use crate::{ + disk::state::Node, + helper::{ + check_died, check_user_input, signal_end, sleep_end_loop, ProcessName, ProcessSignal, + ProcessState, + }, + macros::{arc_mut, lock2, sleep}, +}; +use std::fmt::Write; + +use super::{lock, Helper, HumanNumber, HumanTime, Process}; + +impl Helper { + #[cold] + #[inline(never)] + fn read_pty_node( + output_parse: Arc>, + output_pub: Arc>, + reader: Box, + ) { + 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!("Node PTY Parse | Output error: {}", e); + } + if let Err(e) = writeln!(lock!(output_pub), "{}", line) { + error!("Node PTY Pub | Output error: {}", e); + } + if i > 20 { + break; + } else { + i += 1; + } + } + while let Some(Ok(line)) = stdout.next() { + if let Err(e) = writeln!(lock!(output_parse), "{}", line) { + error!("P2Pool PTY Parse | Output error: {}", e); + } + if let Err(e) = writeln!(lock!(output_pub), "{}", line) { + error!("P2Pool PTY Pub | Output error: {}", e); + } + } + } + pub fn build_node_args(state: &crate::disk::state::Node) -> Vec { + let mut args = Vec::with_capacity(500); + if !state.arguments.is_empty() { + args.push(state.arguments.clone()); + return args; + } + + // [Simple] + if state.simple { + // Build the node argument to be compatible with p2pool, prune by default + args.push("--zmq-pub".to_string()); + args.push("tcp://127.0.0.1:18083".to_string()); // Local P2Pool (the default) + args.push("--out-peers".to_string()); + args.push("32".to_string()); + args.push("--in-peers".to_string()); + args.push("64".to_string()); // Rig name + args.push("--add-priority-node".to_string()); + args.push("p2pmd.xmrvsbeast.com:18080".to_string()); + args.push("--add-priority-node".to_string()); + args.push("nodes.hashvault.pro:18080".to_string()); + args.push("--disable-dns-checkpoints".to_string()); + args.push("--enable-dns-blocklist".to_string()); + args.push("--sync-pruned-blocks".to_string()); + args.push("--prune-blockchain".to_string()); + + // [Advanced] + } else { + let dir = if state.path_db.is_empty() { + String::from(".bitmonero") + } else { + state.path_db.to_string() + }; + args.push("--data-dir".to_string()); + args.push(dir); + args.push("--zmq-pub".to_string()); + args.push(format!("tcp://{}:{}", state.zmq_ip, state.zmq_port)); + args.push("--rpc-bind-ip".to_string()); + args.push(state.api_ip.clone()); + args.push("--rpc-bind-port".to_string()); + args.push(state.api_port.to_string()); + args.push("--out-peers".to_string()); + args.push(state.out_peers.to_string()); + args.push("--in-peers".to_string()); + args.push(state.in_peers.to_string()); + args.push("--sync-pruned-blocks".to_string()); + if state.dns_blocklist { + args.push("--enable-dns-blocklist".to_string()); + } + if state.disable_dns_checkpoint { + args.push("--disable-dns-checkpoints".to_string()); + } + if state.pruned { + args.push("--prune-blockchain".to_string()); + } + } + args + } + #[cold] + #[inline(never)] + // Just sets some signals for the watchdog thread to pick up on. + pub fn stop_node(helper: &Arc>) { + info!("Node | Attempting to stop..."); + lock2!(helper, node).signal = ProcessSignal::Stop; + lock2!(helper, node).state = ProcessState::Middle; + let gui_api = Arc::clone(&lock!(helper).gui_api_node); + let pub_api = Arc::clone(&lock!(helper).pub_api_node); + *lock!(pub_api) = PubNodeApi::new(); + *lock!(gui_api) = PubNodeApi::new(); + } + #[cold] + #[inline(never)] + // The "restart frontend" to a "frontend" function. + // Basically calls to kill the current p2pool, waits a little, then starts the below function in a a new thread, then exit. + pub fn restart_node(helper: &Arc>, state: &Node, path: &Path) { + info!("Node | Attempting to restart..."); + lock2!(helper, node).signal = ProcessSignal::Restart; + lock2!(helper, node).state = ProcessState::Middle; + + let helper = Arc::clone(helper); + let state = state.clone(); + let path = path.to_path_buf(); + // This thread lives to wait, start p2pool then die. + thread::spawn(move || { + while lock2!(helper, node).state != ProcessState::Waiting { + warn!("Node | Want to restart but process is still alive, waiting..."); + sleep!(1000); + } + // Ok, process is not alive, start the new one! + info!("Node | Old process seems dead, starting new one!"); + Self::start_node(&helper, &state, &path); + }); + info!("Node | Restart ... OK"); + } + #[cold] + #[inline(never)] + // The "frontend" function that parses the arguments, and spawns either the [Simple] or [Advanced] Node watchdog thread. + pub fn start_node(helper: &Arc>, state: &Node, path: &Path) { + lock2!(helper, node).state = ProcessState::Middle; + + let args = Self::build_node_args(state); + + // Print arguments & user settings to console + crate::disk::print_dash(&format!("Node | Launch arguments: {:#?}", args)); + + // Spawn watchdog thread + let process = Arc::clone(&lock!(helper).node); + let gui_api = Arc::clone(&lock!(helper).gui_api_node); + let pub_api = Arc::clone(&lock!(helper).pub_api_node); + let path = path.to_path_buf(); + let state = state.clone(); + thread::spawn(move || { + Self::spawn_node_watchdog(&process, &gui_api, &pub_api, args, path, state); + }); + } + #[tokio::main] + #[allow(clippy::await_holding_lock)] + #[allow(clippy::too_many_arguments)] + async fn spawn_node_watchdog( + process: &Arc>, + gui_api: &Arc>, + pub_api: &Arc>, + args: Vec, + path: std::path::PathBuf, + state: Node, + ) { + lock!(process).start = Instant::now(); + // spawn pty + debug!("Node | 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!("Node | 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!((output_parse, output_pub) async move { + Self::read_pty_node(output_parse, output_pub, reader); + })); + // 1b. Create command + debug!("Node | 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!("Node | Creating child..."); + let child_pty = arc_mut!(pair.slave.spawn_command(cmd).unwrap()); + drop(pair.slave); + let mut stdin = pair.master.take_writer().unwrap(); + // set state + let client = Client::new(); + lock!(process).state = ProcessState::Syncing; + lock!(process).signal = ProcessSignal::None; + // reset stats + *lock!(pub_api) = PubNodeApi::new(); + *lock!(gui_api) = PubNodeApi::new(); + // loop + let start = lock!(process).start; + info!("Node | Entering watchdog mode... woof!"); + loop { + let now = Instant::now(); + debug!("Node 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!("Node Watchdog | Attempting GUI log reset check"); + { + let mut lock = lock!(gui_api); + Self::check_reset_gui_output(&mut lock.output, ProcessName::Node); + } + // No need to check output since monerod has a sufficient API + // Always update from output + debug!("Node Watchdog | Starting [update_from_output()]"); + PubNodeApi::update_from_output(pub_api, &output_pub, start.elapsed()); + // update data from api + debug!("Node Watchdog | Attempting HTTP API request..."); + match PrivNodeApi::request_api(&client, &state).await { + Ok(priv_api) => { + debug!("Node Watchdog | HTTP API request OK, attempting [update_from_priv()]"); + PubNodeApi::update_from_priv(pub_api, priv_api); + } + Err(err) => { + // if node is just starting, do not throw an error + if start.elapsed() > Duration::from_secs(10) { + warn!( + "Node Watchdog | Could not send HTTP API request to node\n{}", + err + ); + } + } + } + // do not use more than 1 second for the loop + sleep_end_loop(now, ProcessName::Node).await; + } + + // 5. If loop broke, we must be done here. + info!("XMRig-Proxy Watchdog | Watchdog thread exiting... Goodbye!"); + // sleep + } +} +#[derive(Clone)] +pub struct PubNodeApi { + pub output: String, + pub uptime: HumanTime, + pub blockheight: HumanNumber, + pub difficulty: HumanNumber, + pub database_size: String, + pub free_space: String, + pub nettype: String, + pub outgoing_connections: u16, + pub incoming_connections: u16, + pub status: String, + pub synchronized: bool, +} +impl Default for PubNodeApi { + fn default() -> Self { + Self::new() + } +} + +impl PubNodeApi { + pub fn new() -> Self { + Self { + output: String::new(), + uptime: HumanTime::new(), + blockheight: HumanNumber::unknown(), + difficulty: HumanNumber::unknown(), + database_size: HumanNumber::unknown().to_string(), + free_space: HumanNumber::unknown().to_string(), + nettype: String::from("???"), + outgoing_connections: 0, + incoming_connections: 0, + status: String::from("Offline"), + synchronized: false, + } + } + pub fn combine_gui_pub_api(gui_api: &mut Self, pub_api: &mut Self) { + let output = std::mem::take(&mut gui_api.output); + let buf = std::mem::take(&mut pub_api.output); + *gui_api = Self { + output, + ..pub_api.clone() + }; + if !buf.is_empty() { + gui_api.output.push_str(&buf); + } + } + fn update_from_priv(public: &Arc>, private: PrivNodeApi) { + let mut public = lock!(public); + *public = Self { + blockheight: HumanNumber::from_u64(private.result.height), + difficulty: HumanNumber::from_u64(private.result.difficulty), + database_size: Byte::from(private.result.database_size).to_string(), + free_space: Byte::from(private.result.free_space).to_string(), + nettype: private.result.nettype, + outgoing_connections: private.result.outgoing_connections_count, + incoming_connections: private.result.incoming_connections_count, + status: private.result.status, + synchronized: private.result.synchronized, + ..std::mem::take(&mut *public) + } + } + pub fn update_from_output( + public: &Arc>, + output_pub: &Arc>, + elapsed: std::time::Duration, + ) { + // 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 = HumanTime::into_human(elapsed); + } + } +} +#[derive(Deserialize, Serialize)] +struct PrivNodeApi { + result: ResultNodeJson, +} +#[derive(Deserialize, Serialize)] +struct ResultNodeJson { + pub height: u64, + pub difficulty: u64, + pub database_size: u64, + pub free_space: u64, + pub nettype: String, + pub outgoing_connections_count: u16, + pub incoming_connections_count: u16, + pub status: String, + pub synchronized: bool, +} +impl PrivNodeApi { + async fn request_api( + client: &Client, + state: &Node, + ) -> std::result::Result { + let adr = format!("http://{}:{}/json_rpc", state.api_ip, state.api_port); + let private = client + .post(adr) + .body(r#"{"jsonrpc":"2.0","id":"0","method":"get_info"}"#) + .send() + .await? + .json::() + .await?; + Ok(private) + } +} diff --git a/src/inits.rs b/src/inits.rs index 1d97ad8..f97e506 100644 --- a/src/inits.rs +++ b/src/inits.rs @@ -1,4 +1,5 @@ use crate::components::update::Update; +use crate::errors::process_running; use crate::helper::{Helper, ProcessSignal}; use crate::utils::constants::{ APP_MAX_HEIGHT, APP_MAX_WIDTH, APP_MIN_HEIGHT, APP_MIN_WIDTH, BYTES_ICON, @@ -43,7 +44,7 @@ pub fn init_text_styles(ctx: &egui::Context, width: f32, pixels_per_point: f32) ), ( Name("Tab".into()), - FontId::new(scale * 1.2, egui::FontFamily::Monospace), + FontId::new(scale * 1.05, egui::FontFamily::Monospace), ), ( Name("Bottom".into()), @@ -180,6 +181,26 @@ pub fn init_auto(app: &mut App) { info!("Skipping auto-ping..."); } + // [Auto-Node] + if app.state.gupax.auto_node { + if !Gupax::path_is_file(&app.state.gupax.node_path) { + warn!("Gupaxx | Node path is not a file! Skipping auto-node..."); + } else if !crate::components::update::check_node_path(&app.state.gupax.node_path) { + warn!("Gupaxx | Node path is not valid! Skipping auto-node..."); + } else if process_running(crate::helper::ProcessName::Node) { + warn!("Gupaxx | Node instance is already running outside of Gupaxx ! Skipping auto-node..."); + } else { + // enable hugepage on linux + // sudo sysctl vm.nr_hugepages=3072 + Helper::start_node( + &app.helper, + &app.state.node, + &app.state.gupax.absolute_node_path, + ); + } + } else { + info!("Skipping auto-p2pool..."); + } // [Auto-P2Pool] if app.state.gupax.auto_p2pool { if !Regexes::addr_ok(&app.state.p2pool.address) { @@ -188,6 +209,8 @@ pub fn init_auto(app: &mut App) { warn!("Gupaxx | P2Pool path is not a file! Skipping auto-p2pool..."); } else if !crate::components::update::check_p2pool_path(&app.state.gupax.p2pool_path) { warn!("Gupaxx | P2Pool path is not valid! Skipping auto-p2pool..."); + } else if process_running(crate::helper::ProcessName::P2pool) { + warn!("Gupaxx | P2pool instance is already running outside of Gupaxx ! Skipping auto-node..."); } else { let backup_hosts = app.gather_backup_hosts(); Helper::start_p2pool( @@ -207,6 +230,8 @@ pub fn init_auto(app: &mut App) { warn!("Gupaxx | XMRig path is not an executable! Skipping auto-xmrig..."); } else if !crate::components::update::check_xmrig_path(&app.state.gupax.xmrig_path) { warn!("Gupaxx | XMRig path is not valid! Skipping auto-xmrig..."); + } else if process_running(crate::helper::ProcessName::Xmrig) { + warn!("Gupaxx | Xmrig instance is already running outside of Gupaxx ! Skipping auto-node..."); } else if cfg!(windows) { Helper::start_xmrig( &app.helper, @@ -227,6 +252,8 @@ pub fn init_auto(app: &mut App) { 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 if process_running(crate::helper::ProcessName::XmrigProxy) { + warn!("Gupaxx | Xmrig-Proxy instance is already running outside of Gupaxx ! Skipping auto-node..."); } else { Helper::start_xp( &app.helper, diff --git a/src/utils/constants.rs b/src/utils/constants.rs index 4c42fa6..a7a7940 100644 --- a/src/utils/constants.rs +++ b/src/utils/constants.rs @@ -19,6 +19,7 @@ pub const GUPAX_VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); // e.g: pub const P2POOL_VERSION: &str = "v4.1"; pub const XMRIG_VERSION: &str = "v6.21.1"; pub const XMRIG_PROXY_VERSION: &str = "v6.21.1"; +pub const NODE_VERSION: &str = "v18.3.4"; 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 [.] @@ -102,6 +103,12 @@ pub const P2POOL_MIDDLE: &str = "P2Pool is in the middle of (re)starting/stoppin pub const P2POOL_SYNCING: &str = "P2Pool is still syncing. This indicator will turn GREEN when P2Pool is ready"; +pub const NODE_ALIVE: &str = "Node is online and fully synchronized"; +pub const NODE_DEAD: &str = "Node is offline"; +pub const NODE_FAILED: &str = "Node is offline and failed when exiting"; +pub const NODE_MIDDLE: &str = "Node is in the middle of (re)starting/stopping"; +pub const NODE_SYNCING: &str = + "Node is still syncing. This indicator will turn GREEN when Node is ready"; pub const XMRIG_ALIVE: &str = "XMRig is online and mining"; pub const XMRIG_DEAD: &str = "XMRig is offline"; pub const XMRIG_FAILED: &str = "XMRig is offline and failed when exiting"; @@ -236,6 +243,17 @@ pub const STATUS_XVB_WINNER: &str = "Current Raffle Winner"; pub const STATUS_XVB_SHARE: &str = "Share effort"; pub const STATUS_XVB_BLOCK_REWARD: &str = "Block reward"; pub const STATUS_XVB_YEARLY: &str = "Estimated Reward (Yearly)"; +// Status Node +pub const STATUS_NODE_UPTIME: &str = "How long the Node has been online"; +pub const STATUS_NODE_BLOCK_HEIGHT: &str = "The height of where the node is synchronized"; +pub const STATUS_NODE_DIFFICULTY: &str = "current difficulty of the network"; +pub const STATUS_NODE_DB_SIZE: &str = "Size of the database"; +pub const STATUS_NODE_FREESPACE: &str = "Free space left on the partition storing the database"; +pub const STATUS_NODE_NETTYPE: &str = "Type of network (mainnet, stagenet, testnet)"; +pub const STATUS_NODE_OUT: &str = "Current number of active outbound connections"; +pub const STATUS_NODE_IN: &str = "Current number of active incoming connections"; +pub const STATUS_NODE_SYNC: &str = "Does the node is synchronized with the network ?"; +pub const STATUS_NODE_STATUS: &str = "General status of the node"; // Status Submenus pub const STATUS_SUBMENU_PROCESSES: &str = "View the status of process related data for [Gupaxx|P2Pool|XMRig]"; @@ -311,6 +329,7 @@ pub const GUPAX_SHOULD_RESTART: &str = 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_NODE: &str = "Automatically start Node on Gupaxx startup. 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!"; @@ -332,6 +351,7 @@ pub const GUPAX_TAB_GUPAX: &str = "Set the tab Gupaxx starts on to: Gupaxx"; pub const GUPAX_TAB_P2POOL: &str = "Set the tab Gupaxx starts on to: P2Pool"; pub const GUPAX_TAB_XMRIG: &str = "Set the tab Gupaxx starts on to: XMRig"; pub const GUPAX_TAB_XVB: &str = "Set the tab Gupaxx starts on to: XvB"; +pub const GUPAX_TAB_NODE: &str = "Set the default tab Gupaxx starts on to: Node"; pub const GUPAX_SIMPLE: &str = r#"Use simple Gupaxx settings: - Update button @@ -407,6 +427,39 @@ pub const LIST_ADD: &str = "Add the current values to the list"; pub const LIST_SAVE: &str = "Save the current values to the already existing entry"; pub const LIST_DELETE: &str = "Delete the currently selected entry"; pub const LIST_CLEAR: &str = "Clear all current values"; +// Node +pub const NODE_ARGUMENTS: &str = r#"WARNING: Make sure to set [--zmq-pub ] so that P2Pool can connect to it !"#; +pub const NODE_INPUT: &str = "Send a command to Node"; +pub const NODE_PRUNNING: &str = "Reduce the database size to a third. Does not have any security/privacy impact.If you have enough storage, a full node is preferable to make the network even more decentralized."; +pub const NODE_DB_PATH_EMPTY: &str = + "If the PATH of the DB is empty, the default ~/.bitmonero will be used."; +pub const NODE_DB_DIR: &str = "The PATH needs to be a correct path to a directory"; +pub const NODE_SIMPLE: &str = r#"Use simple Node settings: + - Default Node settings"#; +pub const NODE_ADVANCED: &str = r#"Use advanced Node settings: + - Prunning + - Custom path for database + - Terminal input + - Overriding command arguments + - Manual zmq port + - Out/In peer setting + - Log level setting + - Disable DNS checkpoint + - DNS blocking"#; +pub const GUPAX_PATH_NODE: &str = "The location of the DB for the Node: Both absolute and relative paths are accepted; A red [X] will appear if there is no directory found at the given path"; +pub const NODE_PATH_OK: &str = "PATH for DB is valid."; +pub const NODE_PATH_NOT_FILE: &str = "Node binary not found at the given PATH in the Gupaxx tab! To fix: goto the [Gupaxx Advanced] tab, select [Open] and specify where NODE is located."; +pub const NODE_PATH_NOT_VALID: &str = "Node binary at the given PATH in the Gupaxx tab doesn't look like Node! To fix: goto the [Gupaxx Advanced] tab, select [Open] and specify where Node is located."; +pub const NODE_PATH_EMPTY: &str = "Node PATH is empty! To fix: goto the [Gupaxx Advanced] tab, select [Open] and specify where Node is located."; +pub const NODE_URL: &str = "https://github.com/monero-project/monero"; +pub const NODE_DNS_BLOCKLIST: &str = + "Apply realtime blocklist from DNS to ban known malicious nodes. (recommended)"; +pub const NODE_DNS_CHECKPOINT: &str = + "Do not retrieve checkpoints from DNS to prevent periodic lags (recommended)"; +pub const NODE_API_BIND: &str = "bind address of RPC API"; +pub const NODE_API_PORT: &str = "RPC API listen port"; +pub const NODE_ZMQ_BIND: &str = "bind address of ZMQ API"; +pub const NODE_ZMQ_PORT: &str = "ZMQ API listen port"; // XMRig pub const XMRIG_SIMPLE: &str = r#"Use simple XMRig settings: - Mine to local P2Pool (localhost:3333) diff --git a/src/utils/errors.rs b/src/utils/errors.rs index fb887c1..d38d803 100644 --- a/src/utils/errors.rs +++ b/src/utils/errors.rs @@ -98,6 +98,7 @@ impl ErrorState { pub fn process_running(process_name: ProcessName) -> bool { let name = match process_name { + ProcessName::Node => "monerod", ProcessName::P2pool => "p2pool", ProcessName::Xmrig => "xmrig", ProcessName::XmrigProxy => "xmrig-proxy", diff --git a/src/utils/panic.rs b/src/utils/panic.rs index 75046b2..4caa5b4 100644 --- a/src/utils/panic.rs +++ b/src/utils/panic.rs @@ -1,5 +1,7 @@ //---------------------------------------------------------------------------------------------------- Use -use crate::constants::{COMMIT, GUPAX_VERSION, OS_NAME, P2POOL_VERSION, XMRIG_VERSION}; +use crate::constants::{ + COMMIT, GUPAX_VERSION, NODE_VERSION, OS_NAME, P2POOL_VERSION, XMRIG_VERSION, +}; //---------------------------------------------------------------------------------------------------- #[cold] @@ -17,13 +19,15 @@ pub(crate) fn set_panic_hook(now: std::time::Instant) { "{panic_info:#?} info: - OS | {OS_NAME} - args | {args:?} - commit | {COMMIT} - gupaxx | {GUPAX_VERSION} - p2pool | {P2POOL_VERSION} (bundled) - xmrig | {XMRIG_VERSION} (bundled) - uptime | {uptime} seconds + OS | {OS_NAME} + args | {args:?} + commit | {COMMIT} + gupaxx | {GUPAX_VERSION} + monerod | {NODE_VERSION} (bundled) + p2pool | {P2POOL_VERSION} (bundled) + xmrig | {XMRIG_VERSION} (bundled) + xmrig-proxy | {XMRIG_VERSION} (bundled) + uptime | {uptime} seconds stack backtrace:\n{stack_trace}", ); diff --git a/utils/create_tmp_env.sh b/utils/create_tmp_env.sh index 319d552..d6a5391 100755 --- a/utils/create_tmp_env.sh +++ b/utils/create_tmp_env.sh @@ -93,6 +93,32 @@ mv p2pool-v4.1-windows-x64/p2pool.exe /tmp/${FOLDER}/skel/windows_b/P2Pool/p2poo rm -r p2pool-v4.1-windows-x64 rm p2pool-v4.1-windows-x64.zip +## Download Monero Binaries +# download monero into directory linux +wget https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.3.4.tar.bz2 +tar -xf monero-linux-x64-v0.18.3.4.tar.bz2 +mv monero-x86_64-linux-gnu-v0.18.3.4/monerod /tmp/${FOLDER}/skel/linux_b/node/monerod +rm -r monero-x86_64-linux-gnu-v0.18.3.4 +rm monero-linux-x64-v0.18.3.4.tar.bz2 +# download monero into directory macos-arm64 +wget https://downloads.getmonero.org/cli/monero-mac-armv8-v0.18.3.4.tar.bz2 +tar -xf monero-mac-armv8-v0.18.3.4.tar.bz2 +mv monero-aarch64-apple-darwin11-v0.18.3.4/monerod /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/node/monerod +rm -r monero-aarch64-apple-darwin11-v0.18.3.4 +rm monero-mac-armv8-v0.18.3.4.tar.bz2 +# download monero into directory macos-x64 +wget https://downloads.getmonero.org/cli/monero-mac-x64-v0.18.3.4.tar.bz2 +tar -xf monero-mac-x64-v0.18.3.4.tar.bz2 +mv monero-x86_64-apple-darwin11-v0.18.3.4/monerod /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/node/monerod +rm -r monero-x86_64-apple-darwin11-v0.18.3.4 +rm monero-mac-x64-v0.18.3.4.tar.bz2 +# download monero into directory windows +wget https://downloads.getmonero.org/cli/monero-win-x64-v0.18.3.4.zip +unzip monero-win-x64-v0.18.3.4.zip +mv monero-x86_64-w64-mingw32-v0.18.3.4/monerod.exe /tmp/${FOLDER}/skel/windows_b/node/monerod.exe +rm -r monero-x86_64-w64-mingw32-v0.18.3.4 +rm monero-win-x64-v0.18.3.4.zip + set +ex echo diff --git a/utils/package.sh b/utils/package.sh index f30853a..3f23090 100755 --- a/utils/package.sh +++ b/utils/package.sh @@ -34,12 +34,14 @@ title "Linux folder check" [[ -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" +[[ -f linux_b/node/monerod ]]; check "linux_b/node/monerod" 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" +[[ -f macos-x64_b/node/monerod ]]; check "macos-x64_b/node/monerod" 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" @@ -47,12 +49,14 @@ title "macOS-arm64 folder check" [[ -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" +[[ -f macos-arm64_b/node/monerod ]]; check "macos-arm64_b/node/monerod" 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" +[[ -f windows_b/node/monerod.exe ]]; check "windows_b/node/monerod.exe" # Get random date for tar/zip title "RNG Date" @@ -67,6 +71,7 @@ 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 +chmod +x linux_b/node/monderod 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