diff --git a/Cargo.lock b/Cargo.lock
index 1d97e07..7a047f7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -133,6 +133,15 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+[[package]]
+name = "block-buffer"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
+dependencies = [
+ "generic-array",
+]
+
[[package]]
name = "bumpalo"
version = "3.11.0"
@@ -346,6 +355,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "cpufeatures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "crc32fast"
version = "1.3.2"
@@ -427,6 +445,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
[[package]]
name = "cty"
version = "0.2.2"
@@ -440,7 +468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
dependencies = [
"byteorder",
- "digest",
+ "digest 0.9.0",
"rand_core 0.5.1",
"subtle",
"zeroize",
@@ -490,6 +518,36 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "digest"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
[[package]]
name = "dispatch"
version = "0.2.0"
@@ -950,15 +1008,21 @@ name = "gupax"
version = "0.1.0"
dependencies = [
"chrono",
+ "dirs",
"eframe",
"egui",
"egui_extras",
"env_logger",
+ "hex-literal",
"image",
"log",
"monero",
"num_cpus",
"regex",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "toml",
]
[[package]]
@@ -1699,6 +1763,17 @@ dependencies = [
"bitflags",
]
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom 0.2.7",
+ "redox_syscall",
+ "thiserror",
+]
+
[[package]]
name = "regex"
version = "1.6.0"
@@ -1820,6 +1895,17 @@ dependencies = [
"pkg-config",
]
+[[package]]
+name = "sha2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.10.5",
+]
+
[[package]]
name = "shared_library"
version = "0.1.9"
diff --git a/Cargo.toml b/Cargo.toml
index e80bacc..996ed95 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,27 +4,33 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-egui = "0.19.0"
-eframe = "0.19.0"
-egui_extras = { version = "0.19.0", features = ["image"] }
-image = { version = "0.24.4", features = ["png"] }
-num_cpus = "1.13.1"
-monero = "0.18.0"
-regex = "1.6.0"
-log = "0.4.17"
-env_logger = "0.9.1"
+dirs = "4.0.0"
chrono = "0.4.22"
+eframe = "0.19.0"
+egui = "0.19.0"
+egui_extras = { version = "0.19.0", features = ["image"] }
+env_logger = "0.9.1"
+hex-literal = "0.3.4"
+image = { version = "0.24.4", features = ["png"] }
+log = "0.4.17"
+monero = "0.18.0"
+num_cpus = "1.13.1"
+regex = "1.6.0"
+serde = "1.0.145"
+serde_derive = "1.0.145"
+sha2 = "0.10.6"
+toml = "0.5.9"
[profile.optimized]
-inherits = "release"
-strip = "debuginfo"
-debug = false
+codegen-units = 1
debug-assertions = false
+debug = false
+incremental = true
+inherits = "release"
lto = true
overflow-checks = false
-incremental = true
-codegen-units = 1
rpath = false
+strip = "debuginfo"
[profile.optimized.package."*"]
opt-level = 3
diff --git a/build.sh b/build.sh
index baf276e..f5a1341 100755
--- a/build.sh
+++ b/build.sh
@@ -4,4 +4,17 @@ set -e
[[ $PWD = */gupax ]]
-RUSTFLAGS="-C target-cpu=native" cargo build --profile optimized && du -hs target/optimized/gupax
+if [[ $1 = *all* ]]; then
+ echo "=== building all ==="
+ echo "=== windows ==="
+ cargo build --profile optimized --target x86_64-pc-windows-gnu
+# echo "=== macos ==="
+# cargo build --profile optimized --target x86_64-apple-darwin
+ echo "=== linux ==="
+ cargo build --profile optimized
+ du -hs target/x86_64-pc-windows-gnu/optimized/gupax target/x86_64-apple-darwin/optimized/gupax target/optimized/gupax
+else
+ echo "=== building linux cpu optimized ==="
+ RUSTFLAGS="-C target-cpu=native" cargo build --profile optimized
+ du -hs target/optimized/gupax
+fi
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..c082873
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,49 @@
+# Gupax source files
+* [State](#State)
+* [Structure](#Structure)
+* [Bootstrap](#Bootstrap)
+
+## Structure
+| File/Folder | Purpose |
+|----------------|---------|
+| `about.rs` | Struct/impl for `About` tab
+| `constants.rs` | General constants needed in Gupax
+| `gupax.rs` | Struct/impl for `Gupax` tab
+| `main.rs` | Struct/enum/impl for `App/Tab/State`, init functions, main function
+| `p2pool.rs` | Struct/impl for `P2Pool` tab
+| `status.rs` | Struct/impl for `Status` tab
+| `toml.rs` | Struct/impl for `gupax.toml`, the disk state
+| `xmrig.rs` | Struct/impl for `XMRig` tab
+
+## Bootstrap
+This is how Gupax works internally when starting up, divided into 3 sections.
+
+1. **INIT**
+ - Initialize custom console logging with `log`, `env_logger` || *warn!*
+ - Initialize misc data (structs, text styles, thread count, images, etc) || *panic!*
+ - Check for admin privilege (for XMRig) || *warn!*
+ - Attempt to read `gupax.toml` || *warn!*, *initialize config with default options*
+ - If errors were found, pop-up window
+
+2. **AUTO-UPDATE**
+ - If `auto_update` == `true`, pop-up auto-updating window || *info!*, *skip auto-update*
+ - Multi-threaded GitHub API check on Gupax -> P2Pool -> XMRig || *warn!*, *skip auto-update*
+ - Multi-threaded download if current version != new version || *warn!*, *skip auto-update*
+ - After download, atomically replace current binaries with new || *warn!*, *skip auto-update*
+ - Update version metadata || *warn!*, *skip auto-update*
+
+3. **MAIN**
+ - All data must be initialized at this point, either via `gupax.toml` or default options || *panic!*
+ - Start `App` frame || *panic!*
+ - Write state to `gupax.toml` on user clicking `Save` (after checking input for correctness) || *warn!*
+ - If `ask_before_quit` == `true`, check for running processes, unsaved state, and update connections before quitting
+ - Kill processes, kill connections, exit
+
+## State
+Internal state is saved in the "OS data folder" as `gupax.toml`, using the [TOML](https://github.com/toml-lang/toml) format. If the version can't be parsed (not in the `vX.X.X` or `vX.X` format), the auto-updater will be skipped. [If not found, a default `gupax.toml` file will be created with `Toml::default`.](https://github.com/hinto-janaiyo/gupax/blob/main/src/toml.rs)
+
+| OS | Data Folder | Example |
+|----------|----------------------------------------- |-----------------------------------------------------------|
+| Windows | `{FOLDERID_LocalAppData}` | C:\Users\Alice\AppData\Roaming\Gupax\gupax.toml |
+| macOS | `$HOME`/Library/Application Support | /Users/Alice/Library/Application Support/Gupax/gupax.toml |
+| Linux | `$XDG_DATA_HOME` or `$HOME`/.local/share | /home/alice/.local/share/gupax/gupax.toml |
diff --git a/src/constants.rs b/src/constants.rs
index 3b13e42..77395cd 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -16,11 +16,17 @@
// along with this program. If not, see .
use std::net::{Ipv4Addr,SocketAddrV4};
+use sha2::{Sha256,Digest};
+use hex_literal::hex;
+use std::{io, fs};
+
+// These are the versions bundled with Gupax.
+pub const P2POOL_VERSION: &'static str = "v2.4";
+pub const XMRIG_VERSION: &'static str = "v6.18.0";
-// Compile-time constants
pub const BYTES_ICON: &[u8] = include_bytes!("../images/png/icon.png");
pub const BYTES_BANNER: &[u8] = include_bytes!("../images/png/banner.png");
-pub const P2POOL_BASE_ARGS: &'static str = "--host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --loglevel 3 --out-peers 10 --in-peers 10";
+pub const P2POOL_BASE_ARGS: &'static str = "";
pub const XMRIG_BASE_ARGS: &'static str = "--http-host=127.0.0.1 --http-port=18088 --algo=rx/0 --coin=Monero --randomx-cache-qos";
// OS specific
diff --git a/src/main.rs b/src/main.rs
index cf0601e..d5824e0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,7 +16,7 @@
// along with this program. If not, see .
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
-use eframe::egui;
+use eframe::{egui,NativeOptions};
use egui::{Vec2,Pos2};
use std::process::exit;
use std::thread;
@@ -36,12 +36,13 @@ use std::io::Write;
use std::time::Instant;
mod constants;
+mod toml;
mod about;
mod status;
mod gupax;
mod p2pool;
mod xmrig;
-use {constants::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*};
+use {constants::*,crate::toml::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*};
// The state of the outer [App].
// See the [State] struct for the
@@ -60,6 +61,8 @@ pub struct App {
xmrig: bool,
state: State,
og: State,
+ allowed_to_close: bool,
+ show_confirmation_dialog: bool,
}
impl App {
@@ -79,12 +82,12 @@ impl App {
let resolution = cc.integration_info.window_info.size;
init_text_styles(&cc.egui_ctx, resolution[0] as f32);
let banner = match RetainedImage::from_image_bytes("banner.png", BYTES_BANNER) {
- Ok(banner) => { info!("Banner loading OK"); banner },
+ Ok(banner) => { info!("Banner loading ... OK"); banner },
Err(err) => { error!("{}", err); panic!("{}", err); },
};
let mut state = State::new();
let mut og = State::new();
- info!("{:?}", resolution);
+ info!("Frame resolution ... {:#?}", resolution);
Self {
version,
name_version,
@@ -99,6 +102,8 @@ impl App {
xmrig: false,
state,
og,
+ allowed_to_close: false,
+ show_confirmation_dialog: false,
}
}
}
@@ -169,48 +174,58 @@ fn init_text_styles(ctx: &egui::Context, width: f32) {
// style.spacing.button_padding = Vec2::new(scale/2.0, scale/2.0);
ctx.set_style(style);
ctx.set_pixels_per_point(1.0);
+ ctx.request_repaint();
}
-fn main() {
+fn init_logger() {
use env_logger::fmt::Color;
Builder::new().format(|buf, record| {
let level;
let mut style = buf.style();
match record.level() {
Level::Error => { style.set_color(Color::Red); level = "ERROR" },
- Level::Warn => { style.set_color(Color::Yellow); level = "WARN " },
- Level::Info => { style.set_color(Color::White); level = "INFO " },
+ Level::Warn => { style.set_color(Color::Yellow); level = "WARN" },
+ Level::Info => { style.set_color(Color::White); level = "INFO" },
Level::Debug => { style.set_color(Color::Blue); level = "DEBUG" },
Level::Trace => { style.set_color(Color::Magenta); level = "TRACE" },
};
writeln!(
buf,
- "| {} | {} | {}:{} | {}",
+ "[{}] [{}] [{}:{}] {}",
style.set_bold(true).value(level),
buf.style().set_dimmed(true).value(chrono::Local::now().format("%F %T%.3f")),
buf.style().set_dimmed(true).value(record.file().unwrap_or("???")),
buf.style().set_dimmed(true).value(record.line().unwrap_or(0)),
record.args(),
)
- }).filter_level(LevelFilter::Info).write_style(WriteStyle::Always).parse_default_env().format_timestamp_millis().try_init();
- info!("test");
- warn!("test");
- error!("test");
- debug!("test");
+ }).filter_level(LevelFilter::Info).write_style(WriteStyle::Always).parse_default_env().format_timestamp_millis().init();
+ info!("init_logger() ... OK");
+}
+fn init_options() -> NativeOptions {
let mut options = eframe::NativeOptions::default();
options.min_window_size = Option::from(Vec2::new(1280.0, 720.0));
options.max_window_size = Option::from(Vec2::new(3180.0, 2160.0));
options.initial_window_size = Option::from(Vec2::new(1280.0, 720.0));
options.follow_system_theme = false;
options.default_theme = eframe::Theme::Dark;
- let icon = image::load_from_memory(BYTES_ICON).expect("Failed to read ICON bytes").to_rgba8();
+ let icon = image::load_from_memory(BYTES_ICON).expect("Failed to read icon bytes").to_rgba8();
let (icon_width, icon_height) = icon.dimensions();
options.icon_data = Some(eframe::IconData {
rgba: icon.into_raw(),
width: icon_width,
height: icon_height,
});
+ info!("init_options() ... OK");
+ options
+}
+
+fn main() {
+ init_logger();
+ let options = init_options();
+ let toml = Toml::get();
+ info!("Printing gupax.toml...");
+ eprintln!("{:#?}", toml);
let now = Instant::now();
eframe::run_native(
"Gupax",
@@ -220,7 +235,29 @@ fn main() {
}
impl eframe::App for App {
- fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
+ fn on_close_event(&mut self) -> bool {
+ self.show_confirmation_dialog = true;
+ self.allowed_to_close
+ }
+ fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
+ if self.show_confirmation_dialog {
+ // Show confirmation dialog:
+ egui::CentralPanel::default().show(ctx, |ui| {
+ let width = ui.available_width();
+ let width = width - 10.0;
+ let height = ui.available_height();
+ init_text_styles(ctx, width);
+ ui.add_sized([width, height/2.0], Label::new("Are you sure you want to quit?"));
+ ui.group(|ui| {
+ if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() {
+ exit(0);
+ } else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() {
+ self.show_confirmation_dialog = false;
+ }
+ });
+ });
+ return
+ }
// Top: Tabs
egui::CentralPanel::default().show(ctx, |ui| {
init_text_styles(ctx, ui.available_width());
@@ -355,16 +392,3 @@ impl eframe::App for App {
});
}
}
-
-pub trait View {
- fn ui(&mut self, ui: &mut egui::Ui);
-}
-
-/// Something to view
-pub trait Demo {
- /// `&'static` so we can also use it as a key to store open/close state.
- fn name(&self) -> &'static str;
-
- /// Show windows, etc
- fn show(&mut self, ctx: &egui::Context, open: &mut bool);
-}
diff --git a/src/toml.rs b/src/toml.rs
new file mode 100644
index 0000000..b2bd9a1
--- /dev/null
+++ b/src/toml.rs
@@ -0,0 +1,223 @@
+// Gupax - GUI Uniting P2Pool And XMRig
+//
+// Copyright (c) 2022 hinto-janaiyo
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// This handles reading/parsing the state file: [gupax.toml]
+// The TOML format is used. This struct hierarchy directly
+// translates into the TOML parser:
+// Toml/
+// ├─ Gupax/
+// │ ├─ ...
+// ├─ P2pool/
+// │ ├─ ...
+// ├─ Xmrig/
+// │ ├─ ...
+// ├─ Version/
+// ├─ ...
+
+use std::{fs,env};
+use std::fmt::Display;
+use std::path::{Path,PathBuf};
+use serde_derive::{Serialize,Deserialize};
+use log::*;
+
+//---------------------------------------------------------------------------------------------------- Impl
+// Since [State] is already used in [main.rs] to represent
+// working state, [Toml] is used to disk state.
+impl Toml {
+ pub fn default() -> Self {
+ use crate::constants::{P2POOL_VERSION,XMRIG_VERSION};
+ Self {
+ gupax: Gupax {
+ auto_update: true,
+ ask_before_quit: true,
+ p2pool_path: DEFAULT_P2POOL_PATH.to_string(),
+ xmrig_path: DEFAULT_XMRIG_PATH.to_string(),
+ },
+ p2pool: P2pool {
+ simple: true,
+ mini: true,
+ out_peers: 10,
+ in_peers: 10,
+ log_level: 3,
+ monerod: "localhost".to_string(),
+ rpc: 18081,
+ zmq: 18083,
+ address: "".to_string(),
+ },
+ xmrig: Xmrig {
+ simple: true,
+ tls: false,
+ nicehash: false,
+ keepalive: false,
+ threads: 1,
+ priority: 2,
+ pool: "localhost:3333".to_string(),
+ address: "".to_string(),
+ },
+ version: Version {
+ p2pool: P2POOL_VERSION.to_string(),
+ xmrig: XMRIG_VERSION.to_string(),
+ },
+ }
+ }
+
+ pub fn get() -> Result {
+ // Get OS data folder
+ // Linux | $XDG_DATA_HOME or $HOME/.local/share | /home/alice/.local/state
+ // macOS | $HOME/Library/Application Support | /Users/Alice/Library/Application Support
+ // Windows | {FOLDERID_RoamingAppData} | C:\Users\Alice\AppData\Roaming
+ let mut path = match dirs::data_dir() {
+ Some(mut path) => {
+ path.push(DIRECTORY);
+ info!("{}, OS data path ... OK", path.display());
+ path
+ },
+ None => { error!("Couldn't get OS PATH for data"); return Err(TomlError::Path(PATH_ERROR.to_string())) },
+ };
+
+ // Create directory
+ fs::create_dir_all(&path)?;
+
+ // Attempt to read file, create default if not found
+ path.push(FILENAME);
+ let file = match fs::read_to_string(&path) {
+ Ok(file) => file,
+ Err(err) => {
+ error!("TOML not found, attempting to create default");
+ let default = match toml::ser::to_string(&Toml::default()) {
+ Ok(o) => { info!("TOML serialization ... OK"); o },
+ Err(e) => { error!("Couldn't serialize default TOML file: {}", e); return Err(TomlError::Serialize(e)) },
+ };
+ fs::write(&path, default)?;
+ info!("TOML write ... OK");
+ fs::read_to_string(&path)?
+ },
+ };
+ info!("TOML read ... OK");
+
+ // Attempt to parse, return Result
+ match toml::from_str(&file) {
+ Ok(file) => { info!("TOML parse ... OK"); Ok(file) },
+ Err(err) => { error!("Couldn't parse TOML file"); Err(TomlError::Parse(err)) },
+ }
+ }
+}
+
+impl Display for TomlError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use TomlError::*;
+ match self {
+ Io(err) => write!(f, "{} | {}", ERROR, err),
+ Path(err) => write!(f, "{} | {}", ERROR, err),
+ Parse(err) => write!(f, "{} | {}", ERROR, err),
+ Serialize(err) => write!(f, "{} | {}", ERROR, err),
+ }
+ }
+}
+
+impl From for TomlError {
+ fn from(err: std::io::Error) -> Self {
+ TomlError::Io(err)
+ }
+}
+
+fn main() {
+ let state = match Toml::get() {
+ Ok(state) => { println!("OK"); state },
+ Err(err) => panic!(),
+ };
+}
+
+//---------------------------------------------------------------------------------------------------- Const
+const FILENAME: &'static str = "gupax.toml";
+const ERROR: &'static str = "TOML Error";
+const PATH_ERROR: &'static str = "PATH for state directory could not be not found";
+#[cfg(target_os = "windows")]
+const DIRECTORY: &'static str = "Gupax";
+#[cfg(target_os = "macos")]
+const DIRECTORY: &'static str = "Gupax";
+#[cfg(target_os = "linux")]
+const DIRECTORY: &'static str = "gupax";
+#[cfg(target_os = "windows")]
+const DEFAULT_P2POOL_PATH: &'static str = r"P2Pool\p2pool.exe";
+#[cfg(target_os = "macos")]
+const DEFAULT_P2POOL_PATH: &'static str = "P2Pool/p2pool";
+#[cfg(target_os = "linux")]
+const DEFAULT_P2POOL_PATH: &'static str = "p2pool/p2pool";
+#[cfg(target_os = "windows")]
+const DEFAULT_XMRIG_PATH: &'static str = r"XMRig\xmrig.exe";
+#[cfg(target_os = "macos")]
+const DEFAULT_XMRIG_PATH: &'static str = "XMRig/xmrig";
+#[cfg(target_os = "linux")]
+const DEFAULT_XMRIG_PATH: &'static str = "xmrig/xmrig";
+
+//---------------------------------------------------------------------------------------------------- Error Enum
+#[derive(Debug)]
+pub enum TomlError {
+ Io(std::io::Error),
+ Path(String),
+ Parse(toml::de::Error),
+ Serialize(toml::ser::Error),
+}
+
+//---------------------------------------------------------------------------------------------------- Structs
+#[derive(Debug,Deserialize,Serialize)]
+pub struct Toml {
+ gupax: Gupax,
+ p2pool: P2pool,
+ xmrig: Xmrig,
+ version: Version,
+}
+
+#[derive(Debug,Deserialize,Serialize)]
+struct Gupax {
+ auto_update: bool,
+ ask_before_quit: bool,
+ p2pool_path: String,
+ xmrig_path: String,
+}
+
+#[derive(Debug,Deserialize,Serialize)]
+struct P2pool {
+ simple: bool,
+ mini: bool,
+ out_peers: u8,
+ in_peers: u8,
+ log_level: u8,
+ monerod: String,
+ rpc: u16,
+ zmq: u16,
+ address: String,
+}
+
+#[derive(Debug,Deserialize,Serialize)]
+struct Xmrig {
+ simple: bool,
+ tls: bool,
+ nicehash: bool,
+ keepalive: bool,
+ threads: u16,
+ priority: u8,
+ pool: String,
+ address: String,
+}
+
+#[derive(Debug,Deserialize,Serialize)]
+struct Version {
+ p2pool: String,
+ xmrig: String,
+}