From 0a8deee3594f167458baa0608836139bfcca7806 Mon Sep 17 00:00:00 2001 From: hinto-janaiyo Date: Sat, 3 Dec 2022 13:37:57 -0500 Subject: [PATCH] helper: add functions for p2pool/xmrig UI -> command arguments --- Cargo.lock | 22 ++++----- README.md | 4 ++ src/constants.rs | 4 +- src/disk.rs | 16 ++++--- src/helper.rs | 115 ++++++++++++++++++++++++++++++++++++++++++++--- src/node.rs | 12 +++++ src/p2pool.rs | 2 +- src/xmrig.rs | 8 ++-- 8 files changed, 151 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 052c060..34d69b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -491,7 +491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bcf530afb40e45e14440701e5e996d7fd139e84a912a4d83a8d6a0fb3e58663" dependencies = [ "log", - "nix 0.25.0", + "nix 0.25.1", "slotmap", "thiserror", "vec_map", @@ -2143,9 +2143,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" [[package]] name = "libloading" @@ -2431,9 +2431,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ "bitflags", "cfg-if", @@ -2443,9 +2443,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ "autocfg", "bitflags", @@ -3584,7 +3584,7 @@ dependencies = [ "lazy_static", "log", "memmap2", - "nix 0.24.2", + "nix 0.24.3", "pkg-config", "wayland-client", "wayland-cursor", @@ -4781,7 +4781,7 @@ dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.24.2", + "nix 0.24.3", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -4794,7 +4794,7 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" dependencies = [ - "nix 0.24.2", + "nix 0.24.3", "once_cell", "smallvec", "wayland-sys", @@ -4806,7 +4806,7 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ - "nix 0.24.2", + "nix 0.24.3", "wayland-client", "xcursor", ] diff --git a/README.md b/README.md index 2987f87..6d57f5e 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ https://user-images.githubusercontent.com/101352116/194763334-d8e936c9-a71e-474e * A Monero node/wallet ## Build +Windows/Linux: ``` cargo build --release ``` @@ -120,5 +121,8 @@ On macOS, if you want the binary to have an icon in `Finder`, you must install [ ``` cargo bundle --release ``` +This bundles Gupax into a `Gupax.app`, the way it comes in the pre-built tars for macOS. The `build.rs` file in the repo root sets the icon in `File Explorer` for Windows. The taskbar icon & App frame icon (for all OS's) get set at runtime using pre-compiled bytes in [`src/constants.rs`](https://github.com/hinto-janaiyo/gupax/blob/main/src/constants.rs) from [`images`](https://github.com/hinto-janaiyo/gupax/blob/main/images). + +The `--release` profile is set to prefer code performance & small binary sizes over compilation speed (see [`Cargo.toml`](https://github.com/hinto-janaiyo/gupax/blob/main/Cargo.toml)). Gupax itself (with all dependencies already built) takes around 1m30s to build (vs 10s on a normal `--release`) with a Ryzen 5950x. diff --git a/src/constants.rs b/src/constants.rs index 85ea736..01cf749 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -113,7 +113,7 @@ pub const P2POOL_AUTO_SELECT: &str = "Automatically select the fastest community pub const P2POOL_SELECT_FASTEST: &str = "Select the fastest community Monero node"; pub const P2POOL_PING: &str = "Ping the built-in community Monero nodes"; pub const P2POOL_ADDRESS: &str = "You must use a primary Monero address to mine on P2Pool (starts with a 4). It is highly recommended to create a new wallet for P2Pool mining; wallet addresses are public on P2Pool!"; -pub const P2POOL_COMMAND: &str = "Start P2Pool with these arguments and override all below settings; If the [--data-api] flag is not given, Gupax will append it to the arguments automatically so that the [Status] tab can work"; +pub const P2POOL_ARGUMENTS: &str = "Start P2Pool with these arguments and override all below settings; If the [--data-api] & [--local-api] flag is not given, Gupax will append it to the arguments automatically so that the [Status] tab can work"; pub const P2POOL_SIMPLE: &str = r#"Use simple P2Pool settings: - Remote community Monero node @@ -151,7 +151,7 @@ r#"Use advanced XMRig settings: - TLS setting - Keepalive setting - Custom HTTP API IP/Port"#; -pub const XMRIG_CONFIG: &str = "Start XMRig with this config file and override all below settings; If the [http-api] options are not set, the [Status] tab will not properly show XMRig stats. If they are set, Gupax will detect which automatically IP/Port you are using"; +pub const XMRIG_ARGUMENTS: &str = "Start XMRig with these arguments and override all below settings; If the [http-api] options are not set, Gupax will append it to the arguments automatically so that the [Status] tab can work"; pub const XMRIG_ADDRESS: &str = "Specify which Monero address to send payouts to; Must be a valid primary address (starts with 4)"; pub const XMRIG_NAME: &str = "Add a unique name to identify this pool; Only [A-Za-z0-9-_] and spaces allowed; Max length = 30 characters"; pub const XMRIG_IP: &str = "Specify the pool IP to connect to with XMRig; It must be a valid IPv4 address or a valid domain name; Max length = 255 characters"; diff --git a/src/disk.rs b/src/disk.rs index cdf26dd..7e22421 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -112,7 +112,7 @@ pub fn read_to_string(file: File, path: &PathBuf) -> Result { } // Write str to console with [info!] surrounded by "---" -pub fn print_toml(toml: &str) { +pub fn print_dash(toml: &str) { info!("{}", HORIZONTAL); for i in toml.lines() { info!("{}", i); } info!("{}", HORIZONTAL); @@ -165,7 +165,7 @@ impl State { log_level: 3, node: crate::NodeEnum::C3pool, arguments: String::new(), - address: String::with_capacity(95), + address: String::with_capacity(96), name: "Local Monero Node".to_string(), ip: "localhost".to_string(), rpc: "18081".to_string(), @@ -179,8 +179,9 @@ impl State { xmrig: Xmrig { simple: true, pause: 0, - config: String::with_capacity(100), - address: String::with_capacity(95), + simple_rig: String::with_capacity(30), + arguments: String::with_capacity(300), + address: String::with_capacity(96), name: "Local P2Pool".to_string(), rig: "Gupax".to_string(), ip: "localhost".to_string(), @@ -210,7 +211,7 @@ impl State { match toml::de::from_str(string) { Ok(state) => { info!("State | Parse ... OK"); - print_toml(string); + print_dash(string); Ok(state) } Err(err) => { @@ -272,7 +273,7 @@ impl State { let string = match toml::ser::to_string(&self) { Ok(string) => { info!("State | Parse ... OK"); - print_toml(&string); + print_dash(&string); string }, Err(err) => { error!("State | Couldn't parse TOML into string ... FAIL"); return Err(TomlError::Serialize(err)) }, @@ -615,7 +616,8 @@ pub struct P2pool { pub struct Xmrig { pub simple: bool, pub pause: u8, - pub config: String, + pub simple_rig: String, + pub arguments: String, pub tls: bool, pub keepalive: bool, pub max_threads: usize, diff --git a/src/helper.rs b/src/helper.rs index 1e2502c..b11498c 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -323,6 +323,7 @@ impl Hashrate { use tokio::io::{BufReader,AsyncBufReadExt}; impl Helper { + //---------------------------------------------------------------------------------------------------- General Functions pub fn new(instant: std::time::Instant) -> Self { Self { instant, @@ -336,12 +337,6 @@ impl Helper { } } - // Intermediate function that spawns the helper thread. - pub fn spawn_helper(helper: &Arc>) { - let helper = Arc::clone(helper); - thread::spawn(move || { Self::helper(helper); }); - } - // The tokio runtime that blocks while async reading both STDOUT/STDERR // Cheaper than spawning 2 OS threads just to read 2 pipes (...right? :D) #[tokio::main] @@ -364,7 +359,113 @@ impl Helper { tokio::join![stdout_job, stderr_job]; } - // The "helper" loop + //---------------------------------------------------------------------------------------------------- P2Pool specific + // Intermediate function that parses the arguments, and spawns the P2Pool watchdog thread. + pub fn spawn_p2pool(state: &crate::disk::P2pool, api_path: &std::path::Path) { + let mut args = Vec::with_capacity(500); + // [Simple] + if state.simple { + // Build the p2pool argument + let (ip, rpc, zmq) = crate::node::enum_to_ip_rpc_zmq_tuple(state.node); // Get: (IP, RPC, ZMQ) + args.push(format!("--wallet {}", state.address)); // Wallet Address + args.push(format!("--host {}", ip)); // IP Address + args.push(format!("--rpc-port {}", rpc)); // RPC Port + args.push(format!("--zmq-port {}", zmq)); // ZMQ Port + args.push(format!("--data-api {}", api_path.display())); // API Path + args.push("--local-api".to_string()); // Enable API + args.push("--no-color".to_string()); // Remove color escape sequences, Gupax terminal can't parse it :( + args.push("--mini".to_string()); // P2Pool Mini + + // [Advanced] + } else { + // Overriding command arguments + if !state.arguments.is_empty() { + for arg in state.arguments.split_whitespace() { + args.push(arg.to_string()); + } + // Else, build the argument + } else { + args.push(state.address.clone()); // Wallet + args.push(state.selected_ip.clone()); // IP + args.push(state.selected_rpc.clone()); // RPC + args.push(state.selected_zmq.clone()); // ZMQ + args.push("--local-api".to_string()); // Enable API + args.push("--no-color".to_string()); // Remove color escape sequences + if state.mini { args.push("--mini".to_string()); }; // Mini + args.push(format!("--loglevel {}", state.log_level)); // Log Level + args.push(format!("--out-peers {}", state.out_peers)); // Out Peers + args.push(format!("--in-peers {}", state.in_peers)); // In Peers + args.push(format!("--data-api {}", api_path.display())); // API Path + } + } + + // Print arguments to console + crate::disk::print_dash(&format!("P2Pool | Launch arguments ... {:#?}", args)); + + // Spawn watchdog thread + thread::spawn(move || { + Self::spawn_p2pool_watchdog(args); + }); + } + + // The actual P2Pool watchdog tokio runtime. + #[tokio::main] + pub async fn spawn_p2pool_watchdog(args: Vec) { + // 1. Create command + // 2. Spawn STDOUT/STDERR thread + // 3. Loop forever as watchdog until process dies + } + + //---------------------------------------------------------------------------------------------------- XMRig specific + // Intermediate function that parses the arguments, and spawns the XMRig watchdog thread. + pub fn spawn_xmrig(state: &crate::disk::Xmrig, api_path: &std::path::Path) { + let mut args = Vec::with_capacity(500); + if state.simple { + let rig_name = if state.simple_rig.is_empty() { GUPAX_VERSION.to_string() } else { state.simple_rig.clone() }; // Rig name + args.push(format!("--threads {}", state.current_threads)); // Threads + args.push(format!("--user {}", state.simple_rig)); // Rig name + args.push(format!("--url 127.0.0.1:3333")); // Local P2Pool (the default) + args.push("--no-color".to_string()); // No color escape codes + if state.pause != 0 { args.push(format!("--pause-on-active {}", state.pause)); } // Pause on active + } else { + if !state.arguments.is_empty() { + for arg in state.arguments.split_whitespace() { + args.push(arg.to_string()); + } + } else { + args.push(format!("--user {}", state.address.clone())); // Wallet + args.push(format!("--threads {}", state.current_threads)); // Threads + args.push(format!("--rig-id {}", state.selected_rig)); // Rig ID + args.push(format!("--url {}:{}", state.selected_ip.clone(), state.selected_port.clone())); // IP/Port + args.push(format!("--http-host {}", state.api_ip).to_string()); // HTTP API IP + args.push(format!("--http-port {}", state.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 + if state.pause != 0 { args.push(format!("--pause-on-active {}", state.pause)); } // Pause on active + } + } + // Print arguments to console + crate::disk::print_dash(&format!("XMRig | Launch arguments ... {:#?}", args)); + + // Spawn watchdog thread + thread::spawn(move || { + Self::spawn_xmrig_watchdog(args); + }); + } + + // The actual XMRig watchdog tokio runtime. + #[tokio::main] + pub async fn spawn_xmrig_watchdog(args: Vec) { + } + + //---------------------------------------------------------------------------------------------------- The "helper" + // Intermediate function that spawns the helper thread. + pub fn spawn_helper(helper: &Arc>) { + let helper = Arc::clone(helper); + thread::spawn(move || { Self::helper(helper); }); + } + // [helper] = Actual Arc // [h] = Temporary lock that gets dropped // [jobs] = Vector of async jobs ready to go diff --git a/src/node.rs b/src/node.rs index 7de8544..4cebfad 100644 --- a/src/node.rs +++ b/src/node.rs @@ -143,6 +143,18 @@ pub fn enum_to_ip(node: NodeEnum) -> &'static str { } } +// Returns a tuple of (IP, RPC_PORT, ZMQ_PORT) +pub fn enum_to_ip_rpc_zmq_tuple(node: NodeEnum) -> (&'static str, &'static str, &'static str) { + // [.unwrap()] should be safe, IP:PORTs are constants after all. + let (ip, rpc) = enum_to_ip(node).rsplit_once(':').unwrap(); + // Get ZMQ, grr... plowsof is the only 18084 zmq person. + let zmq = match node { + Plowsof1|Plowsof2 => "18084", + _ => "18083", + }; + (ip, rpc, zmq) +} + // 5000 = 4 max length pub fn format_ms(ms: u128) -> String { match ms.to_string().len() { diff --git a/src/p2pool.rs b/src/p2pool.rs index 5e07513..37c02f1 100644 --- a/src/p2pool.rs +++ b/src/p2pool.rs @@ -66,7 +66,7 @@ egui::Frame::none() let width = (width/10.0) - SPACE; ui.style_mut().override_text_style = Some(Monospace); ui.add_sized([width, text_edit], Label::new("Command arguments:")); - ui.add_sized([ui.available_width(), text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.arguments), r#"--wallet <...> --host <...>"#)).on_hover_text(P2POOL_COMMAND); + ui.add_sized([ui.available_width(), text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.arguments), r#"--wallet <...> --host <...>"#)).on_hover_text(P2POOL_ARGUMENTS); self.arguments.truncate(1024); })}); ui.set_enabled(self.arguments.is_empty()); diff --git a/src/xmrig.rs b/src/xmrig.rs index 65f9647..8408bbd 100644 --- a/src/xmrig.rs +++ b/src/xmrig.rs @@ -45,11 +45,11 @@ impl Xmrig { ui.group(|ui| { ui.horizontal(|ui| { let width = (width/10.0) - SPACE; ui.style_mut().override_text_style = Some(Monospace); - ui.add_sized([width, text_edit], Label::new("Config file:")); - ui.add_sized([ui.available_width(), text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.config), r#"...config.json"#)).on_hover_text(XMRIG_CONFIG); - self.config.truncate(1024); + ui.add_sized([width, text_edit], Label::new("Command arguments:")); + ui.add_sized([ui.available_width(), text_edit], TextEdit::hint_text(TextEdit::singleline(&mut self.arguments), r#"--url <...> --user <...> --config <...>"#)).on_hover_text(XMRIG_ARGUMENTS); + self.arguments.truncate(1024); })}); - ui.set_enabled(self.config.is_empty()); + ui.set_enabled(self.arguments.is_empty()); //---------------------------------------------------------------------------------------------------- Address ui.group(|ui| { let width = width - SPACE;