├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── audit.yml │ ├── ci.yml │ ├── lockbud.yml │ ├── rust.yml │ └── typo.yml ├── .gitignore ├── .gitmodules ├── ARCHITECTURE.md ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── DIFFERENCES.md ├── IDEAS.md ├── LICENSE ├── NOTES_ALGORITHM.md ├── README.md ├── assets ├── cpu.json ├── donation_qr.png └── images │ ├── banner.png │ ├── benchmarks.png │ ├── ferris │ ├── cute.png │ ├── error.png │ ├── gesture.png │ ├── happy.png │ ├── oops.png │ ├── panic.png │ └── sudo.png │ ├── gplv3-with-text-136x68.png │ ├── icons │ ├── icon.ico │ ├── icon.png │ └── icon@2x.png │ ├── local.png │ ├── local_node.png │ ├── payouts.png │ ├── processes.png │ ├── remote.png │ ├── thread_model.png │ ├── xmrig.png │ ├── xvb.png │ ├── xvb_raffle_stats.png │ └── xvb_tab.png ├── build.rs ├── nano_config.json ├── p2pool_nano_peers.txt ├── pgp └── cyrix126.asc ├── rust-toolchain.toml ├── src ├── app │ ├── eframe_impl.rs │ ├── keys.rs │ ├── mod.rs │ ├── panels │ │ ├── bottom.rs │ │ ├── middle │ │ │ ├── about.rs │ │ │ ├── common │ │ │ │ ├── console.rs │ │ │ │ ├── header_tab.rs │ │ │ │ ├── list_poolnode.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── state_edit_field.rs │ │ │ │ └── toggle.rs │ │ │ ├── gupax.rs │ │ │ ├── mod.rs │ │ │ ├── node.rs │ │ │ ├── p2pool │ │ │ │ ├── advanced.rs │ │ │ │ ├── mod.rs │ │ │ │ └── simple.rs │ │ │ ├── status │ │ │ │ ├── benchmarks.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── p2pool.rs │ │ │ │ └── processes.rs │ │ │ ├── xmrig.rs │ │ │ ├── xmrig_proxy.rs │ │ │ └── xvb.rs │ │ ├── mod.rs │ │ ├── quit_error.rs │ │ └── top.rs │ └── quit.rs ├── cli.rs ├── components │ ├── gupax.rs │ ├── mod.rs │ ├── node.rs │ └── update.rs ├── disk │ ├── consts.rs │ ├── errors.rs │ ├── gupax_p2pool_api.rs │ ├── mod.rs │ ├── node.rs │ ├── pool.rs │ ├── state.rs │ ├── status.rs │ └── tests.rs ├── helper │ ├── mod.rs │ ├── node.rs │ ├── p2pool.rs │ ├── tests.rs │ ├── xrig │ │ ├── mod.rs │ │ ├── xmrig.rs │ │ └── xmrig_proxy.rs │ └── xvb │ │ ├── algorithm.rs │ │ ├── mod.rs │ │ ├── nodes.rs │ │ ├── priv_stats.rs │ │ ├── public_stats.rs │ │ └── rounds.rs ├── inits.rs ├── main.rs ├── miscs.rs └── utils │ ├── constants.rs │ ├── errors.rs │ ├── ferris.rs │ ├── human.rs │ ├── macros.rs │ ├── mod.rs │ ├── panic.rs │ ├── regex.rs │ ├── resets.rs │ ├── sudo.rs │ └── xmr.rs ├── typos.toml └── utils ├── README.md ├── create_tmp_env.sh ├── move_binary_inside.sh ├── package.sh ├── prepare.sh └── skel ├── linux └── gupaxx ├── linux_b ├── gupaxx ├── p2pool │ └── p2pool └── xmrig │ └── xmrig ├── macos-arm64 └── Gupaxx.app │ └── Contents │ ├── Info.plist │ ├── MacOS │ └── gupaxx │ └── Resources │ └── Gupaxx.icns ├── macos-arm64_b └── Gupaxx.app │ └── Contents │ ├── Info.plist │ ├── MacOS │ ├── gupaxx │ ├── p2pool │ │ └── p2pool │ └── xmrig │ │ └── xmrig │ └── Resources │ └── Gupaxx.icns ├── macos-x64 └── Gupaxx.app │ └── Contents │ ├── Info.plist │ ├── MacOS │ └── gupaxx │ └── Resources │ └── Gupaxx.icns ├── macos-x64_b └── Gupaxx.app │ └── Contents │ ├── Info.plist │ ├── MacOS │ ├── gupaxx │ ├── p2pool │ │ └── p2pool │ └── xmrig │ │ └── xmrig │ └── Resources │ └── Gupaxx.icns ├── windows └── Gupaxx.exe └── windows_b ├── Gupaxx.exe ├── P2Pool └── p2pool.exe └── XMRig ├── WinRing0x64.sys └── xmrig.exe /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a bug report 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## OS & Version: 11 | e.g: Windows 11, Gupaxx v1.4.2 12 | 13 | ## Bug 14 | What is the bug? 15 | 16 | ## Steps 17 | Steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. See error 21 | 22 | ## Screenshots 23 | If possible, add screenshots to help explain the bug. 24 | 25 | ## Crash report 26 | If Gupaxx is crashing, you may have a file here: 27 | 28 | - Windows: `C:\Users\YOUR_USER_NAME\AppData\Roaming\Gupaxx\crash.txt` 29 | - macOS: `/Users/YOUR_USER_NAME/Library/Application Support/Gupaxx/crash.txt` 30 | - Linux: `/home/YOUR_USER_NAME/.local/share/gupaxx/crash.txt` 31 | 32 | If you do, please upload or copy-paste `crash.txt` here: 33 | ``` 34 | paste inside here. 35 | ``` 36 | ## Additional context 37 | 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request a feature 4 | title: '' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Feature request** 11 | Describe the feature you're requesting. 12 | 13 | **Additional context** 14 | Add any other context or screenshots about the feature request. 15 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | # This runs `cargo audit` on all dependencies (only if Cargo deps changed) 2 | 3 | name: Audit 4 | 5 | on: 6 | push: 7 | paths: 8 | - '**/Cargo.toml' 9 | - '**/Cargo.lock' 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | audit: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Cache 21 | uses: actions/cache@v4 22 | with: 23 | path: | 24 | ~/.cargo 25 | target 26 | key: audit 27 | - uses: actions/checkout@v4 28 | with: 29 | submodules: recursive 30 | - name: Install dependencies 31 | run: cargo install cargo-audit --locked 32 | - name: Audit 33 | run: cargo audit 34 | 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Release Artifacts 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | CARGO_TERM_COLOR: always 8 | RUST_BACKTRACE: "full" 9 | 10 | jobs: 11 | ci: 12 | runs-on: ${{ matrix.os }} 13 | 14 | strategy: 15 | matrix: 16 | os: [macos-13, ubuntu-22.04] 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | submodules: recursive 23 | 24 | - name: Cache 25 | uses: actions/cache@v4 26 | with: 27 | path: | 28 | ~/.cargo 29 | target 30 | key: ${{ matrix.os }} 31 | 32 | - name: Install dependencies 33 | run: | 34 | if [ "$RUNNER_OS" == "Linux" ]; then 35 | sudo apt update 36 | sudo apt install -y libgtk-3-dev mingw-w64 37 | rustup target install x86_64-pc-windows-gnu 38 | elif [ "$RUNNER_OS" == "macOS" ]; then 39 | cargo install cargo-bundle 40 | rustup target install aarch64-apple-darwin 41 | rustup target install x86_64-apple-darwin 42 | fi 43 | shell: bash 44 | 45 | 46 | - name: Build 47 | run: | 48 | if [ "$RUNNER_OS" == "macOS" ]; then 49 | cargo bundle --release --target x86_64-apple-darwin 50 | cargo bundle --release --target aarch64-apple-darwin 51 | mv target/x86_64-apple-darwin/release/bundle/osx/Gupaxx.app Gupaxx-macos-x64.app 52 | mv target/aarch64-apple-darwin/release/bundle/osx/Gupaxx.app Gupaxx-macos-arm64.app 53 | cargo bundle --release --target x86_64-apple-darwin --features=bundle 54 | cargo bundle --release --target aarch64-apple-darwin --features=bundle 55 | mv target/x86_64-apple-darwin/release/bundle/osx/Gupaxx.app Gupaxx-macos-x64.app_b 56 | mv target/aarch64-apple-darwin/release/bundle/osx/Gupaxx.app Gupaxx-macos-arm64.app_b 57 | tar -cf macos.tar Gupaxx-macos-arm64.app Gupaxx-macos-x64.app Gupaxx-macos-arm64.app_b Gupaxx-macos-x64.app_b 58 | elif [ "$RUNNER_OS" == "Linux" ]; then 59 | cargo build --release --target x86_64-unknown-linux-gnu 60 | mv target/x86_64-unknown-linux-gnu/release/gupaxx . 61 | cargo build --release --target x86_64-unknown-linux-gnu --features=bundle 62 | mv target/x86_64-unknown-linux-gnu/release/gupaxx gupaxx_b 63 | tar -cf linux.tar gupaxx gupaxx_b 64 | cargo build --release --target x86_64-pc-windows-gnu 65 | mv target/x86_64-pc-windows-gnu/release/gupaxx.exe . 66 | cargo build --release --target x86_64-pc-windows-gnu --features=bundle 67 | mv target/x86_64-pc-windows-gnu/release/gupaxx.exe gupaxx_b.exe 68 | tar -cf windows.tar gupaxx.exe gupaxx_b.exe 69 | fi 70 | shell: bash 71 | 72 | - name: Archive (Windows) 73 | if: ${{ runner.os == 'Linux' }} 74 | uses: actions/upload-artifact@v4 75 | with: 76 | name: windows 77 | path: windows.tar 78 | 79 | - name: Archive 80 | if: ${{ runner.os == 'macOS' }} 81 | uses: actions/upload-artifact@v4 82 | with: 83 | name: macos 84 | path: macos.tar 85 | 86 | - name: Archive (Linux) 87 | if: ${{ runner.os == 'Linux' }} 88 | uses: actions/upload-artifact@v4 89 | with: 90 | name: linux 91 | path: linux.tar 92 | -------------------------------------------------------------------------------- /.github/workflows/lockbud.yml: -------------------------------------------------------------------------------- 1 | name: Lockbud 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**/*.md' 7 | 8 | jobs: 9 | test: 10 | name: lockbud 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Generate code coverage 17 | run: | 18 | git clone https://github.com/BurtonQin/lockbud.git 19 | cd lockbud 20 | cargo install --path . 21 | cd .. 22 | cargo clean 23 | cargo lockbud -k deadlock -l gupaxx &> >(tee log.out) 24 | if grep -q "WARN" log.out; then 25 | echo "Lockbud warnings found:" 26 | echo "$output" 27 | exit 1 28 | fi 29 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main", "dev" ] 6 | paths-ignore: 7 | - '**/*.md' 8 | pull_request: 9 | branches: [ "main" ] 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | fmt: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | submodules: recursive 22 | - name: Format 23 | run: cargo fmt --all --check 24 | test: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Test 29 | run: cargo test 30 | clippy: 31 | runs-on: ${{ matrix.os }} 32 | strategy: 33 | matrix: 34 | os: [macos-latest, ubuntu-latest] 35 | steps: 36 | - uses: actions/checkout@v4 37 | - name: Clippy (fail on warnings) 38 | run: cargo clippy -- -D warnings 39 | check: 40 | runs-on: ${{ matrix.os }} 41 | strategy: 42 | matrix: 43 | os: [macos-latest, ubuntu-latest] 44 | steps: 45 | - uses: actions/checkout@v4 46 | - name: Check compilation 47 | run: cargo check --verbose 48 | doc: 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v4 52 | - name: Documentation 53 | run: cargo doc --no-deps 54 | -------------------------------------------------------------------------------- /.github/workflows/typo.yml: -------------------------------------------------------------------------------- 1 | # This catches typos. 2 | 3 | name: Typo 4 | 5 | on: 6 | push: 7 | branches: [ "main", "dev" ] 8 | pull_request: 9 | branches: [ "main" ] 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | RUST_BACKTRACE: "full" 14 | 15 | jobs: 16 | typo: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | - name: Spell Check 22 | uses: crate-ci/typos@master 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /ra_target 3 | /feature 4 | .DS_Store 5 | rustc-ice-* 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "origin"] 2 | path = external/egui 3 | url = https://github.com/hinto-janaiyo/egui 4 | [submodule "external/rust-runas"] 5 | path = external/rust-runas 6 | url = https://github.com/mitsuhiko/rust-runas 7 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Gupaxx ARCHITECTURE 2 | 3 | This document explains how the source code is organized. Everything differing from [Gupax](https://github.com/hinto-janai/gupax) is described here. Things that are not changed will not be present. 4 | 5 | ## Structure 6 | | File/Folder | Purpose | 7 | |--------------|---------| 8 | |main.rs| Launch the app. 9 | |inits.rs| Launch the threads if auto, including XvB. 10 | |miscs.rs| Useful functions. 11 | |cli.rs| Command line arguments. 12 | |app| Directory with everything related to displaying the UI. 13 | |app/keys.rs| Handle keys input. 14 | |app/mod.rs| Define App struct, used by egui. 15 | |app/eframe_impl.rs| First entry to the UI. 16 | |app/panels| All the different parts of the UI. 17 | |disk/| Code for writing to disk: `state.toml/node.toml/pool.toml`; This holds the structs for the [State] struct. 18 | |helper| The "helper" thread that runs for the entire duration Gupax is alive. All the processing that needs to be done without blocking the main GUI thread runs here, including everything related to handling P2Pool/XMRig/XvB. 19 | |helper/node.rs| Node thread and principal loop. 20 | |helper/xrig| All related thread XMRig and Xmrig-Proxy code. 21 | |helper/xrig/xmrig.rs| XMRig thread and principal loop. 22 | |helper/xrig/xmrig-proxy.rs| XMRig-Proxy thread and principal loop. 23 | |helper/xvb| All related thread XvB code. 24 | |helper/xvb/mod.rs| XvB thread and principal loop, checks and triggers, gluing every other code of this directory. 25 | |helper/xvb/algorithm.rs| Algorithm logic with calculations and actions. 26 | |helper/xvb/nodes.rs| Manage connection of XvB nodes. 27 | |helper/xvb/rounds.rs| Struct for Rounds with printing and detecting of current round. 28 | |helper/xvb/public\|private_stats| Struct to retrieve public and private stats with request. 29 | |component| Gupaxx related features, like updates and nodes. 30 | 31 | 32 | ## Technical differences of column XMRig in Status Tab process sub-menu with upstream Gupax 33 | 34 | Status of process for XMRig use for some information an image of data when the process started. 35 | The node of xmrig in upstream can not change without a restart of the process. In this fork, the node used by XMRig needs to be updated without restart (using the config HTTP API of XMRig). 36 | So Gupaxx needs to refresh the value of status tab submenu process for XMRig where before the values could not change without a restart of the process. 37 | The field node from ImgXmrig needs to be moved to PubXvbApi. This value must be updated by XMRig at start and by XvB process at runtime. 38 | 39 | ## Updates 40 | 41 | A new option in Gupaxx tab advanced will enable bundled updates. 42 | The binary included of Gupaxx will have default value for bundled updates depending if it is coming from the standalone or the bundled release. 43 | 44 | Updates from Gupaxx will do the following differently from upstream: 45 | - Check if using bundled or standalone with state. Update only Gupaxx binary if the latter or xmrig and p2pool from bundle version if the former. 46 | - Prevent user to run updates twice without restart. 47 | - Ask the user to restart Gupaxx. 48 | - Do not verify if file P2Pool or XMRig exist. (so that the update can create them). 49 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["profile-rustflags", "codegen-backend"] 2 | [package] 3 | name = "gupaxx" 4 | version = "1.10.0" 5 | authors = ["cyrix126 "] 6 | description = "Fork of Gupax integrating the XMRvsBeast Raffle " 7 | documentation = "https://github.com/cyrix126/gupaxx" 8 | edition = "2024" 9 | 10 | [profile.release] 11 | panic = "abort" 12 | lto = "fat" 13 | codegen-units = 1 14 | incremental = false 15 | strip = "symbols" 16 | rustflags = ["-Zlocation-detail=none"] 17 | 18 | [profile.dev] 19 | # can induce issue when using rust-analyzer, but allows smaller dev build time 20 | # codegen-backend = "cranelift" 21 | 22 | [features] 23 | default = [] 24 | bundle = [] 25 | distro = [] 26 | log=[] 27 | 28 | [dependencies] 29 | clap = {version="4.5", features=["cargo", "derive"]} 30 | anyhow = "1.0.98" 31 | benri = "0.1.12" 32 | bytes = "1.10.1" 33 | dirs = "6.0.0" 34 | #-------------------------------------------------------------------------------- 35 | egui = "0.31" 36 | # egui = {git="https://github.com/emilk/egui"} 37 | egui_extras = {version="0.31", features = ["image"] } 38 | # egui_extras = {git="https://github.com/emilk/egui", features = ["image"] } 39 | 40 | ## 2023-12-28: https://github.com/hinto-janai/gupax/issues/68 41 | ## 42 | ## 2024-03-18: Both `glow` and `wgpu` seem to crash: 43 | ## 44 | ## `wgpu` seems to crash on less computers though so... 45 | 46 | ## 2023-02-06: The below gets fixed by using the [wgpu] backend instead of [glow] 47 | ## It also fixes crashes on CPU-based graphics. Only used for Windows. 48 | ## Using [wgpu] actually crashes macOS (fixed in 0.20.x though). 49 | 50 | #-------------------------------------------------------------------------------- 51 | env_logger = "0.11.8" 52 | figment = { version = "0.10.19", features = ["toml"] } 53 | reqwest = {version = "0.12.15", default-features=false, features=["json", "rustls-tls"]} 54 | reqwest-middleware = "0.4" 55 | reqwest-retry = "0.7" 56 | image = { version = "0.25.6", features = ["png"] } 57 | log = "0.4.27" 58 | num-format = { version = "0.4.4", default-features = false } 59 | once_cell = "1.21.3" 60 | rand = "0.9.1" 61 | regex = { version = "1.11.1", default-features = false, features = ["perf"] } 62 | rfd = "0.15.3" 63 | serde = { version = "1.0.219", features = ["rc", "derive"] } 64 | serde_json = "1.0.140" 65 | sysinfo = { version = "0.35", default-features = false, features=["system"] } 66 | # tls-api = "0.9.0" 67 | tokio = { version = "1.45.0", features = ["rt", "time", "macros", "process", "rt-multi-thread"] } 68 | toml = { version = "0.8.22", features = ["preserve_order"] } 69 | walkdir = "2.5.0" 70 | zeroize = "1.8.1" 71 | strsim = "0.11.1" 72 | strip-ansi-escapes = "0.2.1" 73 | derive_more = {version="2.0.1", default-features=false, features=["display", "deref", "deref_mut"]} 74 | serde-this-or-that = "0.5.0" 75 | readable = "0.16" 76 | chrono = {version="0.4.41", default-features=false, features=["clock", "std"]} 77 | enclose = "1.2.1" 78 | bounded-vec-deque = {version="0.1.1", default-features=false} 79 | cfg-if = "1.0" 80 | flexi_logger = "0.30" 81 | eframe = {version="0.31", features=["wgpu"]} 82 | # eframe = {git="https://github.com/emilk/egui", features=["wgpu"]} 83 | strum = {version="0.27", features=["derive"]} 84 | ## force version of ring to prevent https://rustsec.org/advisories/RUSTSEC-2025-0009 85 | ring = "0.17.14" 86 | ## get local ip 87 | local-ip-address = "0.6" 88 | ## get public ip 89 | # public-ip = "0.2" 90 | public-ip = {git="https://github.com/jcgruenhage/rust-public-ip"} 91 | ## check if port open 92 | port_check = "0.2" 93 | # Unix dependencies 94 | [target.'cfg(unix)'.dependencies] 95 | tar = "0.4.44" 96 | flate2 = "1.1" 97 | sudo = "0.6.0" 98 | # https://github.com/emilk/egui/releases/tag/0.30.0 see breaking change 99 | eframe = {version="0.31", features=["x11", "wayland"]} 100 | portable-pty = "0.9.0" 101 | # macOS 102 | [target.'cfg(target_os = "macos")'.dependencies] 103 | # On apple-darwin targets there is an issue with the native and rustls 104 | # tls implementation so this makes it fall back to the openssl variant. 105 | # 106 | # https://gitlab.torproject.org/tpo/core/arti/-/issues/715 107 | # tls-api-openssl = "0.9.0" 108 | # `arti-client` with `static` doesn't actually 109 | # statically link OpenSSL on macOS, both x64 + ARM. 110 | # Should probably file a bug report. 111 | # openssl = { version = "0.10", features = ["vendored"] } 112 | # We don't even use `xz` in `flate2` but this gets dynamically 113 | # linked as well which causes problems, so statically link it. 114 | lzma-sys = { version = "0.1", features = ["static"] } 115 | [dev-dependencies] 116 | egui = {version="0.31", features=["callstack"]} 117 | # egui = {git="https://github.com/emilk/egui", features=["callstack"]} 118 | 119 | # [target.'cfg(not(target_os = "macos"))'.dependencies] 120 | # tls-api-native-tls = "0.9.0" 121 | 122 | # Windows dependencies 123 | [target.'cfg(windows)'.dependencies] 124 | # glow start on windows but not wgpu 125 | # need the same version that eframe is using with egui_wgpu 126 | # feature angle to enable support for old cpu on Windows 127 | wgpu = {version = "24.0.3", features=["angle"]} 128 | zip = "2.6.1" 129 | is_elevated = "0.1.2" 130 | ## portable-pty upgrade on 0.9 will break terminals on windows 131 | portable-pty = "0.8.1" 132 | 133 | # For Windows build (icon) 134 | [target.'cfg(windows)'.build-dependencies] 135 | winres = "0.1.12" 136 | static_vcruntime = "2.0" 137 | 138 | # For macOS build (cargo-bundle) 139 | [package.metadata.bundle] 140 | name = "Gupaxx" 141 | identifier = "com.github.cyrix126.gupaxx" 142 | icon = ["images/icons/icon@2x.png"] 143 | category = "public.app-category.utilities" 144 | -------------------------------------------------------------------------------- /DIFFERENCES.md: -------------------------------------------------------------------------------- 1 | # Differences with upstream [Gupax](https://github.com/hinto-janai/gupax) 2 | 3 | This document reference the biggest changes from Gupax. It will not go into every details that you can see in the different releases changelog. 4 | 5 | 6 | ## Added functionalities 7 | ### Integration of the XvB Raffle 8 | A new fancy tab to apply an algorithm of distribution of HR to XMRig (see [NOTES_ALGORITHM](NOTES_ALGORITHMS)) with your token from XvB. 9 | This tab also includes a console output that lets you track if everything is working and see what the decision are of the algorithm, and show you your personal stats from XvB. 10 | 11 | A new column in Status Tab to see public stats from the raffle. 12 | ### XMRig-Proxy 13 | You can now point all your external miners and get all the juicy stats in Gupaxx. XvB algorithm is able to control XMRig-Proxy when it is enabled. 14 | 15 | ### Monerod 16 | A new tab for a monero node integration. It allows you to start monerod from gupaxx and benefit from an easy setup with p2pool. 17 | 18 | ## Removed functionality 19 | Updates by tor. The version of the crate used was outdated, plagued with security concerns and bloated the binary. 20 | It was only for updates. 21 | If you want Gupaxx to update by tor, you can torify it when launching. 22 | 23 | ## Technical Debt 24 | All dependencies are upgraded to last possible version, even when there is a breaking change (code of Gupaxx is modified for that). 25 | 26 | ## Bugfixes (visuals and performances) 27 | The rendering of Tabs has been modified so that the minimum stated size of the window is capable to show everything. In Upstream the middle panels often overlap on the bottom. 28 | 29 | The rendering of the benchmark table and of console outputs were calculating every line at the same time. Now it only renders what you see. It is a significant improvement for your processor, and you can feel the difference if it is not very powerful. 30 | 31 | Updates from Gupaxx does not retrieve XMRig and P2Pool from upstream anymore, but use versions in the bundled version. This modification prevent bad surprise (see [#3](https://github.com/Cyrix126/gupaxx/issues/3)). 32 | 33 | It also allows advanced users to use your their own version of P2Pool and XMRig. The standalone version of Gupaxx will not replace them. 34 | 35 | pings of remote p2pool nodes are much faster. 36 | 37 | ## UI 38 | 39 | ### Text size 40 | 41 | The fonts size has been rethinked to enable you to use Gupaxx on different size of screen. Before, the size of text was tied to the size of the window. You could not show more content by making the window bigger. Now the size of the text remains the same, except if you change it in the Gupaxx tab. 42 | 43 | ### Hidden tab and columns 44 | 45 | Tabs and column from the Status tab can be hidden, to let you see only what you use in Gupaxx. 46 | 47 | ## Security 48 | Gupaxx is updated frequently and there is a CI action to check for known vulnerabilities (cargo audit). 49 | -------------------------------------------------------------------------------- /NOTES_ALGORITHM.md: -------------------------------------------------------------------------------- 1 | 2 | **Draft** 3 | 4 | # Algorithm of distribution of Hashrate between P2pool and XvB 5 | ## Objective 6 | If the Hashrate (HR) is not enough to probably always have at least one share in the window PPLNS (WP), the HR will never be redirected to XvB node but always stay on P2Pool node. 7 | 8 | If no share is acquired, all HR will stay on P2Pool node until there is one. 9 | 10 | If HR is enough to probably always have at least one share in the (WP), the spare HR will be: 11 | **Default mode**: in part given to XvB node to be in the most possible round type and keep in P2Pool the rest of HR that will not impact the type of round (sHR for spared HR). 12 | **Hero mode**: entirely given to the XvB node regardless of sHR. 13 | 14 | ## How 15 | PPLNS window size (PWS): API P2pool pplnsWindowSize [^1] 16 | p2pool difficulty(PD): API P2pool sidechainDifficulty [^1] 17 | mHR: minimum required HR to stay in WP = PD / (PWS*10) [^2] 18 | 19 | Because it is probabilities, a buffer will be put in place for the required HR for P2Pool and mHR for round type to allow some margin of errors. 20 | The mHR needs to be refreshed periodically because it can change with the difficulty changing. (PWS should not change). 21 | 22 | Calculation is made in % of time that will go to P2Pool and to XvB, depending if your mining on mini or main side chain. 23 | Every ten minutes, the algorithm will decide how next 10 minutes will be distributed depending on default or hero mode. 24 | 25 | ## Manage with outside HashRate 26 | If miners outside the Gupaxx instance are mining on P2Pool for the same address, Gupaxx will send too much on P2Pool because it doesn't take extra HR outside his control into account. 27 | To solve this issue, it will look at the sidechain estimated HR (eHR) from status command of P2Pool. It is an estimation based on passed discovered shares, their difficulty, the number of blocks between them etc... 28 | 29 | This eHR will be retrieved at the same interval as the algorithm. 30 | This estimated external HR(eHR) minus the local HR sent to P2Pool will be removed from the mHR. 31 | 32 | If miners outside the Gupaxx instance are mining on XvB for the same address, Gupaxx will maybe send too much (more than enough for the round) or too less (could have been in better round) on XvB. 33 | To solve this second issue, it will remove from the required HR to get to rounds the average HR sent to XvB (retrieved by XvB API) minus what he is sending of its own. 34 | 35 | ## Examples 36 | PWS = 2160 37 | PD = 85.5M 38 | ### Example 1: the poor 39 | Miner has 2kH/s on Gupaxx. 40 | HR never goes on XvB, because the minimum required to have a share in WP is 4kH/s based on PD and PWS values. 41 | ### Example 2: the modest 42 | Miner has 10kH/s on Gupaxx 43 | for ten minutes, 4 are required to be put on P2Pool. 44 | 45 | **Default mode**: 9 minutes are given to P2Pool and one for XvB. 46 | Because after giving 4mn to P2Pool to meet mHR, he still have ~5kH/s to spare. 47 | The first round type (Donor round) need 1kH/s and second round type (VIP Donor) need 10kH/s. 48 | 5kH/s is enough for the Donor Round but not enough for the VIP Donor. 49 | So 1kH/s is given to XvB node so that the miner participate in the Donor round. 50 | 51 | **Hero mode**: 4 minutes are given to P2Pool and 6 for XvB. 52 | ### Example 3: the hardworker 53 | Miner has 5kH/s on Gupaxx 54 | He also have one remote miner that mines 2kH/s on P2Pool and another one 8kH/s on XvB 55 | 56 | **Default mode**: 6 minutes are given to P2Pool and 4 for XvB. 57 | Because to have at still one share per window, gupaxx need to complete the already existent HR on P2Pool with 4 minutes (2kH/s). But after that, it still have 6 minutes spared (3kH/s) when only 4 minutes are needed to gain the better round of vip donor (10kH/s). So it will send only the necessary 4 minutes to XvB and give the 2 minutes not needed to P2Pool. 58 | 59 | **Hero mode**: 4 minutes are given to P2Pool and 6 for XvB. 60 | 61 | ## Technical Implementation 62 | ### Knowing if a share is in PW 63 | P2Pool node (PN) local API show only found shares and not current. 64 | Gupaxx process will check frequently the output of the P2Pool command "status" without impacting the console output of the P2Pool tab to get the current shares. 65 | ### know oHR on P2Pool 66 | Gupaxx watch for sidechain HR for his address on the output of status command of P2Pool in a way that do not disturb the output of console in the UI. 67 | ### know oHR on XvB 68 | Gupaxx receive this data from XvB API. 69 | ### Knowing the HR generated by Gupaxx 70 | Gupaxx will simply watch the values retrieved by XMRig process. 71 | ### Switching HR from P2Pool node to XvB node 72 | The mHR is calculated depending on the sidechain the P2Pool is mining on. 73 | The XvB process will check every ten minutes the last 15 minutes average HR and decide when to switch (in seconds) for the ten next minutes. (first P2Pool then XvB). 74 | *Need to see the time for XMRig takes to set the new settings by API.* 75 | When the time to switch arrives, XvB process will send a request to XMRig to change the node used. 76 | ### Modification of config of XMRig 77 | The following 4 attributes must be applied to XMRig config when mining to XvB node. 78 | 79 | ```ignore 80 | "url": "xvb node:4247" 81 | "user": "user id", 82 | "keepalive": true, 83 | "tls": true, 84 | ``` 85 | Or to return back to p2pool 86 | 87 | ```ignore 88 | "url": "127.0.0.1:3333" 89 | "user": "Gupax_v1_3_5", 90 | "keepalive": false, 91 | "tls": false, 92 | ``` 93 | 94 | The HTTP API of XMRig requires to give a full config. 95 | The current config will be requested, modified and sent back. 96 | 97 | [^1]: https://p2pool.io/mini/api/pool/stats 98 | [^2]: https://github.com/SChernykh/p2pool?tab=readme-ov-file#how-payouts-work-in-p2pool 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Gupaxx logo](assets/images/banner.png) 2 | 3 | ## Development Status 4 | This fork has a stable release. 5 | It is intended for end users and offers a friendly and easy user experience. 6 | 7 | ## Gupaxx 8 | `Gupaxx` is a fork of [**Gupax**](https://github.com/hinto-janai/gupax) integrating the [XMRvsBeast Raffle](https://xmrvsbeast.com), it is also a maintained software. Designed to simplify mining on [P2Pool](https://www.getmonero.org/2021/10/05/p2pool-released.html) while optionally participating (but you will want to 😉) in the XMRvsBeast raffle. 9 | 10 | ## System requirements 11 | `Gupaxx` may not run on machines with: 12 | - A deprecated OS (Windows 7, Ubuntu 18.04, etc) 13 | - CPU whithout support for OpenGL 3.1 (<2010) 14 | 15 | [![CI](https://github.com/cyrix126/gupaxx/actions/workflows/ci.yml/badge.svg)](https://github.com/cyrix126/gupaxx/actions/workflows/ci.yml) 16 | 17 | ## Contents 18 | * [What is Gupaxx/XMRvsBeast?](#what-is-gupaxxxmrvsbeast) 19 | * [Guide](#guide) 20 | * [XvB Tab](#xvb-tab) 21 | - [Console](#console-of-xvb) 22 | - [Token Input](#token-input) 23 | - [Account stats](#account-stats) 24 | * [XvB Raffle](#xvb-raffle-status) 25 | * [Other changes](#other-changes) 26 | * [License](#license) 27 | 28 | ## What is Gupaxx/XMRvsBeast? 29 | [**`Gupaxx`**](https://getmonero.org) is a fork of [*Gupax*](https://github.com/hinto-janai/gupax) that integrates the [XMRvsBeast raffle](https://xmrvsbeast.com). 30 | 31 | With this fork, you can easily split your hashrate between P2Pool and XMRvsBeast, increasing your chances of winning in the raffle while also supporting the Monero network via decentralizing the mining using using p2pool. 32 | 33 | For a detailed explanation of Gupax, see the [README](https://github.com/hinto-janai/gupax) of upstream. 34 | 35 | 36 | ## Guide 37 | 1. [Download the bundled version of Gupaxx](https://github.com/Cyrix126/gupaxx/releases) 38 | 2. Extract 39 | 3. Launch Gupaxx 40 | 41 | Next steps can be seen in this video tutorial: 42 | 43 | https://github.com/Cyrix126/gupaxx/assets/58007246/610cbfea-fd97-4150-95ed-9c8a7ef5ba94 44 | 45 | 46 | 47 | 4. Input your Monero address in the `P2Pool` tab 48 | 5. Register the same address on [XMRvsBeast](https://xmrvsbeast.com) 49 | 6. Input the token received in the `XvB` Tab 50 | 6. Start `P2Pool` 51 | 7. Start `XMRig` 52 | 8. Start `XvB` 53 | 54 | Gupaxx will distribute your hashrate between P2Pool and XMRvsBeast as defined by [this algorithm](NOTES_ALGORITHM.md). 55 | 56 | The algorithm will decide which quantity of HR that will be directed to P2Pool and to XMRvsBeast, so that you still keep a share in the [PPLNS Window](https://github.com/SChernykh/p2pool#how-payouts-work-in-p2pool). 57 | It will by default send just enough to get to the highest round or, if hero mode is enabled, everything minus the minimum required to still have a share in the PPLNS Window. 58 | 59 | 60 | ## XvB Tab 61 | ![CI](assets/images/xvb_tab.png) 62 | ### Console of XvB 63 | The output of the console will show useful information on the status of the XvB process and the decision of the algorithm for every 10 minutes. 64 | ### Token input 65 | When you registered your XMR payout address, you should have received a token. Please enter this token here. 66 | ### Account stats 67 | Account stats about your address on XMRvsBeast can be found here after the process is started with your token provided. 68 | 69 | 70 | ## XvB Raffle Status 71 | Gupaxx adds a new column called **XvB Raffle** on the Status Tab in the Process submenu. It displays public statistics of XMRvsBeast, which are available [here](https://xmrvsbeast.com/p2pool). 72 | It is refreshed every minute. 73 | This column will be active if the XvB process is started even partially, it doesn't need the token to be provided. 74 | 75 | ![XvB raffle stats](assets/images/xvb_raffle_stats.png) 76 | 77 | 78 | ## Other changes 79 | This fork brings upgrades of dependence and some bugfixes about visual, performance and security that you can find in [DIFFERENCES](DIFFERENCES.md). 80 | ~~I will eventually (meaning when I'll have time) create pull requests for upstream about these differences.~~ 81 | **Edit**: 82 | There is currently no plan to upstream the changes as the owner of Gupax said he won't have time to review the PR. 83 | 84 | 85 | ## Troubleshooting 86 | If you have any issue, feel free to ask for support in the [xmrvsbeast matrix room](#xmrvsbeast:monero.social) [![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#xmrvsbeast:monero.social) or you can also just [open an issue](https://github.com/Cyrix126/gupaxx/issues/new/choose) in this repo. You can also contact me through [email](mailto:gupaxx@baermail.fr). 87 | ### Windows 88 | You must add an exception to your antivirus for the directory where Gupaxx is executed. Follow the step for Windows Only, that starts at 30 seconds in this [video](https://user-images.githubusercontent.com/101352116/207978455-6ffdc0cc-204c-4594-9a2f-e10c505745bc.mp4). 89 | ### Mac OSX 90 | You must remove Gupaxx app from quarantine with following command: 91 | *If you have put Gupaxx.app in your Applications* 92 | `xattr -d com.apple.quarantine /Applications/Gupaxx.app` 93 | See this [issue](https://github.com/hinto-janai/gupax/issues/51). 94 | 95 | 96 | ## License 97 | 98 | ![GPL v3](assets/images/gplv3-with-text-136x68.png) 99 | 100 | [Gupaxx](https://github.com/cyrix126/gupax/blob/master/LICENSE), [P2Pool](https://github.com/SChernykh/p2pool/blob/master/LICENSE), [XMRig](https://github.com/xmrig/xmrig/blob/master/LICENSE) and [XMRig-Proxy](https://github.com/xmrig/xmrig-proxy/blob/master/LICENSE) are licensed under the GNU General Public License v3.0. 101 | 102 | [Monerod](https://github.com/monero-project/monero) [licence](https://github.com/monero-project/monero?tab=License-1-ov-file) 103 | 104 | [See the licenses of various dependencies.](https://github.com/Cyrix126/gupaxx/blob/master/Cargo.toml) 105 | 106 | ## Mirror 107 | In case Github repository is down, you can still find the source code at [librejo](https://librejo.monerodevs.org/Ecosystem/gupaxx) 108 | 109 | ## Donations 110 | If you'd like to thank me for the development of Gupaxx and/or motivate me to improve it you're welcome to send any amount of XMR to the following address: 111 | 112 | ![QR CODE DONATION ADDRESS](assets/donation_qr.png) 113 | ``` 114 | 4AGJScWSv45E28pmwck9YRP21KuwGx6fuMYV9kTxXFnWEij5FVEUyccBs7ExDy419DJXRPw3u57TH5BaGbsHTdnf6SvY5p5 115 | ``` 116 | 117 | Every donations will be converted to hours of work ! 118 | 119 | ### Donation transparency 120 | 121 | A Kuno page exist so you can easly keep track of the amount funded in this project. 122 | [Gupaxx Kuno](https://kuno.anne.media/fundraiser/dsrr/) 123 | In case you don't want to rely on the kuno website, the secret view key is: 124 | 125 | ``` 126 | 6c6f841e1eda3fba95f2261baa4614e3ec614af2a97176bbae2c0be5281d1d0f 127 | ``` 128 | -------------------------------------------------------------------------------- /assets/donation_qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/donation_qr.png -------------------------------------------------------------------------------- /assets/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/banner.png -------------------------------------------------------------------------------- /assets/images/benchmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/benchmarks.png -------------------------------------------------------------------------------- /assets/images/ferris/cute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/ferris/cute.png -------------------------------------------------------------------------------- /assets/images/ferris/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/ferris/error.png -------------------------------------------------------------------------------- /assets/images/ferris/gesture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/ferris/gesture.png -------------------------------------------------------------------------------- /assets/images/ferris/happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/ferris/happy.png -------------------------------------------------------------------------------- /assets/images/ferris/oops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/ferris/oops.png -------------------------------------------------------------------------------- /assets/images/ferris/panic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/ferris/panic.png -------------------------------------------------------------------------------- /assets/images/ferris/sudo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/ferris/sudo.png -------------------------------------------------------------------------------- /assets/images/gplv3-with-text-136x68.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/gplv3-with-text-136x68.png -------------------------------------------------------------------------------- /assets/images/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/icons/icon.ico -------------------------------------------------------------------------------- /assets/images/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/icons/icon.png -------------------------------------------------------------------------------- /assets/images/icons/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/icons/icon@2x.png -------------------------------------------------------------------------------- /assets/images/local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/local.png -------------------------------------------------------------------------------- /assets/images/local_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/local_node.png -------------------------------------------------------------------------------- /assets/images/payouts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/payouts.png -------------------------------------------------------------------------------- /assets/images/processes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/processes.png -------------------------------------------------------------------------------- /assets/images/remote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/remote.png -------------------------------------------------------------------------------- /assets/images/thread_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/thread_model.png -------------------------------------------------------------------------------- /assets/images/xmrig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/xmrig.png -------------------------------------------------------------------------------- /assets/images/xvb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/xvb.png -------------------------------------------------------------------------------- /assets/images/xvb_raffle_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/xvb_raffle_stats.png -------------------------------------------------------------------------------- /assets/images/xvb_tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/assets/images/xvb_tab.png -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // This [build.rs] is for setting Windows icons. 2 | // The icon in [File Explorer] gets set here. 3 | // The icon in the taskbar and top of the App window gets 4 | // set in [src/main.rs, src/constants.rs] at runtime with 5 | // pre-compiled bytes using [include_bytes!()] on the images in [images/]. 6 | #[cfg(windows)] 7 | fn main() -> std::io::Result<()> { 8 | set_commit_env(); 9 | 10 | static_vcruntime::metabuild(); 11 | let mut res = winres::WindowsResource::new(); 12 | // This sets the icon. 13 | res.set_icon("assets/images/icons/icon.ico"); 14 | // This sets the [Run as Administrator] metadata flag for Windows. 15 | // Why do I do this?: [https://github.com/hinto-janai/gupax/tree/main/src#why-does-gupax-need-to-be-admin-on-windows] 16 | // TL;DR: Because Windows. 17 | res.set_manifest( 18 | r#" 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | "#, 29 | ); 30 | res.compile() 31 | } 32 | 33 | #[cfg(unix)] 34 | fn main() { 35 | set_commit_env(); 36 | } 37 | 38 | // Set the current git commit to the env var [COMMIT]. 39 | fn set_commit_env() { 40 | println!("cargo:rerun-if-changed=.git/refs/heads/"); 41 | 42 | let output = std::process::Command::new("git") 43 | .args(["rev-parse", "HEAD"]) 44 | .output() 45 | .unwrap(); 46 | 47 | let commit = String::from_utf8(output.stdout).unwrap(); 48 | 49 | assert!(commit.len() >= 40); 50 | 51 | println!("cargo:rustc-env=COMMIT={commit}"); 52 | } 53 | -------------------------------------------------------------------------------- /nano_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nano", 3 | "password": "", 4 | "block_time": 30, 5 | "min_diff": 100000, 6 | "pplns_window": 2160, 7 | "uncle_penalty": 10 8 | } 9 | -------------------------------------------------------------------------------- /p2pool_nano_peers.txt: -------------------------------------------------------------------------------- 1 | 86.107.101.72:37890 2 | 96.28.236.202:37890 3 | 146.185.21.170:37889 4 | 121.98.149.60:37889 5 | 84.23.142.12:37888 6 | 68.235.46.9:37890 7 | 85.138.19.220:37890 8 | 156.146.51.132:37890 9 | 66.152.106.102:37890 10 | 86.26.216.181:37890 11 | 212.14.126.232:37890 12 | 186.219.142.251:37890 13 | 185.182.198.88:37890 14 | 217.147.185.190:37890 15 | 188.27.87.88:37890 16 | 191.84.253.28:37890 17 | 94.25.229.117:37890 18 | 118.93.193.202:37890 19 | 185.168.196.39:37890 20 | 89.115.180.239:37890 21 | 37.19.200.139:37890 22 | 78.128.62.99:37890 23 | 114.198.40.142:37890 24 | 24.231.191.11:37890 25 | 185.156.153.147:37890 26 | 185.193.64.100:37890 27 | 151.237.3.204:37890 28 | 100.34.9.122:37890 29 | 122.150.228.151:37890 30 | 91.156.176.144:37890 31 | 73.222.186.210:37890 32 | 136.36.39.250:37890 33 | 177.104.100.188:37889 34 | 104.8.116.97:37889 35 | 95.91.252.156:37890 36 | 121.99.244.62:37890 37 | 65.21.161.163:37890 38 | 143.105.53.106:37890 39 | 172.12.68.81:37890 40 | 83.21.90.3:37890 41 | 203.96.177.100:37890 42 | 216.147.127.197:37889 43 | 154.9.250.64:996 44 | 185.122.144.171:37890 45 | 185.180.13.99:37890 46 | 45.83.220.209:37890 47 | 185.180.13.102:37890 48 | 109.183.171.140:37890 49 | 88.168.53.24:37890 50 | 122.150.228.218:37890 51 | 185.213.154.206:37890 52 | 122.150.228.167:37890 53 | 185.65.135.247:37890 54 | 185.180.13.101:37890 55 | -------------------------------------------------------------------------------- /pgp/cyrix126.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mDMEZf1yBxYJKwYBBAHaRw8BAQdAtqhavCUv++al9LBLJHB6j3YOwP8iIsewhfmK 4 | 9tK53zG0HUN5cml4MTI2IDxndXBheHhAYmFlcm1haWwuZnI+iJMEExYKADsWIQSO 5 | /+SowP1LbSHDqrLsblu0AcY2LQUCZf1yBwIbAwULCQgHAgIiAgYVCgkICwIEFgID 6 | AQIeBwIXgAAKCRDsblu0AcY2Lf9FAP9JEa2EnhCDghkdMRDMlGuLuwqYCwTKYbvm 7 | CKHmILpkEQEA6QHXovAhcO+YGtsG03ulqVXpEnjHGbaWG+N5XNGX0Q24OARl/XIH 8 | EgorBgEEAZdVAQUBAQdA7s9vxDjRjIw5se74p/ccIEctizgORjN9Ny12uCwPAyAD 9 | AQgHiHgEGBYKACAWIQSO/+SowP1LbSHDqrLsblu0AcY2LQUCZf1yBwIbDAAKCRDs 10 | blu0AcY2LZbeAQDtVJ48IHWjBFuEv1h4eO07yDFHqIdk8JXQH4jJ6mU7hwEA/vS3 11 | X4+W54CB7XRVu2mIUVszq7rE+rXPY7oe3ECUmQc= 12 | =doUZ 13 | -----END PGP PUBLIC KEY BLOCK----- 14 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-02-01" 3 | components = [ "rustfmt", "rustc-dev", "cargo", "clippy", "rust-analyzer", "rust-src", "llvm-tools-preview", "rustc-codegen-cranelift-preview"] 4 | target = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-apple-darwin","x86_64-pc-windows-gnu"] 5 | -------------------------------------------------------------------------------- /src/app/eframe_impl.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use super::App; 4 | #[cfg(target_os = "windows")] 5 | use crate::errors::{ErrorButtons, ErrorFerris, process_running}; 6 | use crate::helper::{Helper, ProcessName, ProcessState}; 7 | use crate::inits::init_text_styles; 8 | use crate::{NODE_MIDDLE, P2POOL_MIDDLE, SECOND, XMRIG_MIDDLE, XMRIG_PROXY_MIDDLE, XVB_MIDDLE}; 9 | use derive_more::derive::{Deref, DerefMut}; 10 | use log::debug; 11 | 12 | impl eframe::App for App { 13 | fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { 14 | // *-------* 15 | // | DEBUG | 16 | // *-------* 17 | debug!("App | ----------- Start of [update()] -----------"); 18 | // If closing 19 | self.quit(ctx); 20 | // Handle Keys 21 | let (key, wants_input) = self.keys_handle(ctx); 22 | 23 | // Refresh AT LEAST once a second 24 | debug!("App | Refreshing frame once per second"); 25 | ctx.request_repaint_after(SECOND); 26 | 27 | // Get P2Pool/XMRig process state. 28 | // These values are checked multiple times so 29 | // might as well check only once here to save 30 | // on a bunch of [.lock().unwrap()]s. 31 | let mut process_states = ProcessStatesGui::new(self); 32 | // resize window and fonts if button "set" has been clicked in Gupaxx tab 33 | if self.must_resize { 34 | init_text_styles(ctx, self.state.gupax.selected_scale); 35 | self.must_resize = false; 36 | } 37 | // check for windows that a local instance of xmrig is not running outside of Gupaxx. Important because it could lead to crashes on this platform. 38 | // Warn only once per restart of Gupaxx. 39 | #[cfg(target_os = "windows")] 40 | if !self.xmrig_outside_warning_acknowledge 41 | && process_running(ProcessName::Xmrig) 42 | && !process_states.find(ProcessName::Xmrig).alive 43 | { 44 | self.error_state.set("An instance of xmrig is running outside of Gupaxx.\nThis is not supported and could lead to crashes on this platform.\nPlease stop your local instance and start xmrig from Gupaxx Xmrig tab.", ErrorFerris::Error, ErrorButtons::Okay); 45 | self.xmrig_outside_warning_acknowledge = true; 46 | } 47 | // If there's an error, display [ErrorState] on the whole screen until user responds 48 | debug!("App | Checking if there is an error in [ErrorState]"); 49 | if self.error_state.error { 50 | self.quit_error_panel(ctx, &process_states, &key); 51 | return; 52 | } 53 | // Compare [og == state] & [node_vec/pool_vec] and enable diff if found. 54 | // The struct fields are compared directly because [Version] 55 | // contains Arc's that cannot be compared easily. 56 | // They don't need to be compared anyway. 57 | debug!("App | Checking diff between [og] & [state]"); 58 | let og = self.og.lock().unwrap(); 59 | self.diff = og.status != self.state.status 60 | || og.gupax != self.state.gupax 61 | || og.node != self.state.node 62 | || og.p2pool != self.state.p2pool 63 | || og.xmrig != self.state.xmrig 64 | || og.xmrig_proxy != self.state.xmrig_proxy 65 | || og.xvb != self.state.xvb 66 | || self.og_node_vec != self.node_vec 67 | || self.og_pool_vec != self.pool_vec; 68 | drop(og); 69 | 70 | self.top_panel(ctx); 71 | self.bottom_panel(ctx, &key, wants_input, &process_states); 72 | // xvb_is_alive is not the same for bottom and for middle. 73 | // for status we don't want to enable the column when it is retrying requests. 74 | // but also we don't want the user to be able to start it in this case. 75 | let p_xvb = process_states.find_mut(ProcessName::Xvb); 76 | p_xvb.alive = p_xvb.state != ProcessState::Dead; 77 | self.middle_panel(ctx, frame, key, &process_states); 78 | } 79 | } 80 | #[derive(Debug)] 81 | pub struct ProcessStateGui { 82 | pub name: ProcessName, 83 | pub state: ProcessState, 84 | pub alive: bool, 85 | pub waiting: bool, 86 | } 87 | 88 | impl ProcessStateGui { 89 | pub fn run_middle_msg(&self) -> &str { 90 | match self.name { 91 | ProcessName::Node => NODE_MIDDLE, 92 | ProcessName::P2pool => P2POOL_MIDDLE, 93 | ProcessName::Xmrig => XMRIG_MIDDLE, 94 | ProcessName::XmrigProxy => XMRIG_PROXY_MIDDLE, 95 | ProcessName::Xvb => XVB_MIDDLE, 96 | } 97 | } 98 | pub fn stop(&self, helper: &Arc>) { 99 | match self.name { 100 | ProcessName::Node => Helper::stop_node(helper), 101 | ProcessName::P2pool => Helper::stop_p2pool(helper), 102 | ProcessName::Xmrig => Helper::stop_xmrig(helper), 103 | ProcessName::XmrigProxy => Helper::stop_xp(helper), 104 | ProcessName::Xvb => Helper::stop_xvb(helper), 105 | } 106 | } 107 | } 108 | 109 | #[derive(Deref, DerefMut, Debug)] 110 | pub struct ProcessStatesGui(Vec); 111 | 112 | impl ProcessStatesGui { 113 | // order is important for lock 114 | pub fn new(app: &App) -> Self { 115 | let mut process_states = ProcessStatesGui(vec![]); 116 | for process in [ 117 | &app.node, 118 | &app.p2pool, 119 | &app.xmrig, 120 | &app.xmrig_proxy, 121 | &app.xvb, 122 | ] { 123 | let lock = process.lock().unwrap(); 124 | process_states.push(ProcessStateGui { 125 | name: lock.name, 126 | alive: lock.is_alive(), 127 | waiting: lock.is_waiting(), 128 | state: lock.state, 129 | }); 130 | } 131 | process_states 132 | } 133 | pub fn is_alive(&self, name: ProcessName) -> bool { 134 | self.iter() 135 | .find(|p| p.name == name) 136 | .unwrap_or_else(|| panic!("This vec should always contains all Processes {:?}", self)) 137 | .alive 138 | } 139 | pub fn find(&self, name: ProcessName) -> &ProcessStateGui { 140 | self.iter() 141 | .find(|p| p.name == name) 142 | .unwrap_or_else(|| panic!("This vec should always contains all Processes {:?}", self)) 143 | } 144 | pub fn find_mut(&mut self, name: ProcessName) -> &mut ProcessStateGui { 145 | self.iter_mut() 146 | .find(|p| p.name == name) 147 | .expect("This vec should always contains all Processes") 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/app/keys.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use egui::{Key, Modifiers}; 19 | use log::info; 20 | 21 | use crate::{disk::status::Submenu, utils::macros::flip}; 22 | 23 | use super::{App, Tab}; 24 | 25 | //---------------------------------------------------------------------------------------------------- [Pressed] enum 26 | // These represent the keys pressed during the frame. 27 | // I could use egui's [Key] but there is no option for 28 | // a [None] and wrapping [key_pressed] like [Option] 29 | // meant that I had to destructure like this: 30 | // if let Some(egui::Key)) = key_pressed { /* do thing */ } 31 | // 32 | // That's ugly, so these are used instead so a simple compare can be used. 33 | #[derive(Debug, Clone, Eq, PartialEq)] 34 | pub enum KeyPressed { 35 | F11, 36 | Up, 37 | Down, 38 | Esc, 39 | Z, 40 | X, 41 | C, 42 | V, 43 | S, 44 | R, 45 | D, 46 | None, 47 | } 48 | 49 | impl KeyPressed { 50 | #[inline] 51 | pub(super) fn is_f11(&self) -> bool { 52 | *self == Self::F11 53 | } 54 | #[inline] 55 | pub(super) fn is_z(&self) -> bool { 56 | *self == Self::Z 57 | } 58 | #[inline] 59 | pub(super) fn is_x(&self) -> bool { 60 | *self == Self::X 61 | } 62 | #[inline] 63 | pub(super) fn is_up(&self) -> bool { 64 | *self == Self::Up 65 | } 66 | #[inline] 67 | pub(super) fn is_down(&self) -> bool { 68 | *self == Self::Down 69 | } 70 | #[inline] 71 | pub(super) fn is_esc(&self) -> bool { 72 | *self == Self::Esc 73 | } 74 | #[inline] 75 | pub(super) fn is_s(&self) -> bool { 76 | *self == Self::S 77 | } 78 | #[inline] 79 | pub(super) fn is_r(&self) -> bool { 80 | *self == Self::R 81 | } 82 | #[inline] 83 | pub(super) fn is_d(&self) -> bool { 84 | *self == Self::D 85 | } 86 | #[inline] 87 | pub(super) fn is_c(&self) -> bool { 88 | *self == Self::C 89 | } 90 | #[inline] 91 | pub(super) fn is_v(&self) -> bool { 92 | *self == Self::V 93 | } 94 | // #[inline] 95 | // pub(super) fn is_none(&self) -> bool { 96 | // *self == Self::None 97 | // } 98 | } 99 | 100 | impl App { 101 | pub fn keys_handle(&mut self, ctx: &egui::Context) -> (KeyPressed, bool) { 102 | // If [F11] was pressed, reverse [fullscreen] bool 103 | let key: KeyPressed = ctx.input_mut(|input| { 104 | if input.consume_key(Modifiers::NONE, Key::F11) { 105 | KeyPressed::F11 106 | } else if input.consume_key(Modifiers::NONE, Key::Z) { 107 | KeyPressed::Z 108 | } else if input.consume_key(Modifiers::NONE, Key::X) { 109 | KeyPressed::X 110 | } else if input.consume_key(Modifiers::NONE, Key::C) { 111 | KeyPressed::C 112 | } else if input.consume_key(Modifiers::NONE, Key::V) { 113 | KeyPressed::V 114 | } else if input.consume_key(Modifiers::NONE, Key::ArrowUp) { 115 | KeyPressed::Up 116 | } else if input.consume_key(Modifiers::NONE, Key::ArrowDown) { 117 | KeyPressed::Down 118 | } else if input.consume_key(Modifiers::NONE, Key::Escape) { 119 | KeyPressed::Esc 120 | } else if input.consume_key(Modifiers::NONE, Key::S) { 121 | KeyPressed::S 122 | } else if input.consume_key(Modifiers::NONE, Key::R) { 123 | KeyPressed::R 124 | } else if input.consume_key(Modifiers::NONE, Key::D) { 125 | KeyPressed::D 126 | } else { 127 | KeyPressed::None 128 | } 129 | }); 130 | // Check if egui wants keyboard input. 131 | // This prevents keyboard shortcuts from clobbering TextEdits. 132 | // (Typing S in text would always [Save] instead) 133 | let wants_input = ctx.wants_keyboard_input(); 134 | 135 | if key.is_f11() { 136 | if ctx.input(|i| i.viewport().maximized == Some(true)) { 137 | info!("fullscreen bool"); 138 | ctx.send_viewport_cmd(egui::ViewportCommand::Fullscreen(true)); 139 | } 140 | // Change Tabs LEFT 141 | } else if key.is_z() && !wants_input { 142 | let tabs = Tab::from_show_processes(&self.state.gupax.show_processes); 143 | let index = tabs 144 | .iter() 145 | .position(|t| *t == self.tab) 146 | .expect("can't be on a hidden tab"); 147 | self.tab = if (index as i32 - 1) < 0 { 148 | tabs.last() 149 | .expect("there is always 3 tabs that can not be hidden") 150 | .to_owned() 151 | } else { 152 | tabs[index - 1] 153 | }; 154 | // Change Tabs RIGHT 155 | } else if key.is_x() && !wants_input { 156 | let tabs = Tab::from_show_processes(&self.state.gupax.show_processes); 157 | let index = tabs 158 | .iter() 159 | .position(|t| *t == self.tab) 160 | .expect("can't be on a hidden tab"); 161 | self.tab = if (index + 1) == tabs.len() { 162 | tabs[0] 163 | } else { 164 | tabs[index + 1] 165 | }; 166 | // Change Submenu LEFT 167 | } else if key.is_c() && !wants_input { 168 | match self.tab { 169 | Tab::Status => match self.state.status.submenu { 170 | Submenu::Processes => self.state.status.submenu = Submenu::Benchmarks, 171 | Submenu::P2pool => self.state.status.submenu = Submenu::Processes, 172 | Submenu::Benchmarks => self.state.status.submenu = Submenu::P2pool, 173 | }, 174 | Tab::Gupax => flip!(self.state.gupax.simple), 175 | Tab::Node => flip!(self.state.node.simple), 176 | Tab::P2pool => flip!(self.state.p2pool.simple), 177 | Tab::Xmrig => flip!(self.state.xmrig.simple), 178 | Tab::XmrigProxy => flip!(self.state.xmrig_proxy.simple), 179 | Tab::Xvb => flip!(self.state.xvb.simple), 180 | Tab::About => (), 181 | }; 182 | // Change Submenu RIGHT 183 | } else if key.is_v() && !wants_input { 184 | match self.tab { 185 | Tab::Status => match self.state.status.submenu { 186 | Submenu::Processes => self.state.status.submenu = Submenu::P2pool, 187 | Submenu::P2pool => self.state.status.submenu = Submenu::Benchmarks, 188 | Submenu::Benchmarks => self.state.status.submenu = Submenu::Processes, 189 | }, 190 | Tab::Gupax => flip!(self.state.gupax.simple), 191 | Tab::P2pool => flip!(self.state.p2pool.simple), 192 | Tab::Xmrig => flip!(self.state.xmrig.simple), 193 | Tab::XmrigProxy => flip!(self.state.xmrig_proxy.simple), 194 | Tab::Xvb => flip!(self.state.xvb.simple), 195 | Tab::Node => flip!(self.state.node.simple), 196 | Tab::About => (), 197 | }; 198 | } 199 | (key, wants_input) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/app/panels/middle/about.rs: -------------------------------------------------------------------------------- 1 | use egui::{Image, Label, ScrollArea, TextStyle, Ui, Vec2}; 2 | use log::debug; 3 | 4 | use crate::{ 5 | BYTES_BANNER, COMMIT, GUPAX_VERSION, KEYBOARD_SHORTCUTS, OS_NAME, P2POOL_VERSION, SPACE, 6 | XMRIG_PROXY_VERSION, XMRIG_VERSION, 7 | app::keys::KeyPressed, 8 | errors::{ErrorButtons, ErrorFerris}, 9 | }; 10 | 11 | impl crate::app::App { 12 | #[allow(clippy::too_many_arguments)] 13 | pub fn about_show(&mut self, key: KeyPressed, ui: &mut Ui) { 14 | ScrollArea::vertical().show(ui, |ui|{ 15 | debug!("App | Entering [About] Tab"); 16 | // If [D], show some debug info with [ErrorState] 17 | if key.is_d() { 18 | debug!("App | Entering [Debug Info]"); 19 | #[cfg(feature = "distro")] 20 | let distro = true; 21 | #[cfg(not(feature = "distro"))] 22 | let distro = false; 23 | let node_gui_len = self.node_api.lock().unwrap().output.len(); 24 | let p2pool_gui_len = self.p2pool_api.lock().unwrap().output.len(); 25 | let xmrig_gui_len = self.xmrig_api.lock().unwrap().output.len(); 26 | let xmrig_proxy_gui_len = self.xmrig_proxy_api.lock().unwrap().output.len(); 27 | let gupax_p2pool_api = self.gupax_p2pool_api.lock().unwrap(); 28 | let debug_info = format!( 29 | "Gupax version: {}\n 30 | Bundled Node version: {}\n 31 | Bundled P2Pool version: {}\n 32 | Bundled XMRig version: {}\n 33 | Bundled XMRig-Proxy version: {}\n 34 | Gupax uptime: {} seconds\n 35 | Selected resolution: {}x{}\n 36 | Internal resolution: {}x{}\n 37 | Operating system: {}\n 38 | Max detected threads: {}\n 39 | Gupax PID: {}\n 40 | State diff: {}\n 41 | Node list length: {}\n 42 | Pool list length: {}\n 43 | Admin privilege: {}\n 44 | Release build: {}\n 45 | Debug build: {}\n 46 | Distro build: {}\n 47 | Build commit: {}\n 48 | OS Data PATH: {}\n 49 | Gupax PATH: {}\n 50 | P2Pool PATH: {}\n 51 | XMRig PATH: {}\n 52 | XMRig-Proxy PATH: {}\n 53 | P2Pool console byte length: {}\n 54 | XMRig console byte length: {}\n 55 | XMRig-Proxy console byte length: {}\n 56 | ------------------------------------------ P2POOL IMAGE ------------------------------------------ 57 | {:#?}\n 58 | ------------------------------------------ XMRIG IMAGE ------------------------------------------ 59 | {:#?}\n 60 | ------------------------------------------ GUPAX-P2POOL API ------------------------------------------ 61 | payout: {:#?} 62 | payout_u64: {:#?} 63 | xmr: {:#?} 64 | path_log: {:#?} 65 | path_payout: {:#?} 66 | path_xmr: {:#?}\n 67 | ------------------------------------------ WORKING STATE ------------------------------------------ 68 | {:#?}\n 69 | ------------------------------------------ ORIGINAL STATE ------------------------------------------ 70 | {:#?}", 71 | GUPAX_VERSION, 72 | P2POOL_VERSION, 73 | XMRIG_VERSION, 74 | XMRIG_PROXY_VERSION, 75 | self.now.elapsed().as_secs_f32(), 76 | self.state.gupax.selected_width, 77 | self.state.gupax.selected_height, 78 | self.size.x, 79 | self.size.y, 80 | OS_NAME, 81 | self.max_threads, 82 | self.pid, 83 | self.diff, 84 | self.node_vec.len(), 85 | self.pool_vec.len(), 86 | self.admin, 87 | !cfg!(debug_assertions), 88 | cfg!(debug_assertions), 89 | distro, 90 | COMMIT, 91 | self.os_data_path.display(), 92 | self.exe, 93 | self.state.gupax.absolute_p2pool_path.display(), 94 | self.state.gupax.absolute_xmrig_path.display(), 95 | self.state.gupax.absolute_xp_path.display(), 96 | node_gui_len, 97 | p2pool_gui_len, 98 | xmrig_gui_len, 99 | xmrig_proxy_gui_len, 100 | self.p2pool_img.lock().unwrap(), 101 | self.xmrig_img.lock().unwrap(), 102 | gupax_p2pool_api.payout, 103 | gupax_p2pool_api.payout_u64, 104 | gupax_p2pool_api.xmr, 105 | gupax_p2pool_api.path_log, 106 | gupax_p2pool_api.path_payout, 107 | gupax_p2pool_api.path_xmr, 108 | self.state, 109 | self.og.lock().unwrap(), 110 | ); 111 | self.error_state 112 | .set(debug_info, ErrorFerris::Cute, ErrorButtons::Debug); 113 | } 114 | ui.add_space(10.0); 115 | ui.vertical_centered(|ui| { 116 | let space = SPACE * 2.0; 117 | ui.style_mut().override_text_style = Some(TextStyle::Heading); 118 | // ui.set_max_height(max_height); 119 | // Display [Gupaxx] banner 120 | // let link_width = width / 14.0; 121 | ui.add_sized( 122 | Vec2::new(ui.text_style_height(&TextStyle::Heading) * 20.0, 226.0), 123 | Image::from_bytes("bytes://banner.png", BYTES_BANNER), 124 | ); 125 | ui.label("is a GUI for mining"); 126 | ui.add_space(space); 127 | ui.hyperlink_to("[Monero]", "https://www.getmonero.org"); 128 | ui.label("on"); 129 | ui.hyperlink_to("[P2Pool]", "https://www.github.com/SChernykh/p2pool"); 130 | ui.add_space(space); 131 | ui.label("using"); 132 | ui.add_space(space); 133 | ui.hyperlink_to("[Monerod]", "https://github.com/monero-project/monero"); 134 | ui.hyperlink_to("[Xmrig]", "https://www.github.com/xmrig/xmrig"); 135 | ui.hyperlink_to("[Xmrig-Proxy]", "https://www.github.com/xmrig/xmrig-proxy"); 136 | ui.add_space(space); 137 | ui.label(" and participating in"); 138 | ui.hyperlink_to("[XvB Bonus Hashrate Raffle]", "https://xmrvsbeast.com"); 139 | ui.add_space(space); 140 | ui.add_sized([ui.available_width(), 0.0], Label::new(KEYBOARD_SHORTCUTS)); 141 | ui.add_space(space); 142 | 143 | if cfg!(debug_assertions) { 144 | ui.label(format!( 145 | "Gupaxx is running in debug mode - {}", 146 | self.now.elapsed().as_secs_f64() 147 | )); 148 | } 149 | ui.label(format!( 150 | "Gupaxx has been running for\n{}", 151 | self.pub_sys.lock().unwrap().gupax_uptime 152 | )); 153 | }); 154 | }); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/app/panels/middle/common/console.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use std::sync::{Arc, Mutex}; 19 | 20 | use egui::{Button, ScrollArea, TextEdit, TextStyle, TextWrapMode, Ui}; 21 | 22 | use crate::{ 23 | DARK_GRAY, 24 | helper::{Process, ProcessName}, 25 | regex::num_lines, 26 | }; 27 | 28 | pub fn console(ui: &mut Ui, text: &str, console_height: &mut u32, process_name: ProcessName) { 29 | let nb_lines = num_lines(text); 30 | *console_height = egui::Resize::default() 31 | .id_salt(process_name.to_string()) 32 | .default_height(*console_height as f32) 33 | .min_width(ui.available_width()) 34 | .max_width(ui.available_width()) 35 | .show(ui, |ui| { 36 | egui::Frame::new().fill(DARK_GRAY).show(ui, |ui| { 37 | ui.style_mut().wrap_mode = Some(TextWrapMode::Wrap); 38 | ui.style_mut().override_text_style = Some(TextStyle::Small); 39 | egui::ScrollArea::vertical() 40 | .stick_to_bottom(true) 41 | .max_width(ui.available_width()) 42 | .max_height(ui.available_height()) 43 | .auto_shrink([false; 2]) 44 | // .show_viewport(ui, |ui, _| { 45 | .show_rows( 46 | ui, 47 | ui.text_style_height(&TextStyle::Small), 48 | nb_lines, 49 | |ui, row_range| { 50 | for i in row_range { 51 | if let Some(line) = text.lines().nth(i) { 52 | ui.label(line); 53 | } 54 | } 55 | }, 56 | ); 57 | }) 58 | }) 59 | .response 60 | .rect 61 | .height() as u32; 62 | } 63 | 64 | // input args 65 | pub fn input_args_field( 66 | ui: &mut Ui, 67 | buffer: &mut String, 68 | process: &Arc>, 69 | hint: &str, 70 | hover: &str, 71 | ) { 72 | ui.style_mut().spacing.text_edit_width = ui.available_width(); 73 | let response = ui 74 | .add(TextEdit::hint_text(TextEdit::singleline(buffer), hint)) 75 | .on_hover_text(hover); 76 | // If the user pressed enter, dump buffer contents into the process STDIN 77 | if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { 78 | response.request_focus(); // Get focus back 79 | let buffer = std::mem::take(buffer); // Take buffer 80 | let mut process = process.lock().unwrap(); 81 | if process.is_alive() { 82 | process.input.push(buffer); 83 | } // Push only if alive 84 | } 85 | } 86 | 87 | // Command arguments 88 | pub fn start_options_field( 89 | ui: &mut Ui, 90 | arguments: &mut String, 91 | default_args_simple: &str, 92 | default_args_advanced: &str, 93 | hint: &str, 94 | hover: &str, 95 | ) { 96 | ui.group(|ui| { 97 | ui.label("Start options:"); 98 | ui.style_mut().wrap_mode = Some(TextWrapMode::Wrap); 99 | ui.style_mut().spacing.text_edit_width = ui.available_width(); 100 | ui.add( 101 | TextEdit::multiline(arguments) 102 | .hint_text(hint) 103 | .desired_rows(1) 104 | .desired_width(ui.available_width()), 105 | ) 106 | .on_hover_text(hover); 107 | ui.horizontal(|ui| { 108 | ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); 109 | 110 | ScrollArea::horizontal().show(ui, |ui| { 111 | if ui 112 | .add_enabled( 113 | default_args_simple != arguments, 114 | Button::new(" Reset to Simple options "), 115 | ) 116 | .on_hover_text("Reset the start options to arguments used for simple mode") 117 | .clicked() 118 | { 119 | *arguments = default_args_simple.to_string(); 120 | } 121 | 122 | if ui 123 | .add_enabled( 124 | default_args_advanced != arguments, 125 | Button::new("Reset to Advanced options"), 126 | ) 127 | .on_hover_text("Reset the start options to arguments used for advanced mode") 128 | .clicked() 129 | { 130 | *arguments = default_args_advanced.to_string(); 131 | } 132 | if ui 133 | .add_enabled(!arguments.is_empty(), Button::new("Clear")) 134 | .on_hover_text("Clear custom start options to use the advanced settings") 135 | .clicked() 136 | { 137 | *arguments = String::new(); 138 | } 139 | }); 140 | }); 141 | }); 142 | } 143 | -------------------------------------------------------------------------------- /src/app/panels/middle/common/header_tab.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use egui::{Hyperlink, Image, Label, Separator, TextStyle, TextWrapMode, Ui}; 19 | 20 | use crate::SPACE; 21 | // prevent compiling if no elements are added to a header. No need for a header then. 22 | const fn check_header_element(is_logo_none: bool, is_links_empty: bool, is_subtitle_none: bool) { 23 | if is_logo_none && is_links_empty && is_subtitle_none { 24 | panic!("header_tab must be used with at least one element"); 25 | } 26 | } 27 | /// logo first, first hyperlink will be the header, description under. 28 | /// will take care of centering widgets if boerder weight is more than 0. 29 | #[allow(clippy::needless_range_loop)] 30 | pub fn header_tab( 31 | ui: &mut Ui, 32 | logo: Option, 33 | // text, link, hover text 34 | links: &[(&str, &str, &str)], 35 | subtitle: Option<&str>, 36 | one_line_center: bool, 37 | ) { 38 | check_header_element(logo.is_none(), links.is_empty(), subtitle.is_none()); 39 | ui.add_space(SPACE); 40 | ui.scope(|ui| { 41 | ui.style_mut().wrap_mode = Some(TextWrapMode::Extend); 42 | ui.style_mut().override_text_style = Some(TextStyle::Heading); 43 | ui.spacing_mut().item_spacing.x = 0.0; 44 | if one_line_center { 45 | let height_logo = 64.0; 46 | let width_links = links 47 | .iter() 48 | .map(|x| x.0.len() as f32 * ui.text_style_height(&TextStyle::Heading) / 2.0) 49 | .collect::>(); 50 | let width_subtitle = subtitle.unwrap_or_default().len() as f32 51 | * ui.text_style_height(&TextStyle::Body) 52 | / 2.0; 53 | let nb_txt = links.len() + if subtitle.is_some() { 1 } else { 0 }; 54 | // width of separator depends of width of ui and number of texts 55 | let width_separator = 56 | (ui.available_width() / ui.text_style_height(&TextStyle::Heading) * 4.0) 57 | / nb_txt as f32; 58 | // width available - logo and separator - total width of txt - separator for each text then divided by two 59 | let border_width = (((ui.available_width() 60 | - if logo.is_some() { 61 | height_logo + width_separator 62 | } else { 63 | 0.0 64 | } 65 | - width_links.iter().sum::() 66 | - width_subtitle 67 | - (nb_txt as f32 * width_separator)) 68 | + width_separator) 69 | / 2.0) 70 | .max(0.0); 71 | ui.horizontal(|ui| { 72 | ui.add_space(border_width); 73 | if let Some(logo) = logo { 74 | ui.add_sized([height_logo, height_logo], logo); 75 | ui.add_sized( 76 | [0.0, height_logo], 77 | Separator::default().vertical().spacing(width_separator), 78 | ); 79 | } 80 | for (count, link) in links.iter().enumerate() { 81 | ui.add_sized( 82 | [width_links[count], height_logo], 83 | // [0.0, height_logo], 84 | Hyperlink::from_label_and_url(link.0, link.1), 85 | ); 86 | if count != (links.len() - 1) || subtitle.is_some() { 87 | ui.add_sized( 88 | [0.0, height_logo], 89 | Separator::default().vertical().spacing(width_separator), 90 | ); 91 | } 92 | } 93 | if let Some(desc) = subtitle { 94 | ui.style_mut().override_text_style = Some(TextStyle::Body); 95 | ui.add_sized([0.0, height_logo], Label::new(desc)); 96 | } 97 | }); 98 | } else { 99 | // top down 100 | ui.vertical_centered(|ui| { 101 | if let Some(source) = logo { 102 | ui.add(source); 103 | } 104 | for link in links { 105 | ui.hyperlink_to(link.0, link.1); 106 | } 107 | if let Some(desc) = subtitle { 108 | ui.style_mut().override_text_style = Some(TextStyle::Body); 109 | ui.label(desc); 110 | } 111 | }); 112 | } 113 | }); 114 | ui.add_space(SPACE); 115 | } 116 | -------------------------------------------------------------------------------- /src/app/panels/middle/common/mod.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | pub mod console; 19 | pub mod header_tab; 20 | pub mod list_poolnode; 21 | pub mod state_edit_field; 22 | pub mod toggle; 23 | -------------------------------------------------------------------------------- /src/app/panels/middle/common/toggle.rs: -------------------------------------------------------------------------------- 1 | pub fn toggle_ui_compact(on: &mut bool, ui: &mut egui::Ui) -> egui::Response { 2 | let desired_size = ui.spacing().interact_size.y * egui::vec2(4.0, 2.0); 3 | let (rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click()); 4 | if response.clicked() { 5 | *on = !*on; 6 | response.mark_changed(); 7 | } 8 | response.widget_info(|| { 9 | egui::WidgetInfo::selected(egui::WidgetType::Checkbox, ui.is_enabled(), *on, "") 10 | }); 11 | 12 | if ui.is_rect_visible(rect) { 13 | let how_on = ui.ctx().animate_bool_responsive(response.id, *on); 14 | let visuals = ui.style().interact_selectable(&response, *on); 15 | let rect = rect.expand(visuals.expansion); 16 | let radius = 0.5 * rect.height(); 17 | ui.painter().rect( 18 | rect, 19 | radius, 20 | visuals.bg_fill, 21 | visuals.bg_stroke, 22 | egui::StrokeKind::Inside, 23 | ); 24 | let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on); 25 | let center = egui::pos2(circle_x, rect.center().y); 26 | ui.painter() 27 | .circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke); 28 | } 29 | 30 | response 31 | } 32 | -------------------------------------------------------------------------------- /src/app/panels/middle/node.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field}; 19 | use crate::app::panels::middle::common::header_tab::header_tab; 20 | use crate::app::panels::middle::common::state_edit_field::{path_db_field, slider_state_field}; 21 | use crate::app::panels::middle::{rpc_bind_field, rpc_port_field, zmq_bind_field, zmq_port_field}; 22 | use crate::{ 23 | NODE_DNS_BLOCKLIST, NODE_DNS_CHECKPOINT, NODE_FULL_MEM, NODE_INPUT, NODE_PRUNNING, NODE_URL, 24 | START_OPTIONS_HOVER, 25 | }; 26 | use egui::TextStyle; 27 | use std::sync::{Arc, Mutex}; 28 | 29 | use log::debug; 30 | 31 | use crate::components::gupax::FileWindow; 32 | use crate::disk::state::{Node, StartOptionsMode}; 33 | use crate::helper::node::PubNodeApi; 34 | use crate::helper::{Process, ProcessName}; 35 | use crate::{P2POOL_IN, P2POOL_LOG, P2POOL_OUT, SPACE}; 36 | 37 | impl Node { 38 | #[inline(always)] // called once 39 | pub fn show( 40 | &mut self, 41 | process: &Arc>, 42 | api: &Arc>, 43 | buffer: &mut String, 44 | file_window: &Arc>, 45 | ui: &mut egui::Ui, 46 | ) { 47 | ui.style_mut().override_text_style = Some(TextStyle::Body); 48 | header_tab( 49 | ui, 50 | None, 51 | &[("Monerod", NODE_URL, "")], 52 | Some("C++ Monero Node"), 53 | true, 54 | ); 55 | // console output for log 56 | debug!("Node Tab | Rendering [Console]"); 57 | egui::ScrollArea::vertical().show(ui, |ui| { 58 | let text = &api.lock().unwrap().output; 59 | ui.group(|ui| { 60 | console(ui, text, &mut self.console_height, ProcessName::Node); 61 | if !self.simple { 62 | ui.separator(); 63 | input_args_field( 64 | ui, 65 | buffer, 66 | process, 67 | r#"Commands: help, status, set_log , diff"#, 68 | NODE_INPUT, 69 | ); 70 | } 71 | }); 72 | //---------------------------------------------------------------------------------------------------- [Advanced] Console 73 | if !self.simple { 74 | //---------------------------------------------------------------------------------------------------- Arguments 75 | debug!("Node Tab | Rendering [Arguments]"); 76 | let default_args_simple = self.start_options(StartOptionsMode::Simple); 77 | let default_args_advanced = self.start_options(StartOptionsMode::Advanced); 78 | start_options_field( 79 | ui, 80 | &mut self.arguments, 81 | &default_args_simple, 82 | &default_args_advanced, 83 | Self::process_name().start_options_hint(), 84 | START_OPTIONS_HOVER, 85 | ); 86 | //---------------------------------------------------------------------------------------------------- Prunned checkbox 87 | if !self.arguments.is_empty() { 88 | ui.disable(); 89 | } 90 | ui.add_space(SPACE); 91 | debug!("Node Tab | Rendering DNS and Prunning buttons"); 92 | ui.horizontal(|ui| { 93 | ui.group(|ui| { 94 | ui.checkbox(&mut self.pruned, "Prunned") 95 | .on_hover_text(NODE_PRUNNING); 96 | ui.separator(); 97 | ui.checkbox(&mut self.dns_blocklist, "DNS blocklist") 98 | .on_hover_text(NODE_DNS_BLOCKLIST); 99 | ui.separator(); 100 | ui.checkbox(&mut self.disable_dns_checkpoint, "DNS checkpoint") 101 | .on_hover_text(NODE_DNS_CHECKPOINT); 102 | ui.separator(); 103 | ui.checkbox(&mut self.full_memory, "Full memory") 104 | .on_hover_text(NODE_FULL_MEM); 105 | }); 106 | }); 107 | 108 | ui.add_space(SPACE); 109 | // // idea 110 | // // need to warn the user if local firewall is blocking port 111 | // // need to warn the user if NAT is blocking port 112 | // // need to show local ip address 113 | // // need to show public ip 114 | ui.horizontal(|ui| { 115 | egui::ScrollArea::horizontal().show(ui, |ui| { 116 | ui.group(|ui| { 117 | ui.vertical(|ui| { 118 | rpc_bind_field(&mut self.api_ip, ui); 119 | rpc_port_field(&mut self.api_port, ui); 120 | ui.add_space(SPACE); 121 | zmq_bind_field(&mut self.zmq_ip, ui); 122 | zmq_port_field(&mut self.zmq_port, ui); 123 | }); 124 | }); 125 | 126 | //---------------------------------------------------------------------------------------------------- In/Out peers 127 | debug!("Node Tab | Rendering sliders elements"); 128 | ui.vertical(|ui| { 129 | ui.group(|ui| { 130 | ui.add_space(SPACE); 131 | slider_state_field( 132 | ui, 133 | "Out peers [2-450]:", 134 | P2POOL_OUT, 135 | &mut self.out_peers, 136 | 2..=450, 137 | ); 138 | ui.add_space(SPACE); 139 | slider_state_field( 140 | ui, 141 | "In peers [2-450]:", 142 | P2POOL_IN, 143 | &mut self.in_peers, 144 | 2..=450, 145 | ); 146 | ui.add_space(SPACE); 147 | slider_state_field( 148 | ui, 149 | "Log level [ 0-4 ]:", 150 | P2POOL_LOG, 151 | &mut self.log_level, 152 | 0..=6, 153 | ); 154 | ui.add_space(SPACE); 155 | }); 156 | }); 157 | }); 158 | }); 159 | //---------------------------------------------------------------------------------------------------- DB path 160 | ui.add_space(SPACE); 161 | ui.group(|ui| { 162 | path_db_field(ui, &mut self.path_db, file_window); 163 | let mut guard = file_window.lock().unwrap(); 164 | if guard.picked_nodedb { 165 | self.path_db.clone_from(&guard.nodedb_path); 166 | guard.picked_nodedb = false; 167 | } 168 | }); 169 | ui.add_space(SPACE); 170 | } 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/app/panels/middle/p2pool/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field}; 2 | use crate::disk::state::{P2pool, StartOptionsMode, State}; 3 | use crate::helper::p2pool::PubP2poolApi; 4 | // Gupaxx - Fork of Gupax 5 | // 6 | // Copyright (c) 2024-2025 Cyrix126 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | use crate::{components::node::*, constants::*, helper::*}; 21 | use log::*; 22 | 23 | use std::path::Path; 24 | use std::sync::{Arc, Mutex}; 25 | 26 | use super::common::header_tab::header_tab; 27 | use super::common::list_poolnode::PoolNode; 28 | 29 | mod advanced; 30 | mod simple; 31 | 32 | impl P2pool { 33 | #[inline(always)] // called once 34 | #[allow(clippy::too_many_arguments)] 35 | pub fn show( 36 | &mut self, 37 | node_vec: &mut Vec<(String, PoolNode)>, 38 | _og: &Arc>, 39 | ping: &Arc>, 40 | process: &Arc>, 41 | api: &Arc>, 42 | buffer: &mut String, 43 | _ctx: &egui::Context, 44 | ui: &mut egui::Ui, 45 | backup_nodes: Option>, 46 | path: &Path, 47 | local_node_zmq_port: u16, 48 | local_node_rpc_port: u16, 49 | ) { 50 | //---------------------------------------------------------------------------------------------------- [Simple] Console 51 | // debug!("P2Pool Tab | Rendering [Console]"); 52 | let mut api_lock = api.lock().unwrap(); 53 | // let mut prefer_local_node = api.lock().unwrap().prefer_local_node; 54 | header_tab( 55 | ui, 56 | None, 57 | &[("P2Pool", P2POOL_URL, "")], 58 | Some("Decentralized pool for Monero mining"), 59 | true, 60 | ); 61 | egui::ScrollArea::vertical().show(ui, |ui| { 62 | let text = &api_lock.output; 63 | ui.group(|ui| { 64 | console(ui, text, &mut self.console_height, ProcessName::P2pool); 65 | if !self.simple { 66 | ui.separator(); 67 | input_args_field( 68 | ui, 69 | buffer, 70 | process, 71 | r#"Type a command (e.g "help" or "status") and press Enter"#, 72 | P2POOL_INPUT, 73 | ); 74 | } 75 | }); 76 | if !self.simple { 77 | let default_args_simple = self.start_options( 78 | path, 79 | &backup_nodes, 80 | StartOptionsMode::Simple, 81 | local_node_zmq_port, 82 | local_node_rpc_port, 83 | ); 84 | let default_args_advanced = self.start_options( 85 | path, 86 | &backup_nodes, 87 | StartOptionsMode::Advanced, 88 | local_node_zmq_port, 89 | local_node_rpc_port, 90 | ); 91 | start_options_field( 92 | ui, 93 | &mut self.arguments, 94 | &default_args_simple, 95 | &default_args_advanced, 96 | Self::process_name().start_options_hint(), 97 | START_OPTIONS_HOVER, 98 | ); 99 | } 100 | debug!("P2Pool Tab | Rendering [Address]"); 101 | crate::app::panels::middle::common::state_edit_field::monero_address_field( 102 | &mut self.address, 103 | ui, 104 | P2POOL_ADDRESS, 105 | ); 106 | 107 | if self.simple { 108 | self.simple(ui, ping, &mut api_lock); 109 | } else { 110 | if !self.arguments.is_empty() { 111 | ui.disable(); 112 | } 113 | self.advanced(ui, node_vec); 114 | } 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/app/panels/middle/status/mod.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::{ 19 | app::{Benchmark, eframe_impl::ProcessStatesGui}, 20 | disk::{gupax_p2pool_api::GupaxP2poolApi, state::Status, status::*}, 21 | helper::{ 22 | ProcessName, ProcessState, Sys, 23 | node::PubNodeApi, 24 | p2pool::{ImgP2pool, PubP2poolApi}, 25 | xrig::{ 26 | xmrig::{ImgXmrig, PubXmrigApi}, 27 | xmrig_proxy::PubXmrigProxyApi, 28 | }, 29 | xvb::PubXvbApi, 30 | }, 31 | }; 32 | use std::sync::{Arc, Mutex}; 33 | 34 | mod benchmarks; 35 | mod p2pool; 36 | mod processes; 37 | 38 | impl Status { 39 | #[inline(always)] // called once 40 | #[allow(clippy::too_many_arguments)] 41 | pub fn show( 42 | &mut self, 43 | show_process: &[ProcessName], 44 | sys: &Arc>, 45 | node_api: &Arc>, 46 | p2pool_api: &Arc>, 47 | xmrig_api: &Arc>, 48 | xmrig_proxy_api: &Arc>, 49 | xvb_api: &Arc>, 50 | p2pool_img: &Arc>, 51 | xmrig_img: &Arc>, 52 | states: &ProcessStatesGui, 53 | max_threads: u16, 54 | gupax_p2pool_api: &Arc>, 55 | benchmarks: &[Benchmark], 56 | _ctx: &egui::Context, 57 | ui: &mut egui::Ui, 58 | ) { 59 | //---------------------------------------------------------------------------------------------------- [Processes] 60 | if self.submenu == Submenu::Processes { 61 | self.processes( 62 | show_process, 63 | sys, 64 | ui, 65 | node_api, 66 | p2pool_api, 67 | p2pool_img, 68 | xmrig_api, 69 | xmrig_proxy_api, 70 | xmrig_img, 71 | xvb_api, 72 | max_threads, 73 | states, 74 | ); 75 | //---------------------------------------------------------------------------------------------------- [P2Pool] 76 | } else if self.submenu == Submenu::P2pool { 77 | self.p2pool( 78 | ui, 79 | gupax_p2pool_api, 80 | states.find(ProcessName::P2pool).state == ProcessState::Alive, 81 | p2pool_api, 82 | ); 83 | //---------------------------------------------------------------------------------------------------- [Benchmarks] 84 | } else if self.submenu == Submenu::Benchmarks { 85 | self.benchmarks( 86 | ui, 87 | benchmarks, 88 | states.is_alive(ProcessName::Xmrig), 89 | xmrig_api, 90 | ) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/app/panels/mod.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | pub mod bottom; 19 | pub mod middle; 20 | pub mod quit_error; 21 | pub mod top; 22 | -------------------------------------------------------------------------------- /src/app/panels/top.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::app::Tab; 19 | use egui::TextStyle; 20 | use egui::{ScrollArea, SelectableLabel, Separator, TopBottomPanel, Ui}; 21 | use log::debug; 22 | 23 | impl crate::app::App { 24 | pub fn top_panel(&mut self, ctx: &egui::Context) { 25 | debug!("App | Rendering TOP tabs"); 26 | let tabs = Tab::from_show_processes(&self.state.gupax.show_processes); 27 | TopBottomPanel::top("top").show(ctx, |ui| { 28 | // low spacing to shrink and be able to show all tabs on one line on 640x480 29 | ui.style_mut().spacing.item_spacing.x = 4.0; 30 | // spacing of separator, will reduce width size of the button. Low value so that tabs can be selected easily. 31 | let spacing_separator = 2.0; 32 | ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| { 33 | ui.style_mut().override_text_style = Some(TextStyle::Heading); 34 | let height = ui 35 | .style() 36 | .text_styles 37 | .get(&TextStyle::Heading) 38 | .unwrap() 39 | .size 40 | * 2.75; 41 | // width = (width - / number of tab) - (space between widget * 2.0 + space of separator / 2.0) 42 | let width = (((self.size.x) / tabs.len() as f32) 43 | - ((ui.style().spacing.item_spacing.x * 2.0) + (spacing_separator / 2.0))) 44 | .max(0.0); 45 | // height of tab menu relative to size of text. coeff 2.75 is arbitrary but good enough to be easily clickable. 46 | self.tabs(ui, [width, height], spacing_separator, tabs); 47 | }); 48 | }); 49 | } 50 | 51 | fn tabs(&mut self, ui: &mut Ui, size: [f32; 2], spacing_separator: f32, tabs: Vec) { 52 | ScrollArea::horizontal() 53 | .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) 54 | .show(ui, |ui| { 55 | let nb_tabs = tabs.len(); 56 | for (count, tab) in tabs.into_iter().enumerate() { 57 | ui.horizontal(|ui| { 58 | ui.vertical(|ui| { 59 | // we don't want y item spacing to influence the added space 60 | ui.style_mut().spacing.item_spacing.y = 0.0; 61 | ui.add_space(spacing_separator); 62 | ui.horizontal(|ui| { 63 | if ui 64 | .add_sized( 65 | size, 66 | SelectableLabel::new(self.tab == tab, tab.to_string()), 67 | ) 68 | .clicked() 69 | { 70 | self.tab = tab 71 | } 72 | }); 73 | // add a space to prevent selectable button to be at the same line as the end of the top bar. Make it the same spacing as separators. 74 | ui.add_space(spacing_separator); 75 | }); 76 | if count + 1 != nb_tabs { 77 | ui.add(Separator::default().spacing(spacing_separator).vertical()); 78 | } 79 | }); 80 | } 81 | }); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/app/quit.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use log::info; 19 | 20 | use crate::errors::ErrorButtons; 21 | use crate::errors::ErrorFerris; 22 | 23 | use super::App; 24 | 25 | impl App { 26 | pub(super) fn quit(&mut self, ctx: &egui::Context) { 27 | // If closing. 28 | // Used to be `eframe::App::on_close_event(&mut self) -> bool`. 29 | let close_signal = ctx.input(|input| { 30 | use egui::viewport::ViewportCommand; 31 | 32 | if !input.viewport().close_requested() { 33 | return None; 34 | } 35 | info!("quit"); 36 | if self.state.gupax.auto.ask_before_quit { 37 | // If we're already on the [ask_before_quit] screen and 38 | // the user tried to exit again, exit. 39 | if self.error_state.quit_twice { 40 | if self.state.gupax.auto.save_before_quit { 41 | self.save_before_quit(); 42 | } 43 | return Some(ViewportCommand::Close); 44 | } 45 | // Else, set the error 46 | self.error_state 47 | .set("", ErrorFerris::Oops, ErrorButtons::StayQuit); 48 | self.error_state.quit_twice = true; 49 | Some(ViewportCommand::CancelClose) 50 | // Else, just quit. 51 | } else { 52 | if self.state.gupax.auto.save_before_quit { 53 | self.save_before_quit(); 54 | } 55 | Some(ViewportCommand::Close) 56 | } 57 | }); 58 | // This will either: 59 | // 1. Cancel a close signal 60 | // 2. Close the program 61 | if let Some(cmd) = close_signal { 62 | ctx.send_viewport_cmd(cmd); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use clap::Subcommand; 3 | use clap::crate_authors; 4 | use clap::crate_description; 5 | use clap::crate_name; 6 | use clap::crate_version; 7 | use log::debug; 8 | use log::info; 9 | use log::warn; 10 | use std::process::exit; 11 | 12 | use crate::app::App; 13 | use crate::miscs::print_disk_file; 14 | use crate::miscs::print_gupax_p2pool_api; 15 | use crate::resets::reset; 16 | use crate::resets::reset_gupax_p2pool_api; 17 | use crate::resets::reset_nodes; 18 | use crate::resets::reset_pools; 19 | use crate::resets::reset_state; 20 | 21 | #[derive(Parser)] 22 | #[command(name = crate_name!())] 23 | #[command(author = crate_authors!())] 24 | #[command(version = crate_version!())] 25 | #[command(about = crate_description!(), long_about = None)] 26 | #[command(next_line_help = true)] 27 | #[group(required = false, multiple = false)] 28 | pub struct Cli { 29 | #[command(subcommand)] 30 | pub info: Option, 31 | #[clap(long, short, action)] 32 | pub logfile: bool, 33 | } 34 | 35 | #[derive(Subcommand)] 36 | pub enum GupaxxData { 37 | #[command(about = "Print Gupaxx state")] 38 | State, 39 | #[command(about = "Print the manual node list")] 40 | Nodes, 41 | #[command(about = "Print the P2Pool payout log, payout count, and total XMR mined")] 42 | Payouts, 43 | #[command(about = "Reset all Gupaxxstate (your settings)")] 44 | ResetState, 45 | #[command(about = "Reset the manual node list in the [P2Pool] tab")] 46 | ResetNodes, 47 | #[command(about = "Reset the manual pool list in the [XMRig] tab")] 48 | ResetPools, 49 | #[command(about = "Reset the permanent P2Pool stats that appear in the [Status] tab")] 50 | ResetPayouts, 51 | #[command(about = "Reset all Gupaxx state (your settings)")] 52 | ResetAll, 53 | #[command( 54 | about = "Disable all auto-startup settings for this instance (auto-update, auto-ping, etc)", 55 | name = "no-startup" 56 | )] 57 | Nostartup, 58 | } 59 | // #[cold] 60 | // #[inline(never)] 61 | pub fn parse_args>(mut app: App, args: Cli, panic: S) -> App { 62 | info!("Parsing CLI arguments..."); 63 | 64 | // Abort on panic 65 | let panic = panic.into(); 66 | if !panic.is_empty() { 67 | warn!("[Gupax error] {}", panic); 68 | exit(1); 69 | } 70 | if let Some(arg) = args.info { 71 | match arg { 72 | GupaxxData::State => { 73 | debug!("Printing state..."); 74 | print_disk_file(&app.state_path); 75 | exit(0); 76 | } 77 | GupaxxData::Nodes => { 78 | debug!("Printing node list..."); 79 | print_disk_file(&app.node_path); 80 | exit(0); 81 | } 82 | GupaxxData::Payouts => { 83 | debug!("Printing payouts...\n"); 84 | print_gupax_p2pool_api(&app.gupax_p2pool_api); 85 | exit(0); 86 | } 87 | GupaxxData::ResetState => { 88 | if let Ok(()) = reset_state(&app.state_path) { 89 | println!("\nState reset ... OK"); 90 | exit(0); 91 | } else { 92 | eprintln!("\nState reset ... FAIL"); 93 | exit(1) 94 | } 95 | } 96 | GupaxxData::ResetNodes => { 97 | if let Ok(()) = reset_nodes(&app.node_path) { 98 | println!("\nNode reset ... OK"); 99 | exit(0) 100 | } else { 101 | eprintln!("\nNode reset ... FAIL"); 102 | exit(1) 103 | } 104 | } 105 | GupaxxData::ResetPools => { 106 | if let Ok(()) = reset_pools(&app.pool_path) { 107 | println!("\nPool reset ... OK"); 108 | exit(0) 109 | } else { 110 | eprintln!("\nPool reset ... FAIL"); 111 | exit(1) 112 | } 113 | } 114 | GupaxxData::ResetPayouts => { 115 | if let Ok(()) = reset_gupax_p2pool_api(&app.gupax_p2pool_api_path) { 116 | println!("\nGupaxP2poolApi reset ... OK"); 117 | exit(0) 118 | } else { 119 | eprintln!("\nGupaxP2poolApi reset ... FAIL"); 120 | exit(1) 121 | } 122 | } 123 | GupaxxData::ResetAll => reset( 124 | &app.os_data_path, 125 | &app.state_path, 126 | &app.node_path, 127 | &app.pool_path, 128 | &app.gupax_p2pool_api_path, 129 | ), 130 | GupaxxData::Nostartup => app.no_startup = true, 131 | } 132 | } 133 | app 134 | } 135 | -------------------------------------------------------------------------------- /src/components/gupax.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - GUI Uniting P2Pool And XMRig 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::{disk::state::*, utils::macros::arc_mut}; 19 | use log::*; 20 | use serde::{Deserialize, Serialize}; 21 | use std::{ 22 | sync::{Arc, Mutex}, 23 | thread, 24 | }; 25 | 26 | //---------------------------------------------------------------------------------------------------- FileWindow 27 | // Struct for writing/reading the path state. 28 | // The opened file picker is started in a new 29 | // thread so main() needs to be in sync. 30 | pub struct FileWindow { 31 | pub thread: bool, // Is there already a FileWindow thread? 32 | pub picked_p2pool: bool, // Did the user pick a path for p2pool? 33 | pub picked_xmrig: bool, // Did the user pick a path for xmrig? 34 | pub picked_xp: bool, // Did the user pick a path for xmrig-proxy? 35 | pub picked_node: bool, // Did the user pick a path for node? 36 | pub picked_nodedb: bool, // Did the user pick a path for node? 37 | pub p2pool_path: String, // The picked p2pool path 38 | pub node_path: String, // The picked node path 39 | pub nodedb_path: String, // The picked node path 40 | pub xmrig_path: String, // The picked xmrig path 41 | pub xmrig_proxy_path: String, // The picked xmrig-proxy path 42 | } 43 | 44 | impl FileWindow { 45 | pub fn new() -> Arc> { 46 | arc_mut!(Self { 47 | thread: false, 48 | picked_p2pool: false, 49 | picked_xmrig: false, 50 | picked_xp: false, 51 | picked_node: false, 52 | picked_nodedb: false, 53 | p2pool_path: String::new(), 54 | node_path: String::new(), 55 | nodedb_path: String::new(), 56 | xmrig_path: String::new(), 57 | xmrig_proxy_path: String::new(), 58 | }) 59 | } 60 | } 61 | 62 | #[derive(Debug, Clone)] 63 | pub enum FileType { 64 | P2pool, 65 | Xmrig, 66 | XmrigProxy, 67 | Node, 68 | NodeDB, 69 | } 70 | 71 | //---------------------------------------------------------------------------------------------------- Ratio Lock 72 | // Enum for the lock ratio in the advanced tab. 73 | #[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)] 74 | pub enum Ratio { 75 | Width, 76 | Height, 77 | None, 78 | } 79 | 80 | //---------------------------------------------------------------------------------------------------- Gupaxx 81 | impl Gupax { 82 | // Checks if a path is a valid path to a file. 83 | pub fn path_is_file(path: &str) -> bool { 84 | let path = path.to_string(); 85 | match crate::disk::into_absolute_path(path) { 86 | Ok(path) => path.is_file(), 87 | _ => false, 88 | } 89 | } 90 | // Checks if a path is a valid path to a directory. 91 | pub fn path_is_dir(path: &str) -> bool { 92 | let path = path.to_string(); 93 | match crate::disk::into_absolute_path(path) { 94 | Ok(path) => path.is_dir(), 95 | _ => false, 96 | } 97 | } 98 | 99 | #[cold] 100 | #[inline(never)] 101 | pub fn spawn_file_window_thread(file_window: &Arc>, file_type: FileType) { 102 | use FileType::*; 103 | let name = match file_type { 104 | P2pool => "P2Pool", 105 | Xmrig => "XMRig", 106 | XmrigProxy => "XMRigProxy", 107 | Node => "Node", 108 | NodeDB => "Node DB", 109 | }; 110 | let file_window = file_window.clone(); 111 | file_window.lock().unwrap().thread = true; 112 | thread::spawn(move || { 113 | let path = match file_type { 114 | NodeDB => rfd::FileDialog::new() 115 | .set_title("Select a directory for the DB of your Node") 116 | .pick_folder(), 117 | _ => rfd::FileDialog::new() 118 | .set_title(format!("Select {} Binary for Gupaxx", name)) 119 | .pick_file(), 120 | }; 121 | if let Some(path) = path { 122 | info!("Gupaxx | Path selected for {} ... {}", name, path.display()); 123 | match file_type { 124 | P2pool => { 125 | file_window.lock().unwrap().p2pool_path = path.display().to_string(); 126 | file_window.lock().unwrap().picked_p2pool = true; 127 | } 128 | Xmrig => { 129 | file_window.lock().unwrap().xmrig_path = path.display().to_string(); 130 | file_window.lock().unwrap().picked_xmrig = true; 131 | } 132 | XmrigProxy => { 133 | file_window.lock().unwrap().xmrig_proxy_path = path.display().to_string(); 134 | file_window.lock().unwrap().picked_xp = true; 135 | } 136 | Node => { 137 | file_window.lock().unwrap().node_path = path.display().to_string(); 138 | file_window.lock().unwrap().picked_node = true; 139 | } 140 | NodeDB => { 141 | file_window.lock().unwrap().nodedb_path = path.display().to_string(); 142 | file_window.lock().unwrap().picked_nodedb = true; 143 | } 144 | }; 145 | } else { 146 | info!("Gupaxx | No path selected for {}", name); 147 | } 148 | 149 | file_window.lock().unwrap().thread = false; 150 | }); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/components/mod.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | pub mod gupax; 19 | pub mod node; 20 | pub mod update; 21 | -------------------------------------------------------------------------------- /src/disk/consts.rs: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------- Const 2 | // State file 3 | pub const ERROR: &str = "Disk error"; 4 | pub const PATH_ERROR: &str = "PATH for state directory could not be not found"; 5 | 6 | #[cfg(target_os = "windows")] 7 | pub const DIRECTORY: &str = r#"Gupaxx\"#; 8 | #[cfg(target_os = "macos")] 9 | pub const DIRECTORY: &str = "Gupaxx/"; 10 | #[cfg(target_os = "linux")] 11 | pub const DIRECTORY: &str = "gupaxx/"; 12 | 13 | // File names 14 | pub const STATE_TOML: &str = "state.toml"; 15 | pub const NODE_TOML: &str = "node.toml"; 16 | pub const POOL_TOML: &str = "pool.toml"; 17 | 18 | // P2Pool API 19 | // Lives within the Gupax OS data directory. 20 | // ~/.local/share/gupax/p2pool/ 21 | // ├─ payout_log // Raw log lines of payouts received 22 | // ├─ payout // Single [u64] representing total payouts 23 | // ├─ xmr // Single [u64] representing total XMR mined in atomic units 24 | #[cfg(target_os = "windows")] 25 | pub const GUPAX_P2POOL_API_DIRECTORY: &str = r"p2pool\"; 26 | #[cfg(target_family = "unix")] 27 | pub const GUPAX_P2POOL_API_DIRECTORY: &str = "p2pool/"; 28 | pub const GUPAX_P2POOL_API_LOG: &str = "log"; 29 | pub const GUPAX_P2POOL_API_PAYOUT: &str = "payout"; 30 | pub const GUPAX_P2POOL_API_XMR: &str = "xmr"; 31 | pub const GUPAX_P2POOL_API_FILE_ARRAY: [&str; 3] = [ 32 | GUPAX_P2POOL_API_LOG, 33 | GUPAX_P2POOL_API_PAYOUT, 34 | GUPAX_P2POOL_API_XMR, 35 | ]; 36 | 37 | #[cfg(target_os = "windows")] 38 | pub const DEFAULT_P2POOL_PATH: &str = r"P2Pool\p2pool.exe"; 39 | #[cfg(target_os = "macos")] 40 | pub const DEFAULT_P2POOL_PATH: &str = "p2pool/p2pool"; 41 | #[cfg(target_os = "windows")] 42 | pub const DEFAULT_XMRIG_PATH: &str = r"XMRig\xmrig.exe"; 43 | #[cfg(target_os = "windows")] 44 | pub const DEFAULT_NODE_PATH: &str = r"node\monerod.exe"; 45 | #[cfg(target_os = "windows")] 46 | pub const DEFAULT_XMRIG_PROXY_PATH: &str = r"XMRig-Proxy\xmrig-proxy.exe"; 47 | #[cfg(target_os = "macos")] 48 | pub const DEFAULT_XMRIG_PATH: &str = "xmrig/xmrig"; 49 | #[cfg(target_os = "macos")] 50 | pub const DEFAULT_XMRIG_PROXY_PATH: &str = "xmrig-proxy/xmrig-proxy"; 51 | #[cfg(target_os = "macos")] 52 | pub const DEFAULT_NODE_PATH: &str = "node/monerod"; 53 | 54 | // Default to [/usr/bin/] for Linux distro builds. 55 | #[cfg(target_os = "linux")] 56 | #[cfg(not(feature = "distro"))] 57 | pub const DEFAULT_P2POOL_PATH: &str = "p2pool/p2pool"; 58 | #[cfg(target_os = "linux")] 59 | #[cfg(not(feature = "distro"))] 60 | pub const DEFAULT_XMRIG_PATH: &str = "xmrig/xmrig"; 61 | #[cfg(target_os = "linux")] 62 | #[cfg(not(feature = "distro"))] 63 | pub const DEFAULT_XMRIG_PROXY_PATH: &str = "xmrig-proxy/xmrig-proxy"; 64 | #[cfg(target_os = "linux")] 65 | #[cfg(not(feature = "distro"))] 66 | pub const DEFAULT_NODE_PATH: &str = "node/monerod"; 67 | #[cfg(target_os = "linux")] 68 | #[cfg(feature = "distro")] 69 | pub const DEFAULT_P2POOL_PATH: &str = "/usr/bin/p2pool"; 70 | #[cfg(target_os = "linux")] 71 | #[cfg(feature = "distro")] 72 | pub const DEFAULT_XMRIG_PATH: &str = "/usr/bin/xmrig"; 73 | #[cfg(target_os = "linux")] 74 | #[cfg(feature = "distro")] 75 | pub const DEFAULT_XMRIG_PROXY_PATH: &str = "/usr/bin/xmrig-proxy"; 76 | #[cfg(target_os = "linux")] 77 | #[cfg(feature = "distro")] 78 | pub const DEFAULT_NODE_PATH: &str = "/usr/bin/monerod"; 79 | -------------------------------------------------------------------------------- /src/disk/errors.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use super::*; 19 | //---------------------------------------------------------------------------------------------------- Custom Error [TomlError] 20 | #[derive(Debug)] 21 | pub enum TomlError { 22 | Io(std::io::Error), 23 | Path(String), 24 | Serialize(toml::ser::Error), 25 | Deserialize(toml::de::Error), 26 | Merge(figment::Error), 27 | Format(std::fmt::Error), 28 | Parse(&'static str), 29 | } 30 | 31 | impl Display for TomlError { 32 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 33 | use TomlError::*; 34 | match self { 35 | Io(err) => write!(f, "{}: IO | {}", ERROR, err), 36 | Path(err) => write!(f, "{}: Path | {}", ERROR, err), 37 | Serialize(err) => write!(f, "{}: Serialize | {}", ERROR, err), 38 | Deserialize(err) => write!(f, "{}: Deserialize | {}", ERROR, err), 39 | Merge(err) => write!(f, "{}: Merge | {}", ERROR, err), 40 | Format(err) => write!(f, "{}: Format | {}", ERROR, err), 41 | Parse(err) => write!(f, "{}: Parse | {}", ERROR, err), 42 | } 43 | } 44 | } 45 | 46 | impl From for TomlError { 47 | fn from(err: std::io::Error) -> Self { 48 | TomlError::Io(err) 49 | } 50 | } 51 | 52 | impl From for TomlError { 53 | fn from(err: std::fmt::Error) -> Self { 54 | TomlError::Format(err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/disk/mod.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // This handles reading/writing the disk files: 19 | // - [state.toml] -> [App] state 20 | // - [nodes.toml] -> [Manual Nodes] list 21 | // The TOML format is used. This struct hierarchy 22 | // directly translates into the TOML parser: 23 | // State/ 24 | // ├─ Gupax/ 25 | // │ ├─ ... 26 | // ├─ P2pool/ 27 | // │ ├─ ... 28 | // ├─ Xmrig/ 29 | // │ ├─ ... 30 | // ├─ Version/ 31 | // ├─ ... 32 | 33 | use crate::disk::consts::*; 34 | use crate::{app::Tab, components::gupax::Ratio, constants::*, human::*, macros::*, xmr::*}; 35 | use figment::Figment; 36 | use figment::providers::{Format, Toml}; 37 | use log::*; 38 | use serde::{Deserialize, Serialize}; 39 | #[cfg(target_family = "unix")] 40 | use std::os::unix::fs::PermissionsExt; 41 | use std::path::Path; 42 | use std::{ 43 | fmt::Display, 44 | fmt::Write, 45 | fs, 46 | path::PathBuf, 47 | result::Result, 48 | sync::{Arc, Mutex}, 49 | }; 50 | 51 | use self::errors::TomlError; 52 | 53 | pub mod consts; 54 | pub mod errors; 55 | pub mod gupax_p2pool_api; 56 | pub mod node; 57 | pub mod pool; 58 | pub mod state; 59 | pub mod status; 60 | pub mod tests; 61 | //---------------------------------------------------------------------------------------------------- General functions for all [File]'s 62 | // get_file_path() | Return absolute path to OS data path + filename 63 | // read_to_string() | Convert the file at a given path into a [String] 64 | // create_new() | Write a default TOML Struct into the appropriate file (in OS data path) 65 | // into_absolute_path() | Convert relative -> absolute path 66 | 67 | pub fn get_gupax_data_path() -> Result { 68 | // Get OS data folder 69 | // Linux | $XDG_DATA_HOME or $HOME/.local/share/gupaxx | /home/alice/.local/state/gupaxx 70 | // macOS | $HOME/Library/Application Support/Gupaxx | /Users/Alice/Library/Application Support/Gupaxx 71 | // Windows | {FOLDERID_RoamingAppData}\Gupaxx | C:\Users\Alice\AppData\Roaming\Gupaxx 72 | match dirs::data_dir() { 73 | Some(mut path) => { 74 | path.push(DIRECTORY); 75 | info!("OS | Data path ... {}", path.display()); 76 | create_gupax_dir(&path)?; 77 | let mut gupax_p2pool_dir = path.clone(); 78 | gupax_p2pool_dir.push(GUPAX_P2POOL_API_DIRECTORY); 79 | create_gupax_p2pool_dir(&gupax_p2pool_dir)?; 80 | Ok(path) 81 | } 82 | None => { 83 | error!("OS | Data path ... FAIL"); 84 | Err(TomlError::Path(PATH_ERROR.to_string())) 85 | } 86 | } 87 | } 88 | #[cfg(target_family = "unix")] 89 | pub fn set_unix_750_perms(path: &PathBuf) -> Result<(), TomlError> { 90 | match fs::set_permissions(path, fs::Permissions::from_mode(0o750)) { 91 | Ok(_) => { 92 | info!( 93 | "OS | Unix 750 permissions on path [{}] ... OK", 94 | path.display() 95 | ); 96 | Ok(()) 97 | } 98 | Err(e) => { 99 | error!( 100 | "OS | Unix 750 permissions on path [{}] ... FAIL ... {}", 101 | path.display(), 102 | e 103 | ); 104 | Err(TomlError::Io(e)) 105 | } 106 | } 107 | } 108 | 109 | pub fn get_gupax_p2pool_path(os_data_path: &Path) -> PathBuf { 110 | let mut gupax_p2pool_dir = os_data_path.to_path_buf(); 111 | gupax_p2pool_dir.push(GUPAX_P2POOL_API_DIRECTORY); 112 | gupax_p2pool_dir 113 | } 114 | 115 | pub fn create_gupax_dir(path: &PathBuf) -> Result<(), TomlError> { 116 | // Create Gupax directory 117 | match fs::create_dir_all(path) { 118 | Ok(_) => info!("OS | Create data path ... OK"), 119 | Err(e) => { 120 | error!("OS | Create data path ... FAIL ... {}", e); 121 | return Err(TomlError::Io(e)); 122 | } 123 | } 124 | #[cfg(target_os = "windows")] 125 | return Ok(()); 126 | #[cfg(target_family = "unix")] 127 | set_unix_750_perms(path) 128 | } 129 | 130 | pub fn create_gupax_p2pool_dir(path: &PathBuf) -> Result<(), TomlError> { 131 | // Create Gupax directory 132 | match fs::create_dir_all(path) { 133 | Ok(_) => { 134 | info!( 135 | "OS | Create Gupax-P2Pool API path [{}] ... OK", 136 | path.display() 137 | ); 138 | Ok(()) 139 | } 140 | Err(e) => { 141 | error!( 142 | "OS | Create Gupax-P2Pool API path [{}] ... FAIL ... {}", 143 | path.display(), 144 | e 145 | ); 146 | Err(TomlError::Io(e)) 147 | } 148 | } 149 | } 150 | 151 | // Convert a [File] path to a [String] 152 | pub fn read_to_string(file: File, path: &PathBuf) -> Result { 153 | match fs::read_to_string(path) { 154 | Ok(string) => { 155 | info!("{:?} | Read ... OK", file); 156 | Ok(string) 157 | } 158 | Err(err) => { 159 | warn!("{:?} | Read ... FAIL", file); 160 | Err(TomlError::Io(err)) 161 | } 162 | } 163 | } 164 | 165 | // Write str to console with [info!] surrounded by "---" 166 | pub fn print_dash(toml: &str) { 167 | info!("{}", HORIZONTAL); 168 | for i in toml.lines() { 169 | info!("{}", i); 170 | } 171 | info!("{}", HORIZONTAL); 172 | } 173 | 174 | // Turn relative paths into absolute paths 175 | pub fn into_absolute_path(path: String) -> Result { 176 | let path = PathBuf::from(path); 177 | if path.is_relative() { 178 | let mut dir = std::env::current_exe()?; 179 | dir.pop(); 180 | dir.push(path); 181 | Ok(dir) 182 | } else { 183 | Ok(path) 184 | } 185 | } 186 | 187 | //---------------------------------------------------------------------------------------------------- [File] Enum (for matching which file) 188 | #[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)] 189 | pub enum File { 190 | // State files 191 | State, // state.toml | Gupax state 192 | Node, // node.toml | P2Pool manual node selector 193 | Pool, // pool.toml | XMRig manual pool selector 194 | 195 | // Gupax-P2Pool API 196 | Log, // log | Raw log lines of P2Pool payouts received 197 | Payout, // payout | Single [u64] representing total payouts 198 | Xmr, // xmr | Single [u64] representing total XMR mined in atomic units 199 | } 200 | -------------------------------------------------------------------------------- /src/disk/node.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::{app::panels::middle::common::list_poolnode::PoolNode, disk::*}; 19 | use serde::{Deserialize, Serialize}; 20 | //---------------------------------------------------------------------------------------------------- [Node] Impl 21 | impl Node { 22 | pub fn localhost() -> PoolNode { 23 | PoolNode::Node(Self { 24 | ip: "localhost".to_string(), 25 | rpc: "18081".to_string(), 26 | zmq: "18083".to_string(), 27 | }) 28 | } 29 | 30 | pub fn new_vec() -> Vec<(String, PoolNode)> { 31 | vec![("Local Monero Node".to_string(), Self::localhost())] 32 | } 33 | 34 | pub fn new_tuple() -> (String, PoolNode) { 35 | ("Local Monero Node".to_string(), Self::localhost()) 36 | } 37 | 38 | // Convert [String] to [Node] Vec 39 | pub fn from_str_to_vec(string: &str) -> Result, TomlError> { 40 | let nodes: toml::map::Map = match toml::de::from_str(string) { 41 | Ok(map) => { 42 | info!("Node | Parse ... OK"); 43 | map 44 | } 45 | Err(err) => { 46 | error!("Node | String parse ... FAIL ... {}", err); 47 | return Err(TomlError::Deserialize(err)); 48 | } 49 | }; 50 | let size = nodes.keys().len(); 51 | let mut vec = Vec::with_capacity(size); 52 | for (key, values) in nodes.iter() { 53 | let ip = match values.get("ip") { 54 | Some(ip) => match ip.as_str() { 55 | Some(ip) => ip.to_string(), 56 | None => { 57 | error!("Node | [None] at [ip] parse"); 58 | return Err(TomlError::Parse("[None] at [ip] parse")); 59 | } 60 | }, 61 | None => { 62 | error!("Node | [None] at [ip] parse"); 63 | return Err(TomlError::Parse("[None] at [ip] parse")); 64 | } 65 | }; 66 | let rpc = match values.get("rpc") { 67 | Some(rpc) => match rpc.as_str() { 68 | Some(rpc) => rpc.to_string(), 69 | None => { 70 | error!("Node | [None] at [rpc] parse"); 71 | return Err(TomlError::Parse("[None] at [rpc] parse")); 72 | } 73 | }, 74 | None => { 75 | error!("Node | [None] at [rpc] parse"); 76 | return Err(TomlError::Parse("[None] at [rpc] parse")); 77 | } 78 | }; 79 | let zmq = match values.get("zmq") { 80 | Some(zmq) => match zmq.as_str() { 81 | Some(zmq) => zmq.to_string(), 82 | None => { 83 | error!("Node | [None] at [zmq] parse"); 84 | return Err(TomlError::Parse("[None] at [zmq] parse")); 85 | } 86 | }, 87 | None => { 88 | error!("Node | [None] at [zmq] parse"); 89 | return Err(TomlError::Parse("[None] at [zmq] parse")); 90 | } 91 | }; 92 | let node = Node { ip, rpc, zmq }; 93 | vec.push((key.clone(), PoolNode::Node(node))); 94 | } 95 | Ok(vec) 96 | } 97 | 98 | // Convert [Vec<(String, Self)>] into [String] 99 | // that can be written as a proper TOML file 100 | pub fn to_string(vec: &[(String, PoolNode)]) -> Result { 101 | let mut toml = String::new(); 102 | for (key, value) in vec.iter() { 103 | write!( 104 | toml, 105 | "[\'{}\']\nip = {:#?}\nrpc = {:#?}\nzmq = {:#?}\n\n", 106 | key, 107 | value.ip(), 108 | value.port(), 109 | value.custom(), 110 | )?; 111 | } 112 | Ok(toml) 113 | } 114 | 115 | // Combination of multiple functions: 116 | // 1. Attempt to read file from path into [String] 117 | // |_ Create a default file if not found 118 | // 2. Deserialize [String] into a proper [Struct] 119 | // |_ Attempt to merge if deserialization fails 120 | pub fn get(path: &PathBuf) -> Result, TomlError> { 121 | // Read 122 | let file = File::Node; 123 | let string = match read_to_string(file, path) { 124 | Ok(string) => string, 125 | // Create 126 | _ => { 127 | Self::create_new(path)?; 128 | read_to_string(file, path)? 129 | } 130 | }; 131 | // Deserialize, attempt merge if failed 132 | Self::from_str_to_vec(&string) 133 | } 134 | 135 | // Completely overwrite current [node.toml] 136 | // with a new default version, and return [Vec]. 137 | pub fn create_new(path: &PathBuf) -> Result, TomlError> { 138 | info!("Node | Creating new default..."); 139 | let new = Self::new_vec(); 140 | let string = Self::to_string(&Self::new_vec())?; 141 | fs::write(path, string)?; 142 | info!("Node | Write ... OK"); 143 | Ok(new) 144 | } 145 | 146 | // Save [Node] onto disk file [node.toml] 147 | pub fn save(vec: &[(String, PoolNode)], path: &PathBuf) -> Result<(), TomlError> { 148 | info!("Node | Saving to disk ... [{}]", path.display()); 149 | let string = Self::to_string(vec)?; 150 | match fs::write(path, string) { 151 | Ok(_) => { 152 | info!("Node | Save ... OK"); 153 | Ok(()) 154 | } 155 | Err(err) => { 156 | error!("Node | Couldn't overwrite file"); 157 | Err(TomlError::Io(err)) 158 | } 159 | } 160 | } 161 | 162 | // pub fn merge(old: &String) -> Result { 163 | // info!("Node | Starting TOML merge..."); 164 | // let default = match toml::ser::to_string(&Self::new()) { 165 | // Ok(string) => { info!("Node | Default TOML parse ... OK"); string }, 166 | // Err(err) => { error!("Node | Couldn't parse default TOML into string"); return Err(TomlError::Serialize(err)) }, 167 | // }; 168 | // let mut new: Self = match Figment::new().merge(Toml::string(&old)).merge(Toml::string(&default)).extract() { 169 | // Ok(new) => { info!("Node | TOML merge ... OK"); new }, 170 | // Err(err) => { error!("Node | Couldn't merge default + old TOML"); return Err(TomlError::Merge(err)) }, 171 | // }; 172 | // // Attempt save 173 | // Self::save(&mut new)?; 174 | // Ok(new) 175 | // } 176 | } 177 | //---------------------------------------------------------------------------------------------------- [Node] Struct 178 | #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] 179 | pub struct Node { 180 | pub ip: String, 181 | pub rpc: String, 182 | pub zmq: String, 183 | } 184 | -------------------------------------------------------------------------------- /src/disk/pool.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::app::panels::middle::common::list_poolnode::PoolNode; 19 | 20 | use super::*; 21 | //---------------------------------------------------------------------------------------------------- [Pool] impl 22 | impl Pool { 23 | pub fn p2pool() -> PoolNode { 24 | PoolNode::Pool(Self { 25 | rig: GUPAX_VERSION_UNDERSCORE.to_string(), 26 | ip: "localhost".to_string(), 27 | port: "3333".to_string(), 28 | }) 29 | } 30 | 31 | pub fn new_vec() -> Vec<(String, PoolNode)> { 32 | vec![("Local P2Pool".to_string(), Self::p2pool())] 33 | } 34 | 35 | pub fn new_tuple() -> (String, PoolNode) { 36 | ("Local P2Pool".to_string(), Self::p2pool()) 37 | } 38 | 39 | pub fn from_str_to_vec(string: &str) -> Result, TomlError> { 40 | let pools: toml::map::Map = match toml::de::from_str(string) { 41 | Ok(map) => { 42 | info!("Pool | Parse ... OK"); 43 | map 44 | } 45 | Err(err) => { 46 | error!("Pool | String parse ... FAIL ... {}", err); 47 | return Err(TomlError::Deserialize(err)); 48 | } 49 | }; 50 | let size = pools.keys().len(); 51 | let mut vec = Vec::with_capacity(size); 52 | // We have to do [.as_str()] -> [.to_string()] to get rid of the \"...\" that gets added on. 53 | for (key, values) in pools.iter() { 54 | let rig = match values.get("rig") { 55 | Some(rig) => match rig.as_str() { 56 | Some(rig) => rig.to_string(), 57 | None => { 58 | error!("Pool | [None] at [rig] parse"); 59 | return Err(TomlError::Parse("[None] at [rig] parse")); 60 | } 61 | }, 62 | None => { 63 | error!("Pool | [None] at [rig] parse"); 64 | return Err(TomlError::Parse("[None] at [rig] parse")); 65 | } 66 | }; 67 | let ip = match values.get("ip") { 68 | Some(ip) => match ip.as_str() { 69 | Some(ip) => ip.to_string(), 70 | None => { 71 | error!("Pool | [None] at [ip] parse"); 72 | return Err(TomlError::Parse("[None] at [ip] parse")); 73 | } 74 | }, 75 | None => { 76 | error!("Pool | [None] at [ip] parse"); 77 | return Err(TomlError::Parse("[None] at [ip] parse")); 78 | } 79 | }; 80 | let port = match values.get("port") { 81 | Some(port) => match port.as_str() { 82 | Some(port) => port.to_string(), 83 | None => { 84 | error!("Pool | [None] at [port] parse"); 85 | return Err(TomlError::Parse("[None] at [port] parse")); 86 | } 87 | }, 88 | None => { 89 | error!("Pool | [None] at [port] parse"); 90 | return Err(TomlError::Parse("[None] at [port] parse")); 91 | } 92 | }; 93 | let pool = Pool { rig, ip, port }; 94 | vec.push((key.clone(), PoolNode::Pool(pool))); 95 | } 96 | Ok(vec) 97 | } 98 | 99 | pub fn to_string(vec: &[(String, PoolNode)]) -> Result { 100 | let mut toml = String::new(); 101 | for (key, value) in vec.iter() { 102 | write!( 103 | toml, 104 | "[\'{}\']\nrig = {:#?}\nip = {:#?}\nport = {:#?}\n\n", 105 | key, 106 | value.custom(), 107 | value.ip(), 108 | value.port(), 109 | )?; 110 | } 111 | Ok(toml) 112 | } 113 | 114 | pub fn get(path: &PathBuf) -> Result, TomlError> { 115 | // Read 116 | let file = File::Pool; 117 | let string = match read_to_string(file, path) { 118 | Ok(string) => string, 119 | // Create 120 | _ => { 121 | Self::create_new(path)?; 122 | read_to_string(file, path)? 123 | } 124 | }; 125 | // Deserialize 126 | Self::from_str_to_vec(&string) 127 | } 128 | 129 | pub fn create_new(path: &PathBuf) -> Result, TomlError> { 130 | info!("Pool | Creating new default..."); 131 | let new = Self::new_vec(); 132 | let string = Self::to_string(&Self::new_vec())?; 133 | fs::write(path, string)?; 134 | info!("Pool | Write ... OK"); 135 | Ok(new) 136 | } 137 | 138 | pub fn save(vec: &[(String, PoolNode)], path: &PathBuf) -> Result<(), TomlError> { 139 | info!("Pool | Saving to disk ... [{}]", path.display()); 140 | let string = Self::to_string(vec)?; 141 | match fs::write(path, string) { 142 | Ok(_) => { 143 | info!("Pool | Save ... OK"); 144 | Ok(()) 145 | } 146 | Err(err) => { 147 | error!("Pool | Couldn't overwrite file"); 148 | Err(TomlError::Io(err)) 149 | } 150 | } 151 | } 152 | } 153 | //---------------------------------------------------------------------------------------------------- [Pool] Struct 154 | #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] 155 | pub struct Pool { 156 | pub rig: String, 157 | pub ip: String, 158 | pub port: String, 159 | } 160 | -------------------------------------------------------------------------------- /src/disk/status.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use derive_more::derive::Display; 19 | use strum::{EnumCount, EnumIter}; 20 | 21 | use super::*; 22 | //---------------------------------------------------------------------------------------------------- [Submenu] enum for [Status] tab 23 | #[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)] 24 | pub enum Submenu { 25 | Processes, 26 | P2pool, 27 | Benchmarks, 28 | } 29 | 30 | impl Default for Submenu { 31 | fn default() -> Self { 32 | Self::Processes 33 | } 34 | } 35 | 36 | impl Display for Submenu { 37 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 38 | use Submenu::*; 39 | match self { 40 | P2pool => write!(f, "P2Pool"), 41 | _ => write!(f, "{:?}", self), 42 | } 43 | } 44 | } 45 | 46 | //---------------------------------------------------------------------------------------------------- [PayoutView] enum for [Status/P2Pool] tab 47 | // The enum buttons for selecting which "view" to sort the payout log in. 48 | #[derive( 49 | Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize, Display, EnumIter, EnumCount, 50 | )] 51 | pub enum PayoutView { 52 | Latest, // Shows the most recent logs first 53 | Oldest, // Shows the oldest logs first 54 | Biggest, // Shows highest to lowest payouts 55 | Smallest, // Shows lowest to highest payouts 56 | } 57 | 58 | impl PayoutView { 59 | pub const fn msg_help(&self) -> &str { 60 | match self { 61 | Self::Latest => STATUS_SUBMENU_LATEST, 62 | Self::Oldest => STATUS_SUBMENU_OLDEST, 63 | Self::Biggest => STATUS_SUBMENU_SMALLEST, 64 | Self::Smallest => STATUS_SUBMENU_BIGGEST, 65 | } 66 | } 67 | } 68 | 69 | impl PayoutView { 70 | fn new() -> Self { 71 | Self::Latest 72 | } 73 | } 74 | 75 | impl Default for PayoutView { 76 | fn default() -> Self { 77 | Self::new() 78 | } 79 | } 80 | 81 | //---------------------------------------------------------------------------------------------------- [Hash] enum for [Status/P2Pool] 82 | #[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)] 83 | #[allow(clippy::enum_variant_names)] 84 | pub enum Hash { 85 | Hash, 86 | Kilo, 87 | Mega, 88 | Giga, 89 | } 90 | 91 | impl Default for Hash { 92 | fn default() -> Self { 93 | Self::Hash 94 | } 95 | } 96 | 97 | impl Hash { 98 | pub fn convert_to_hash(f: f64, from: Self) -> f64 { 99 | match from { 100 | Self::Hash => f, 101 | Self::Kilo => f * 1_000.0, 102 | Self::Mega => f * 1_000_000.0, 103 | Self::Giga => f * 1_000_000_000.0, 104 | } 105 | } 106 | #[cfg(test)] 107 | pub fn convert(f: f64, og: Self, new: Self) -> f64 { 108 | match og { 109 | Self::Hash => match new { 110 | Self::Hash => f, 111 | Self::Kilo => f / 1_000.0, 112 | Self::Mega => f / 1_000_000.0, 113 | Self::Giga => f / 1_000_000_000.0, 114 | }, 115 | Self::Kilo => match new { 116 | Self::Hash => f * 1_000.0, 117 | Self::Kilo => f, 118 | Self::Mega => f / 1_000.0, 119 | Self::Giga => f / 1_000_000.0, 120 | }, 121 | Self::Mega => match new { 122 | Self::Hash => f * 1_000_000.0, 123 | Self::Kilo => f * 1_000.0, 124 | Self::Mega => f, 125 | Self::Giga => f / 1_000.0, 126 | }, 127 | Self::Giga => match new { 128 | Self::Hash => f * 1_000_000_000.0, 129 | Self::Kilo => f * 1_000_000.0, 130 | Self::Mega => f * 1_000.0, 131 | Self::Giga => f, 132 | }, 133 | } 134 | } 135 | } 136 | 137 | impl Display for Hash { 138 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 139 | match self { 140 | Hash::Hash => write!(f, "H/s"), 141 | Hash::Kilo => write!(f, "KH/s"), 142 | Hash::Mega => write!(f, "MH/s"), 143 | Hash::Giga => write!(f, "GH/s"), 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/helper/xrig/mod.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use crate::XMRIG_API_CONFIG_ENDPOINT; 19 | use crate::XMRIG_API_SUMMARY_ENDPOINT; 20 | use crate::helper::Pool; 21 | use anyhow::Result; 22 | use anyhow::anyhow; 23 | use log::info; 24 | use reqwest::header::AUTHORIZATION; 25 | use reqwest_middleware::ClientWithMiddleware as Client; 26 | use serde::Deserialize; 27 | use serde::Serialize; 28 | use serde_json::Value; 29 | use xmrig::ImgXmrig; 30 | use xmrig_proxy::ImgProxy; 31 | 32 | pub mod xmrig; 33 | pub mod xmrig_proxy; 34 | 35 | // update config of xmrig or xmrig-proxy 36 | pub async fn update_xmrig_config( 37 | client: &Client, 38 | api_uri: &str, 39 | token: &str, 40 | node: &Pool, 41 | address: &str, 42 | rig: &str, 43 | ) -> Result<()> { 44 | // get config 45 | let request = client 46 | .get(api_uri) 47 | .header(AUTHORIZATION, ["Bearer ", token].concat()); 48 | let mut config = request.send().await?.json::().await?; 49 | // modify node configuration 50 | let uri = [node.url(), ":".to_string(), node.port()].concat(); 51 | info!( 52 | "replace xmrig from api url {api_uri} config with node {}", 53 | uri 54 | ); 55 | *config 56 | .pointer_mut("/pools/0/url") 57 | .ok_or_else(|| anyhow!("pools/0/url does not exist in xmrig config"))? = uri.into(); 58 | *config 59 | .pointer_mut("/pools/0/user") 60 | .ok_or_else(|| anyhow!("pools/0/user does not exist in xmrig config"))? = node 61 | .user(&address.chars().take(8).collect::()) 62 | .into(); 63 | *config 64 | .pointer_mut("/pools/0/rig-id") 65 | .ok_or_else(|| anyhow!("pools/0/rig-id does not exist in xmrig config"))? = rig.into(); 66 | *config 67 | .pointer_mut("/pools/0/tls") 68 | .ok_or_else(|| anyhow!("pools/0/tls does not exist in xmrig config"))? = node.tls().into(); 69 | *config 70 | .pointer_mut("/pools/0/keepalive") 71 | .ok_or_else(|| anyhow!("pools/0/keepalive does not exist in xmrig config"))? = 72 | node.keepalive().into(); 73 | // send new config 74 | client 75 | .put(api_uri) 76 | .header("Authorization", ["Bearer ", token].concat()) 77 | .header("Content-Type", "application/json") 78 | .timeout(std::time::Duration::from_secs(5)) 79 | .body(config.to_string()) 80 | .send() 81 | .await?; 82 | anyhow::Ok(()) 83 | } 84 | #[derive(Debug, Serialize, Deserialize, Clone, Copy)] 85 | struct Hashrate { 86 | total: [Option; 3], 87 | } 88 | 89 | /// Take the runtime port. Even if settings were changed, the port will be the current one. 90 | /// to get config url, true. False for summary 91 | /// provide either xmrig_img or proxy_img 92 | pub fn current_api_url_xrig( 93 | config: bool, 94 | xmrig_img: Option<&ImgXmrig>, 95 | proxy_img: Option<&ImgProxy>, 96 | ) -> String { 97 | let (port, endpoint) = if let Some(xmrig) = xmrig_img { 98 | if config { 99 | (xmrig.api_port, XMRIG_API_CONFIG_ENDPOINT) 100 | } else { 101 | (xmrig.api_port, XMRIG_API_SUMMARY_ENDPOINT) 102 | } 103 | } else if let Some(proxy) = proxy_img { 104 | if config { 105 | (proxy.api_port, XMRIG_API_CONFIG_ENDPOINT) 106 | } else { 107 | (proxy.api_port, XMRIG_API_SUMMARY_ENDPOINT) 108 | } 109 | } else { 110 | panic!("neither xmrig_img or proxy_img is some"); 111 | }; 112 | format!("http://127.0.0.1:{port}/{endpoint}") 113 | } 114 | -------------------------------------------------------------------------------- /src/helper/xvb/priv_stats.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use std::{ 19 | sync::{Arc, Mutex}, 20 | time::Duration, 21 | }; 22 | 23 | use anyhow::bail; 24 | use log::{debug, error, info, warn}; 25 | use reqwest::StatusCode; 26 | use reqwest_middleware::ClientWithMiddleware as Client; 27 | use serde::Deserialize; 28 | 29 | use crate::{ 30 | XVB_ROUND_DONOR_MEGA_MIN_HR, XVB_ROUND_DONOR_MIN_HR, XVB_ROUND_DONOR_VIP_MIN_HR, 31 | XVB_ROUND_DONOR_WHALE_MIN_HR, disk::state::XvbMode, 32 | }; 33 | use crate::{ 34 | XVB_URL, 35 | disk::state::ManualDonationLevel, 36 | helper::{Process, ProcessName, ProcessState, xvb::output_console}, 37 | }; 38 | 39 | use super::{PubXvbApi, nodes::Pool, rounds::XvbRound}; 40 | 41 | #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)] 42 | pub enum RuntimeMode { 43 | #[default] 44 | Auto, 45 | ManualXvb, 46 | ManualP2pool, 47 | Hero, 48 | ManualDonationLevel, 49 | } 50 | 51 | #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)] 52 | pub enum RuntimeDonationLevel { 53 | #[default] 54 | Donor, 55 | DonorVIP, 56 | DonorWhale, 57 | DonorMega, 58 | } 59 | 60 | impl RuntimeDonationLevel { 61 | pub fn get_hashrate(&self) -> f32 { 62 | match &self { 63 | Self::Donor => XVB_ROUND_DONOR_MIN_HR as f32, 64 | Self::DonorVIP => XVB_ROUND_DONOR_VIP_MIN_HR as f32, 65 | Self::DonorWhale => XVB_ROUND_DONOR_WHALE_MIN_HR as f32, 66 | Self::DonorMega => XVB_ROUND_DONOR_MEGA_MIN_HR as f32, 67 | } 68 | } 69 | } 70 | 71 | #[derive(Debug, Clone, Default, Deserialize)] 72 | pub struct XvbPrivStats { 73 | pub fails: u8, 74 | pub donor_1hr_avg: f32, 75 | pub donor_24hr_avg: f32, 76 | #[serde(skip)] 77 | pub win_current: bool, 78 | #[serde(skip)] 79 | pub round_participate: Option, 80 | #[serde(skip)] 81 | pub pool: Pool, 82 | #[serde(skip)] 83 | // it is the time remaining before switching from P2pool to XvB or XvB to P2ool. 84 | // it is not the time remaining of the algo, even if it could be the same if never mining on XvB. 85 | pub time_switch_pool: u32, 86 | #[serde(skip)] 87 | pub msg_indicator: String, 88 | #[serde(skip)] 89 | // so the hero mode can change between two decision of algorithm without restarting XvB. 90 | pub runtime_mode: RuntimeMode, 91 | #[serde(skip)] 92 | pub runtime_manual_amount: f64, 93 | #[serde(skip)] 94 | pub runtime_manual_donation_level: RuntimeDonationLevel, 95 | } 96 | 97 | impl XvbPrivStats { 98 | pub async fn request_api(client: &Client, address: &str, token: &str) -> anyhow::Result { 99 | let resp = client 100 | .get( 101 | [ 102 | XVB_URL, 103 | "/cgi-bin/p2pool_bonus_history_gupaxx_api.cgi?address=", 104 | address, 105 | "&token=", 106 | token, 107 | ] 108 | .concat(), 109 | ) 110 | .timeout(Duration::from_secs(10)) 111 | .send() 112 | .await?; 113 | match resp.status() { 114 | StatusCode::OK => match resp.json::().await { 115 | Ok(s) => Ok(s), 116 | Err(err) => { 117 | error!( 118 | "XvB Watchdog | Data provided from private API is not deserializ-able.Error: {}", 119 | err 120 | ); 121 | bail!( 122 | "Data provided from private API is not deserializ-able.Error: {}", 123 | err 124 | ); 125 | } 126 | }, 127 | StatusCode::UNPROCESSABLE_ENTITY => { 128 | bail!("the token is invalid for this xmr address.") 129 | } 130 | _ => bail!("The status of the response is not expected"), 131 | } 132 | } 133 | pub async fn update_stats( 134 | client: &Client, 135 | address: &str, 136 | token: &str, 137 | pub_api: &Arc>, 138 | gui_api: &Arc>, 139 | process: &Arc>, 140 | ) { 141 | match XvbPrivStats::request_api(client, address, token).await { 142 | Ok(new_data) => { 143 | debug!("XvB Watchdog | HTTP API request OK"); 144 | pub_api.lock().unwrap().stats_priv = new_data; 145 | let previously_failed = process.lock().unwrap().state == ProcessState::Failed; 146 | if previously_failed { 147 | info!("XvB Watchdog | Public stats are working again"); 148 | output_console( 149 | &mut gui_api.lock().unwrap().output, 150 | "requests for public API are now working", 151 | ProcessName::Xvb, 152 | ); 153 | process.lock().unwrap().state = ProcessState::Syncing; 154 | } 155 | // if last request failed, we are now ready to show stats again and maybe be alive next loop. 156 | } 157 | Err(err) => { 158 | warn!( 159 | "XvB Watchdog | Could not send HTTP private API request to: {}\n:{}", 160 | XVB_URL, err 161 | ); 162 | if process.lock().unwrap().state != ProcessState::Failed { 163 | output_console( 164 | &mut gui_api.lock().unwrap().output, 165 | "Failure to retrieve private stats \nWill retry shortly...", 166 | ProcessName::Xvb, 167 | ); 168 | } 169 | // we stop the algo (will be stopped by the check status on next loop) because we can't make the rest work without public stats. (winner in xvb private stats). 170 | output_console( 171 | &mut gui_api.lock().unwrap().output, 172 | "request to get private API failed", 173 | ProcessName::Xvb, 174 | ); 175 | process.lock().unwrap().state = ProcessState::Failed; 176 | } 177 | } 178 | } 179 | } 180 | 181 | impl From for RuntimeMode { 182 | fn from(mode: XvbMode) -> Self { 183 | match mode { 184 | XvbMode::Auto => Self::Auto, 185 | XvbMode::ManualXvb => Self::ManualXvb, 186 | XvbMode::ManualP2pool => Self::ManualP2pool, 187 | XvbMode::Hero => Self::Hero, 188 | XvbMode::ManualDonationLevel => Self::ManualDonationLevel, 189 | } 190 | } 191 | } 192 | 193 | impl From for RuntimeDonationLevel { 194 | fn from(level: ManualDonationLevel) -> Self { 195 | match level { 196 | ManualDonationLevel::Donor => RuntimeDonationLevel::Donor, 197 | ManualDonationLevel::DonorVIP => RuntimeDonationLevel::DonorVIP, 198 | ManualDonationLevel::DonorWhale => RuntimeDonationLevel::DonorWhale, 199 | ManualDonationLevel::DonorMega => RuntimeDonationLevel::DonorMega, 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/helper/xvb/public_stats.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use std::{ 19 | sync::{Arc, Mutex}, 20 | time::Duration, 21 | }; 22 | 23 | use log::{debug, info, warn}; 24 | use reqwest_middleware::ClientWithMiddleware as Client; 25 | use serde::Deserialize; 26 | use serde_this_or_that::as_u64; 27 | 28 | use crate::{ 29 | XVB_URL_PUBLIC_API, 30 | helper::{Process, ProcessName, ProcessState, xvb::output_console}, 31 | }; 32 | 33 | use super::{PubXvbApi, rounds::XvbRound}; 34 | 35 | #[allow(dead_code)] // because deserialize doesn't use all the fields 36 | #[derive(Debug, Clone, Default, Deserialize)] 37 | pub struct XvbPubStats { 38 | pub time_remain: u32, // remaining time of round in minutes 39 | pub bonus_hr: f64, 40 | pub donate_hr: f64, // donated hr from all donors 41 | pub donate_miners: u32, // numbers of donors 42 | pub donate_workers: u32, // numbers of workers from donors 43 | pub players: u32, 44 | pub players_round: u32, 45 | pub winner: String, 46 | pub share_effort: String, 47 | pub block_reward: String, 48 | pub round_type: XvbRound, 49 | #[serde(deserialize_with = "as_u64")] 50 | pub block_height: u64, 51 | pub block_hash: String, 52 | #[serde(deserialize_with = "as_u64")] 53 | pub roll_winner: u64, 54 | #[serde(deserialize_with = "as_u64")] 55 | pub roll_round: u64, 56 | pub reward_yearly: Vec, 57 | } 58 | impl XvbPubStats { 59 | #[inline] 60 | // Send an HTTP request to XvB's API, serialize it into [Self] and return it 61 | pub(in crate::helper) async fn request_api( 62 | client: &Client, 63 | ) -> std::result::Result { 64 | Ok(client 65 | .get(XVB_URL_PUBLIC_API) 66 | .timeout(Duration::from_secs(10)) 67 | .send() 68 | .await? 69 | .json::() 70 | .await?) 71 | } 72 | pub async fn update_stats( 73 | client: &Client, 74 | gui_api: &Arc>, 75 | pub_api: &Arc>, 76 | process: &Arc>, 77 | ) { 78 | debug!("XvB Watchdog | Attempting HTTP public API request..."); 79 | match XvbPubStats::request_api(client).await { 80 | Ok(new_data) => { 81 | debug!("XvB Watchdog | HTTP API request OK"); 82 | pub_api.lock().unwrap().stats_pub = new_data; 83 | let previously_failed = process.lock().unwrap().state == ProcessState::Failed; 84 | if previously_failed { 85 | info!("XvB Watchdog | Public stats are working again"); 86 | output_console( 87 | &mut gui_api.lock().unwrap().output, 88 | "requests for public API are now working", 89 | ProcessName::Xvb, 90 | ); 91 | process.lock().unwrap().state = ProcessState::Syncing; 92 | } 93 | } 94 | Err(err) => { 95 | warn!( 96 | "XvB Watchdog | Could not send HTTP API request to: {} even after multiples tries\n:{}", 97 | XVB_URL_PUBLIC_API, err 98 | ); 99 | // output the error to console 100 | // if error already present, no need to print it multiple times. 101 | output_console( 102 | &mut gui_api.lock().unwrap().output, 103 | &format!( 104 | "Failure to retrieve public stats from {}\nWill retry shortly...", 105 | XVB_URL_PUBLIC_API 106 | ), 107 | ProcessName::Xvb, 108 | ); 109 | // we stop the algo (will be stopped by the check status on next loop) because we can't make the rest work without public stats. (winner in xvb private stats). 110 | output_console( 111 | &mut gui_api.lock().unwrap().output, 112 | "request to get public API failed.\nYou won't have any public stats available but the algorithm can continue.", 113 | ProcessName::Xvb, 114 | ); 115 | pub_api.lock().unwrap().stats_pub = XvbPubStats::default(); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/helper/xvb/rounds.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | use std::sync::{Arc, Mutex}; 19 | 20 | use derive_more::Display; 21 | use serde::Deserialize; 22 | 23 | use crate::{ 24 | XVB_ROUND_DONOR_MEGA_MIN_HR, XVB_ROUND_DONOR_MIN_HR, XVB_ROUND_DONOR_VIP_MIN_HR, 25 | XVB_ROUND_DONOR_WHALE_MIN_HR, XVB_SIDE_MARGIN_1H, 26 | }; 27 | 28 | use super::PubXvbApi; 29 | #[derive(Debug, Clone, Default, Display, Deserialize, PartialEq)] 30 | pub enum XvbRound { 31 | #[default] 32 | #[display("VIP")] 33 | #[serde(alias = "vip")] 34 | Vip, 35 | #[serde(alias = "donor")] 36 | Donor, 37 | #[display("VIP Donor")] 38 | #[serde(alias = "donor_vip")] 39 | DonorVip, 40 | #[display("Whale Donor")] 41 | #[serde(alias = "donor_whale")] 42 | DonorWhale, 43 | #[display("Mega Donor")] 44 | #[serde(alias = "donor_mega")] 45 | DonorMega, 46 | } 47 | 48 | pub(crate) fn round_type(share: u32, pub_api: &Arc>) -> Option { 49 | if share > 0 { 50 | let stats_priv = &pub_api.lock().unwrap().stats_priv; 51 | match ( 52 | ((stats_priv.donor_1hr_avg * 1000.0) * XVB_SIDE_MARGIN_1H) as u32, 53 | (stats_priv.donor_24hr_avg * 1000.0) as u32, 54 | ) { 55 | x if x.0 >= XVB_ROUND_DONOR_MEGA_MIN_HR && x.1 >= XVB_ROUND_DONOR_MEGA_MIN_HR => { 56 | Some(XvbRound::DonorMega) 57 | } 58 | x if x.0 >= XVB_ROUND_DONOR_WHALE_MIN_HR && x.1 >= XVB_ROUND_DONOR_WHALE_MIN_HR => { 59 | Some(XvbRound::DonorWhale) 60 | } 61 | x if x.0 >= XVB_ROUND_DONOR_VIP_MIN_HR && x.1 >= XVB_ROUND_DONOR_VIP_MIN_HR => { 62 | Some(XvbRound::DonorVip) 63 | } 64 | x if x.0 >= XVB_ROUND_DONOR_MIN_HR && x.1 >= XVB_ROUND_DONOR_MIN_HR => { 65 | Some(XvbRound::Donor) 66 | } 67 | (_, _) => Some(XvbRound::Vip), 68 | } 69 | } else { 70 | None 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // Hide console in Windows 19 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 20 | 21 | // Only (windows|macos|linux) + (x64|arm64) are supported. 22 | #[cfg(not(target_pointer_width = "64"))] 23 | compile_error!("gupaxx is only compatible with 64-bit CPUs"); 24 | 25 | #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux",)))] 26 | compile_error!("gupaxx is only built for windows/macos/linux"); 27 | 28 | use crate::app::App; 29 | use crate::cli::Cli; 30 | //---------------------------------------------------------------------------------------------------- Imports 31 | use crate::constants::*; 32 | use crate::inits::{init_auto, init_logger, init_options}; 33 | use crate::miscs::clean_dir; 34 | use crate::utils::*; 35 | use clap::Parser; 36 | use egui::Vec2; 37 | use log::info; 38 | use log::warn; 39 | use std::time::Instant; 40 | 41 | mod app; 42 | mod cli; 43 | mod components; 44 | mod disk; 45 | mod helper; 46 | mod inits; 47 | mod miscs; 48 | mod utils; 49 | 50 | // Sudo (dummy values for Windows) 51 | #[cfg(target_family = "unix")] 52 | extern crate sudo as sudo_check; 53 | 54 | //---------------------------------------------------------------------------------------------------- Main [App] frame 55 | fn main() { 56 | let args = Cli::parse(); 57 | let now = Instant::now(); 58 | 59 | // Set custom panic hook. 60 | crate::panic::set_panic_hook(now); 61 | 62 | // Init logger. 63 | init_logger(now, args.logfile); 64 | let mut app = App::new(now, args); 65 | init_auto(&mut app); 66 | 67 | // Init GUI stuff. 68 | let selected_width = app.state.gupax.selected_width as f32; 69 | let selected_height = app.state.gupax.selected_height as f32; 70 | let initial_window_size = if selected_width > APP_MAX_WIDTH || selected_height > APP_MAX_HEIGHT 71 | { 72 | warn!( 73 | "App | Set width or height was greater than the maximum! Starting with the default resolution..." 74 | ); 75 | Some(Vec2::new(APP_DEFAULT_WIDTH, APP_DEFAULT_HEIGHT)) 76 | } else { 77 | Some(Vec2::new( 78 | app.state.gupax.selected_width as f32, 79 | app.state.gupax.selected_height as f32, 80 | )) 81 | }; 82 | let options = init_options(initial_window_size); 83 | 84 | // Gupax folder cleanup. 85 | match clean_dir() { 86 | Ok(_) => info!("Temporary folder cleanup ... OK"), 87 | Err(e) => warn!("Could not cleanup [gupax_tmp] folders: {}", e), 88 | } 89 | 90 | let resolution = Vec2::new(selected_width, selected_height); 91 | 92 | // Run Gupax. 93 | info!( 94 | "/*************************************/ Init ... OK /*************************************/" 95 | ); 96 | eframe::run_native( 97 | &app.name_version.clone(), 98 | options, 99 | Box::new(move |cc| { 100 | egui_extras::install_image_loaders(&cc.egui_ctx); 101 | Ok(Box::new(App::cc(cc, resolution, app))) 102 | }), 103 | ) 104 | .unwrap(); 105 | } 106 | -------------------------------------------------------------------------------- /src/miscs.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //---------------------------------------------------------------------------------------------------- Misc functions 19 | 20 | // Get absolute [Gupax] binary path 21 | use std::fmt::Write; 22 | use std::time::Duration; 23 | #[cold] 24 | #[inline(never)] 25 | pub fn get_exe() -> Result { 26 | match std::env::current_exe() { 27 | Ok(path) => Ok(path.display().to_string()), 28 | Err(err) => { 29 | error!("Couldn't get absolute Gupax PATH"); 30 | Err(err) 31 | } 32 | } 33 | } 34 | 35 | // Get absolute [Gupax] directory path 36 | #[cold] 37 | #[inline(never)] 38 | pub fn get_exe_dir() -> Result { 39 | match std::env::current_exe() { 40 | Ok(mut path) => { 41 | path.pop(); 42 | Ok(path.display().to_string()) 43 | } 44 | Err(err) => { 45 | error!("Couldn't get exe basepath PATH"); 46 | Err(err) 47 | } 48 | } 49 | } 50 | 51 | // Clean any [gupax_update_.*] directories 52 | // The trailing random bits must be exactly 10 alphanumeric characters 53 | #[cold] 54 | #[inline(never)] 55 | pub fn clean_dir() -> Result<(), anyhow::Error> { 56 | let regex = Regex::new("^gupaxx_update_[A-Za-z0-9]{10}$").unwrap(); 57 | for entry in std::fs::read_dir(get_exe_dir()?)? { 58 | let entry = entry?; 59 | if !entry.path().is_dir() { 60 | continue; 61 | } 62 | if Regex::is_match( 63 | ®ex, 64 | entry 65 | .file_name() 66 | .to_str() 67 | .ok_or_else(|| anyhow::Error::msg("Basename failed"))?, 68 | ) { 69 | let path = entry.path(); 70 | match std::fs::remove_dir_all(&path) { 71 | Ok(_) => info!("Remove [{}] ... OK", path.display()), 72 | Err(e) => warn!("Remove [{}] ... FAIL ... {}", path.display(), e), 73 | } 74 | } 75 | } 76 | Ok(()) 77 | } 78 | 79 | // Print disk files to console 80 | #[cold] 81 | #[inline(never)] 82 | pub fn print_disk_file(path: &PathBuf) { 83 | match std::fs::read_to_string(path) { 84 | Ok(string) => { 85 | print!("{}", string); 86 | exit(0); 87 | } 88 | Err(e) => { 89 | error!("{}", e); 90 | exit(1); 91 | } 92 | } 93 | } 94 | 95 | // Prints the GupaxP2PoolApi files. 96 | #[cold] 97 | #[inline(never)] 98 | pub fn print_gupax_p2pool_api(gupax_p2pool_api: &Arc>) { 99 | let api = gupax_p2pool_api.lock().unwrap(); 100 | let log = match std::fs::read_to_string(&api.path_log) { 101 | Ok(string) => string, 102 | Err(e) => { 103 | error!("{}", e); 104 | exit(1); 105 | } 106 | }; 107 | let payout = match std::fs::read_to_string(&api.path_payout) { 108 | Ok(string) => string, 109 | Err(e) => { 110 | error!("{}", e); 111 | exit(1); 112 | } 113 | }; 114 | let xmr = match std::fs::read_to_string(&api.path_xmr) { 115 | Ok(string) => string, 116 | Err(e) => { 117 | error!("{}", e); 118 | exit(1); 119 | } 120 | }; 121 | let xmr = match xmr.trim().parse::() { 122 | Ok(o) => crate::xmr::AtomicUnit::from_u64(o), 123 | Err(e) => { 124 | warn!("GupaxP2poolApi | [xmr] parse error: {}", e); 125 | exit(1); 126 | } 127 | }; 128 | println!( 129 | "{}\nTotal payouts | {}\nTotal XMR | {} ({} Atomic Units)", 130 | log, 131 | payout.trim(), 132 | xmr, 133 | xmr.to_u64() 134 | ); 135 | exit(0); 136 | } 137 | 138 | #[inline] 139 | pub fn cmp_f64(a: f64, b: f64) -> std::cmp::Ordering { 140 | match (a <= b, a >= b) { 141 | (false, true) => std::cmp::Ordering::Greater, 142 | (true, false) => std::cmp::Ordering::Less, 143 | (true, true) => std::cmp::Ordering::Equal, 144 | _ => std::cmp::Ordering::Less, 145 | } 146 | } 147 | // Free functions. 148 | 149 | use crate::disk::gupax_p2pool_api::GupaxP2poolApi; 150 | use crate::helper::ProcessName; 151 | use chrono::Local; 152 | use egui::TextStyle; 153 | use egui::Ui; 154 | use log::error; 155 | use log::warn; 156 | use regex::Regex; 157 | use reqwest_middleware::ClientWithMiddleware; 158 | use std::path::PathBuf; 159 | use std::process::exit; 160 | use std::sync::Arc; 161 | use std::sync::Mutex; 162 | 163 | use log::info; 164 | 165 | //---------------------------------------------------------------------------------------------------- Use 166 | use crate::constants::*; 167 | 168 | //---------------------------------------------------------------------------------------------------- 169 | #[cold] 170 | #[inline(never)] 171 | // Clamp the scaling resolution `f32` to a known good `f32`. 172 | pub fn clamp_scale(scale: f32) -> f32 { 173 | // Make sure it is finite. 174 | if !scale.is_finite() { 175 | return APP_DEFAULT_SCALE; 176 | } 177 | 178 | // Clamp between valid range. 179 | scale.clamp(APP_MIN_SCALE, APP_MAX_SCALE) 180 | } 181 | pub fn output_console(output: &mut String, msg: &str, p_name: ProcessName) { 182 | if let Err(e) = writeln!(output, "{}{msg}", datetimeonsole()) { 183 | error!("{} Watchdog | GUI status write failed: {}", p_name, e); 184 | } 185 | } 186 | pub fn output_console_without_time(output: &mut String, msg: &str, p_name: ProcessName) { 187 | if let Err(e) = writeln!(output, "{msg}") { 188 | error!("{} Watchdog | GUI status write failed: {}", p_name, e); 189 | } 190 | } 191 | fn datetimeonsole() -> String { 192 | format!("[{}] ", Local::now().format("%Y-%m-%d %H:%M:%S%.3f")) 193 | } 194 | 195 | pub fn client() -> ClientWithMiddleware { 196 | reqwest_middleware::ClientBuilder::new(reqwest::Client::new()) 197 | .with(reqwest_retry::RetryTransientMiddleware::new_with_policy( 198 | reqwest_retry::policies::ExponentialBackoff::builder() 199 | .retry_bounds(Duration::from_secs(1), Duration::from_secs(5)) 200 | .build_with_total_retry_duration(Duration::from_secs(20)), 201 | )) 202 | .build() 203 | } 204 | /// to get the right height that a text must take before a button to be aligned in the center correctly. 205 | pub fn height_txt_before_button(ui: &Ui, style: &TextStyle) -> f32 { 206 | ui.style().spacing.button_padding.y * 2.0 + ui.text_style_height(style) 207 | } 208 | -------------------------------------------------------------------------------- /src/utils/errors.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use sysinfo::System; 4 | 5 | use crate::{ 6 | components::update::{NODE_BINARY, P2POOL_BINARY, XMRIG_BINARY, XMRIG_PROXY_BINARY}, 7 | helper::ProcessName, 8 | }; 9 | 10 | use super::sudo::SudoState; 11 | 12 | //---------------------------------------------------------------------------------------------------- [ErrorState] struct 13 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 14 | #[allow(dead_code)] 15 | pub enum ErrorButtons { 16 | YesNo, 17 | StayQuit, 18 | ResetState, 19 | ResetNode, 20 | Okay, 21 | Quit, 22 | Sudo, 23 | WindowsAdmin, 24 | Debug, 25 | } 26 | 27 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 28 | pub enum ErrorFerris { 29 | Happy, 30 | Cute, 31 | Oops, 32 | Error, 33 | Panic, 34 | Sudo, 35 | } 36 | 37 | pub struct ErrorState { 38 | pub error: bool, // Is there an error? 39 | pub msg: String, // What message to display? 40 | pub ferris: ErrorFerris, // Which ferris to display? 41 | pub buttons: ErrorButtons, // Which buttons to display? 42 | pub quit_twice: bool, // This indicates the user tried to quit on the [ask_before_quit] screen 43 | } 44 | 45 | impl Default for ErrorState { 46 | fn default() -> Self { 47 | Self::new() 48 | } 49 | } 50 | 51 | impl ErrorState { 52 | pub fn new() -> Self { 53 | Self { 54 | error: false, 55 | msg: "Unknown Error".to_string(), 56 | ferris: ErrorFerris::Oops, 57 | buttons: ErrorButtons::Okay, 58 | quit_twice: false, 59 | } 60 | } 61 | 62 | // Convenience function to enable the [App] error state 63 | pub fn set(&mut self, msg: impl Into, ferris: ErrorFerris, buttons: ErrorButtons) { 64 | if self.error { 65 | // If a panic error is already set and there isn't an [Okay] confirm or another [Panic], return 66 | if self.ferris == ErrorFerris::Panic 67 | && (buttons != ErrorButtons::Okay || ferris != ErrorFerris::Panic) 68 | { 69 | return; 70 | } 71 | } 72 | *self = Self { 73 | error: true, 74 | msg: msg.into(), 75 | ferris, 76 | buttons, 77 | quit_twice: false, 78 | }; 79 | } 80 | 81 | // Just sets the current state to new, resetting it. 82 | pub fn reset(&mut self) { 83 | *self = Self::new(); 84 | } 85 | 86 | // Instead of creating a whole new screen and system, this (ab)uses ErrorState 87 | // to ask for the [sudo] when starting XMRig. Yes, yes I know, it's called "ErrorState" 88 | // but rewriting the UI code and button stuff might be worse. 89 | // It also resets the current [SudoState] 90 | pub fn ask_sudo(&mut self, state: &Arc>) { 91 | *self = Self { 92 | error: true, 93 | msg: String::new(), 94 | ferris: ErrorFerris::Sudo, 95 | buttons: ErrorButtons::Sudo, 96 | quit_twice: false, 97 | }; 98 | SudoState::reset(state) 99 | } 100 | } 101 | 102 | pub fn process_running(process_name: ProcessName) -> bool { 103 | let name = match process_name { 104 | ProcessName::Node => NODE_BINARY, 105 | ProcessName::P2pool => P2POOL_BINARY, 106 | ProcessName::Xmrig => XMRIG_BINARY, 107 | ProcessName::XmrigProxy => XMRIG_PROXY_BINARY, 108 | ProcessName::Xvb => { 109 | // XvB does not exist as a process outside of Gupaxx (not yet anyway); 110 | return false; 111 | } 112 | }; 113 | let s = System::new_all(); 114 | if s.processes_by_exact_name(name.as_ref()).next().is_some() { 115 | return true; 116 | } 117 | false 118 | } 119 | -------------------------------------------------------------------------------- /src/utils/ferris.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // Some images of ferris in byte form for error messages, etc 19 | 20 | pub const FERRIS_HAPPY: &[u8] = include_bytes!("../../assets/images/ferris/happy.png"); 21 | pub const FERRIS_CUTE: &[u8] = include_bytes!("../../assets/images/ferris/cute.png"); 22 | pub const FERRIS_OOPS: &[u8] = include_bytes!("../../assets/images/ferris/oops.png"); 23 | pub const FERRIS_ERROR: &[u8] = include_bytes!("../../assets/images/ferris/error.png"); 24 | pub const FERRIS_PANIC: &[u8] = include_bytes!("../../assets/images/ferris/panic.png"); // This isnt technically ferris but its ok since its spooky 25 | pub const FERRIS_SUDO: &[u8] = include_bytes!("../../assets/images/ferris/sudo.png"); 26 | -------------------------------------------------------------------------------- /src/utils/macros.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // These are general QoL macros, nothing too scary, I promise. 19 | // 20 | // | MACRO | PURPOSE | EQUIVALENT CODE | 21 | // |---------|-----------------------------------------------|------------------------------------------------------------| 22 | // | arc_mut | Create a new [Arc] | std::sync::Arc::new(std::sync::Mutex::new(my_value)) | 23 | // | sleep | Sleep the current thread for x milliseconds | std::thread::sleep(std::time::Duration::from_millis(1000)) | 24 | // | flip | Flip a bool in place | my_bool = !my_bool | 25 | // 26 | 27 | // Creates a new [Arc] 28 | macro_rules! arc_mut { 29 | ($arc_mutex:expr) => { 30 | std::sync::Arc::new(std::sync::Mutex::new($arc_mutex)) 31 | }; 32 | } 33 | pub(crate) use arc_mut; 34 | 35 | // Sleeps a [std::thread] using milliseconds 36 | macro_rules! sleep { 37 | ($millis:expr) => { 38 | std::thread::sleep(std::time::Duration::from_millis($millis)) 39 | }; 40 | } 41 | pub(crate) use sleep; 42 | 43 | // Flips a [bool] in place 44 | macro_rules! flip { 45 | ($b:expr) => { 46 | match $b { 47 | true | false => $b = !$b, 48 | } 49 | }; 50 | } 51 | pub(crate) use flip; 52 | 53 | //---------------------------------------------------------------------------------------------------- TESTS 54 | #[cfg(test)] 55 | mod test { 56 | 57 | #[test] 58 | fn arc_mut() { 59 | let a = arc_mut!(false); 60 | assert!(!(*a.lock().unwrap())); 61 | } 62 | 63 | #[test] 64 | fn flip() { 65 | let mut b = true; 66 | flip!(b); 67 | assert!(!b); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | pub mod constants; 19 | pub mod errors; 20 | pub mod ferris; 21 | pub mod human; 22 | pub mod macros; 23 | pub mod panic; 24 | pub mod regex; 25 | pub mod resets; 26 | pub mod sudo; 27 | pub mod xmr; 28 | -------------------------------------------------------------------------------- /src/utils/panic.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | //---------------------------------------------------------------------------------------------------- Use 19 | use crate::constants::{ 20 | COMMIT, GUPAX_VERSION, NODE_VERSION, OS_NAME, P2POOL_VERSION, XMRIG_VERSION, 21 | }; 22 | 23 | //---------------------------------------------------------------------------------------------------- 24 | #[cold] 25 | #[inline(never)] 26 | /// Set custom panic hook. 27 | pub(crate) fn set_panic_hook(now: std::time::Instant) { 28 | std::panic::set_hook(Box::new(move |panic_info| { 29 | // Set stack-trace. 30 | let stack_trace = std::backtrace::Backtrace::force_capture(); 31 | let args = std::env::args_os(); 32 | let uptime = now.elapsed().as_secs_f32(); 33 | let panic_msg = panic_info.to_string(); 34 | // Re-format panic info. 35 | let panic_info = format!( 36 | "panic error: {panic_msg}\n 37 | {panic_info:#?} 38 | 39 | info: 40 | OS | {OS_NAME} 41 | args | {args:?} 42 | commit | {COMMIT} 43 | gupaxx | {GUPAX_VERSION} 44 | monerod | {NODE_VERSION} (bundled) 45 | p2pool | {P2POOL_VERSION} (bundled) 46 | xmrig | {XMRIG_VERSION} (bundled) 47 | xmrig-proxy | {XMRIG_VERSION} (bundled) 48 | uptime | {uptime} seconds 49 | 50 | stack backtrace:\n{stack_trace}", 51 | ); 52 | 53 | // Attempt to write panic info to disk. 54 | match crate::disk::get_gupax_data_path() { 55 | Ok(mut path) => { 56 | path.push("crash.txt"); 57 | match std::fs::write(&path, &panic_info) { 58 | Ok(_) => { 59 | eprintln!("\nmass_panic!() - Saved panic log to: {}\n", path.display()) 60 | } 61 | Err(e) => eprintln!("\nmass_panic!() - Could not save panic log: {e}\n"), 62 | } 63 | } 64 | Err(e) => eprintln!("panic_hook PATH error: {e}"), 65 | } 66 | 67 | // Exit all threads. 68 | benri::mass_panic!(panic_info); 69 | })); 70 | } 71 | -------------------------------------------------------------------------------- /src/utils/resets.rs: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------- Reset functions 2 | use crate::disk::create_gupax_dir; 3 | use crate::disk::errors::TomlError; 4 | use crate::disk::gupax_p2pool_api::GupaxP2poolApi; 5 | use crate::disk::node::Node; 6 | use crate::disk::pool::Pool; 7 | use crate::disk::state::State; 8 | use crate::info; 9 | use log::error; 10 | use std::path::PathBuf; 11 | use std::process::exit; 12 | 13 | #[cold] 14 | #[inline(never)] 15 | pub fn reset_state(path: &PathBuf) -> Result<(), TomlError> { 16 | match State::create_new(path) { 17 | Ok(_) => { 18 | info!("Resetting [state.toml] ... OK"); 19 | Ok(()) 20 | } 21 | Err(e) => { 22 | error!("Resetting [state.toml] ... FAIL ... {}", e); 23 | Err(e) 24 | } 25 | } 26 | } 27 | 28 | #[cold] 29 | #[inline(never)] 30 | pub fn reset_nodes(path: &PathBuf) -> Result<(), TomlError> { 31 | match Node::create_new(path) { 32 | Ok(_) => { 33 | info!("Resetting [node.toml] ... OK"); 34 | Ok(()) 35 | } 36 | Err(e) => { 37 | error!("Resetting [node.toml] ... FAIL ... {}", e); 38 | Err(e) 39 | } 40 | } 41 | } 42 | 43 | #[cold] 44 | #[inline(never)] 45 | pub fn reset_pools(path: &PathBuf) -> Result<(), TomlError> { 46 | match Pool::create_new(path) { 47 | Ok(_) => { 48 | info!("Resetting [pool.toml] ... OK"); 49 | Ok(()) 50 | } 51 | Err(e) => { 52 | error!("Resetting [pool.toml] ... FAIL ... {}", e); 53 | Err(e) 54 | } 55 | } 56 | } 57 | 58 | #[cold] 59 | #[inline(never)] 60 | pub fn reset_gupax_p2pool_api(path: &PathBuf) -> Result<(), TomlError> { 61 | match GupaxP2poolApi::create_new(path) { 62 | Ok(_) => { 63 | info!("Resetting GupaxP2poolApi ... OK"); 64 | Ok(()) 65 | } 66 | Err(e) => { 67 | error!("Resetting GupaxP2poolApi folder ... FAIL ... {}", e); 68 | Err(e) 69 | } 70 | } 71 | } 72 | 73 | #[cold] 74 | #[inline(never)] 75 | pub fn reset( 76 | path: &PathBuf, 77 | state: &PathBuf, 78 | node: &PathBuf, 79 | pool: &PathBuf, 80 | gupax_p2pool_api: &PathBuf, 81 | ) { 82 | let mut code = 0; 83 | // Attempt to remove directory first 84 | match std::fs::remove_dir_all(path) { 85 | Ok(_) => info!("Removing OS data path ... OK"), 86 | Err(e) => { 87 | error!("Removing OS data path ... FAIL ... {}", e); 88 | code = 1; 89 | } 90 | } 91 | // Recreate 92 | match create_gupax_dir(path) { 93 | Ok(_) => (), 94 | Err(_) => code = 1, 95 | } 96 | match reset_state(state) { 97 | Ok(_) => (), 98 | Err(_) => code = 1, 99 | } 100 | match reset_nodes(node) { 101 | Ok(_) => (), 102 | Err(_) => code = 1, 103 | } 104 | match reset_pools(pool) { 105 | Ok(_) => (), 106 | Err(_) => code = 1, 107 | } 108 | match reset_gupax_p2pool_api(gupax_p2pool_api) { 109 | Ok(_) => (), 110 | Err(_) => code = 1, 111 | } 112 | match code { 113 | 0 => println!("\nGupaxx reset ... OK"), 114 | _ => eprintln!("\nGupaxx reset ... FAIL"), 115 | } 116 | exit(code); 117 | } 118 | -------------------------------------------------------------------------------- /src/utils/sudo.rs: -------------------------------------------------------------------------------- 1 | // Gupaxx - Fork of Gupax 2 | // 3 | // Copyright (c) 2024-2025 Cyrix126 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see . 17 | 18 | // Handling of [sudo] for XMRig. 19 | // [zeroize] is used to wipe the memory after use. 20 | // Only gets imported in [main.rs] for Unix. 21 | 22 | use crate::{ 23 | constants::*, 24 | disk::state::{P2pool, Xmrig, XmrigProxy}, 25 | helper::{Helper, ProcessSignal}, 26 | }; 27 | use enclose::enc; 28 | use log::*; 29 | use std::{ 30 | io::Write, 31 | path::Path, 32 | process::*, 33 | sync::{Arc, Mutex}, 34 | thread, 35 | }; 36 | use zeroize::Zeroize; 37 | 38 | #[allow(dead_code)] // for dummy value windows 39 | #[derive(Debug, Clone)] 40 | pub struct SudoState { 41 | pub windows: bool, // If this bool is set, this struct is just a dummy so I don't have to change my type signatures :) 42 | pub testing: bool, // Are we attempting a sudo test right now? 43 | pub success: bool, // Was the sudo test a success? 44 | pub hide: bool, // Are we hiding the password? 45 | pub msg: String, // The message shown to the user if unsuccessful 46 | pub pass: String, // The actual password wrapped in a [SecretVec] 47 | pub signal: ProcessSignal, // Main GUI will set this depending on if we want [Start] or [Restart] 48 | } 49 | 50 | impl Default for SudoState { 51 | fn default() -> Self { 52 | Self::new() 53 | } 54 | } 55 | 56 | impl SudoState { 57 | #[cold] 58 | #[inline(never)] 59 | #[cfg(target_os = "windows")] 60 | pub fn new() -> Self { 61 | Self { 62 | windows: true, 63 | testing: false, 64 | success: false, 65 | hide: true, 66 | msg: String::new(), 67 | pass: String::new(), 68 | signal: ProcessSignal::None, 69 | } 70 | } 71 | #[cold] 72 | #[inline(never)] 73 | #[cfg(target_family = "unix")] 74 | pub fn new() -> Self { 75 | Self { 76 | windows: false, 77 | testing: false, 78 | success: false, 79 | hide: true, 80 | msg: "".to_string(), 81 | pass: String::with_capacity(256), 82 | signal: ProcessSignal::None, 83 | } 84 | } 85 | 86 | #[cold] 87 | #[inline(never)] 88 | // Resets the state. 89 | pub fn reset(state: &Arc>) { 90 | Self::wipe(state); 91 | let mut state = state.lock().unwrap(); 92 | state.testing = false; 93 | state.success = false; 94 | // state.signal = ProcessSignal::None; 95 | } 96 | 97 | #[cold] 98 | #[inline(never)] 99 | // Swaps the pass with another 256-capacity String, 100 | // zeroizes the old and drops it. 101 | pub fn wipe(state: &Arc>) { 102 | let mut new = String::with_capacity(256); 103 | // new is now == old, and vice-versa. 104 | std::mem::swap(&mut new, &mut state.lock().unwrap().pass); 105 | // we're wiping & dropping the old pass here. 106 | new.zeroize(); 107 | std::mem::drop(new); 108 | info!("Sudo | Password wipe with 0's ... OK"); 109 | } 110 | 111 | #[cold] 112 | #[inline(never)] 113 | // Spawns a thread and tests sudo with the provided password. 114 | // Sudo takes the password through STDIN via [--stdin]. 115 | // Sets the appropriate state fields on success/failure. 116 | pub fn test_sudo( 117 | state: Arc>, 118 | helper: &Arc>, 119 | xmrig: &Xmrig, 120 | p2pool: &P2pool, 121 | proxy: &XmrigProxy, 122 | path: &Path, 123 | ) { 124 | let path = path.to_path_buf(); 125 | thread::spawn(enc!((helper, xmrig, p2pool, proxy) move || { 126 | // Set to testing 127 | state.lock().unwrap().testing = true; 128 | 129 | // Make sure sudo timestamp is reset 130 | let reset = Command::new("sudo") 131 | .arg("--reset-timestamp") 132 | .stdout(Stdio::piped()) 133 | .stderr(Stdio::piped()) 134 | .stdin(Stdio::piped()) 135 | .status(); 136 | match reset { 137 | Ok(_) => info!("Sudo | Resetting timestamp ... OK"), 138 | Err(e) => { 139 | error!("Sudo | Couldn't reset timestamp: {}", e); 140 | Self::wipe(&state); 141 | state.lock().unwrap().msg = format!("Sudo error: {}", e); 142 | state.lock().unwrap().testing = false; 143 | return; 144 | } 145 | } 146 | 147 | // Spawn testing sudo 148 | let mut sudo = Command::new("sudo") 149 | .args(["--stdin", "--validate"]) 150 | .stdout(Stdio::piped()) 151 | .stderr(Stdio::piped()) 152 | .stdin(Stdio::piped()) 153 | .spawn() 154 | .unwrap(); 155 | 156 | // Write pass to STDIN 157 | let mut stdin = sudo.stdin.take().unwrap(); 158 | stdin 159 | .write_all(state.lock().unwrap().pass.as_bytes()) 160 | .unwrap(); 161 | drop(stdin); 162 | 163 | // Sudo re-prompts and will hang. 164 | // To workaround this, try checking 165 | // results for 5 seconds in a loop. 166 | for i in 1..=5 { 167 | match sudo.try_wait() { 168 | Ok(Some(code)) => { 169 | if code.success() { 170 | info!("Sudo | Password ... OK!"); 171 | state.lock().unwrap().success = true; 172 | break; 173 | } 174 | } 175 | Ok(None) => { 176 | info!("Sudo | Waiting [{}/5]...", i); 177 | std::thread::sleep(SECOND); 178 | } 179 | Err(e) => { 180 | error!("Sudo | Couldn't reset timestamp: {}", e); 181 | Self::wipe(&state); 182 | state.lock().unwrap().msg = format!("Sudo error: {}", e); 183 | state.lock().unwrap().testing = false; 184 | return; 185 | } 186 | } 187 | } 188 | if let Err(e) = sudo.kill() { 189 | warn!("Sudo | Kill error (it probably already exited): {}", e); 190 | } 191 | if state.lock().unwrap().success { 192 | match state.lock().unwrap().signal { 193 | ProcessSignal::Restart => crate::helper::Helper::restart_xmrig( 194 | &helper, 195 | &xmrig, 196 | &p2pool, 197 | &proxy, 198 | &path, 199 | Arc::clone(&state), 200 | ), 201 | ProcessSignal::Stop => crate::helper::Helper::stop_xmrig(&helper), 202 | _ => crate::helper::Helper::start_xmrig( 203 | &helper, 204 | &xmrig, 205 | &p2pool, 206 | &proxy, 207 | &path, 208 | Arc::clone(&state), 209 | ), 210 | } 211 | } else { 212 | state.lock().unwrap().msg = "Incorrect password! (or sudo timeout)".to_string(); 213 | Self::wipe(&state); 214 | } 215 | state.lock().unwrap().signal = ProcessSignal::None; 216 | state.lock().unwrap().testing = false; 217 | })); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | [files] 2 | extend-exclude = [ 3 | "assets/", 4 | "pgp/", 5 | "README.md" 6 | ] 7 | [default] 8 | extend-ignore-identifiers-re = ["ehr", "PN"] 9 | -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | ## Utilities 2 | 3 | | File/Folder | Purpose | 4 | |-------------|---------| 5 | | prepare.sh | Changes version across repo, commits README.md + CHANGELOG.md 6 | | create_tmp_env.sh | create temporary directory to be used for packaging. 7 | | move_binary_inside.sh | once github CI finished, download zip inside temporary file and execute this script. 8 | | package.sh | Package the contents of `skel`, sign, etc. Checks if all files exist and have the proper naming schemes 9 | | skel | A skeleton directory with the proper naming scheme + folder structure for packaging Gupax for all OS's 10 | -------------------------------------------------------------------------------- /utils/create_tmp_env.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Sets up a packaging environment in [/tmp] 4 | 5 | # Make sure we're in the [gupaxx/utils] directory 6 | set -ex 7 | [[ $PWD = */gupaxx ]] 8 | 9 | # Make sure the folder doesn't already exist 10 | GIT_COMMIT=$(cat .git/refs/heads/main) 11 | FOLDER="gupaxx_${GIT_COMMIT}" 12 | [[ ! -e /tmp/${FOLDER} ]] 13 | 14 | mkdir /tmp/${FOLDER} 15 | cp -r utils/* /tmp/${FOLDER}/ 16 | cp CHANGELOG.md /tmp/${FOLDER}/skel/ 17 | 18 | ## Download XMRig Binaries 19 | # download xmrig into directory linux 20 | wget https://github.com/xmrig/xmrig/releases/download/v6.22.2/xmrig-6.22.2-linux-static-x64.tar.gz 21 | tar -xf xmrig-6.22.2-linux-static-x64.tar.gz 22 | mv xmrig-6.22.2/xmrig /tmp/${FOLDER}/skel/linux_b/xmrig/xmrig 23 | rm -r xmrig-6.22.2 24 | rm xmrig-6.22.2-linux-static-x64.tar.gz 25 | # download xmrig into directory macos-arm64 26 | wget https://github.com/xmrig/xmrig/releases/download/v6.22.2/xmrig-6.22.2-macos-arm64.tar.gz 27 | tar -xf xmrig-6.22.2-macos-arm64.tar.gz 28 | mv xmrig-6.22.2/xmrig /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig 29 | rm -r xmrig-6.22.2 30 | rm xmrig-6.22.2-macos-arm64.tar.gz 31 | # download xmrig into directory macos-x64 32 | wget https://github.com/xmrig/xmrig/releases/download/v6.22.2/xmrig-6.22.2-macos-x64.tar.gz 33 | tar -xf xmrig-6.22.2-macos-x64.tar.gz 34 | mv xmrig-6.22.2/xmrig /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig 35 | rm -r xmrig-6.22.2 36 | rm xmrig-6.22.2-macos-x64.tar.gz 37 | # download xmrig into directory windows 38 | wget https://github.com/xmrig/xmrig/releases/download/v6.22.2/xmrig-6.22.2-msvc-win64.zip 39 | unzip xmrig-6.22.2-msvc-win64.zip 40 | mv xmrig-6.22.2/xmrig.exe /tmp/${FOLDER}/skel/windows_b/XMRig/xmrig.exe 41 | mv xmrig-6.22.2/WinRing0x64.sys /tmp/${FOLDER}/skel/windows_b/XMRig/WinRing0x64.sys 42 | rm -r xmrig-6.22.2 43 | rm xmrig-6.22.2-msvc-win64.zip 44 | 45 | ## Download XMRig-Proxy Binaries 46 | wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.22.0/xmrig-proxy-6.22.0-jammy-x64.tar.gz 47 | tar -xf xmrig-proxy-6.22.0-jammy-x64.tar.gz 48 | mv xmrig-proxy-6.22.0/xmrig-proxy /tmp/${FOLDER}/skel/linux_b/xmrig-proxy/xmrig-proxy 49 | rm -r xmrig-proxy-6.22.0 50 | rm xmrig-proxy-6.22.0-jammy-x64.tar.gz 51 | ## no release for arm64 mac-osx xmrig-proxy, todo make CI build it. 52 | # download xmrig into directory macos-arm64 53 | # wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.22.0/xmrig-proxy-6.22.0-macos-arm64.tar.gz 54 | # tar -xf xmrig-proxy-6.22.0-macos-arm64.tar.gz 55 | # mv xmrig-proxy-6.22.0/xmrig-proxy /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy 56 | # rm -r xmrig-proxy-6.22.0 57 | # rm xmrig-proxy-6.22.0-macos-arm64.tar.gz 58 | # download xmrig into directory macos-x64 59 | wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.22.0/xmrig-proxy-6.22.0-macos-x64.tar.gz 60 | tar -xf xmrig-proxy-6.22.0-macos-x64.tar.gz 61 | mv xmrig-proxy-6.22.0/xmrig-proxy /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy 62 | rm -r xmrig-proxy-6.22.0 63 | rm xmrig-proxy-6.22.0-macos-x64.tar.gz 64 | # download xmrig into directory windows 65 | wget https://github.com/xmrig/xmrig-proxy/releases/download/v6.22.0/xmrig-proxy-6.22.0-msvc-win64.zip 66 | unzip xmrig-proxy-6.22.0-msvc-win64.zip 67 | mv xmrig-proxy-6.22.0/xmrig-proxy.exe /tmp/${FOLDER}/skel/windows_b/XMRig-Proxy/xmrig-proxy.exe 68 | rm -r xmrig-proxy-6.22.0 69 | rm xmrig-proxy-6.22.0-msvc-win64.zip 70 | 71 | ## Download P2Pool Binaries 72 | # download p2pool into directory linux 73 | wget https://github.com/SChernykh/p2pool/releases/download/v4.5/p2pool-v4.5-linux-x64.tar.gz 74 | tar -xf p2pool-v4.5-linux-x64.tar.gz 75 | mv p2pool-v4.5-linux-x64/p2pool /tmp/${FOLDER}/skel/linux_b/p2pool/p2pool 76 | rm -r p2pool-v4.5-linux-x64 77 | rm p2pool-v4.5-linux-x64.tar.gz 78 | # download p2pool into directory macos-arm64 79 | wget https://github.com/SChernykh/p2pool/releases/download/v4.5/p2pool-v4.5-macos-aarch64.tar.gz 80 | tar -xf p2pool-v4.5-macos-aarch64.tar.gz 81 | mv p2pool-v4.5-macos-aarch64/p2pool /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool 82 | rm -r p2pool-v4.5-macos-aarch64 83 | rm p2pool-v4.5-macos-aarch64.tar.gz 84 | # download p2pool into directory macos-x64 85 | wget https://github.com/SChernykh/p2pool/releases/download/v4.5/p2pool-v4.5-macos-x64.tar.gz 86 | tar -xf p2pool-v4.5-macos-x64.tar.gz 87 | mv p2pool-v4.5-macos-x64/p2pool /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool 88 | rm -r p2pool-v4.5-macos-x64 89 | rm p2pool-v4.5-macos-x64.tar.gz 90 | # download p2pool into directory windows 91 | wget https://github.com/SChernykh/p2pool/releases/download/v4.5/p2pool-v4.5-windows-x64.zip 92 | unzip p2pool-v4.5-windows-x64.zip 93 | mv p2pool-v4.5-windows-x64/p2pool.exe /tmp/${FOLDER}/skel/windows_b/P2Pool/p2pool.exe 94 | rm -r p2pool-v4.5-windows-x64 95 | rm p2pool-v4.5-windows-x64.zip 96 | 97 | ## Download Monero Binaries 98 | # download monero into directory linux 99 | wget https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.4.0.tar.bz2 100 | tar -xf monero-linux-x64-v0.18.4.0.tar.bz2 101 | mv monero-x86_64-linux-gnu-v0.18.4.0/monerod /tmp/${FOLDER}/skel/linux_b/node/monerod 102 | rm -r monero-x86_64-linux-gnu-v0.18.4.0 103 | rm monero-linux-x64-v0.18.4.0.tar.bz2 104 | # download monero into directory macos-arm64 105 | wget https://downloads.getmonero.org/cli/monero-mac-armv8-v0.18.4.0.tar.bz2 106 | tar -xf monero-mac-armv8-v0.18.4.0.tar.bz2 107 | mv monero-aarch64-apple-darwin11-v0.18.4.0/monerod /tmp/${FOLDER}/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/node/monerod 108 | rm -r monero-aarch64-apple-darwin11-v0.18.4.0 109 | rm monero-mac-armv8-v0.18.4.0.tar.bz2 110 | # download monero into directory macos-x64 111 | wget https://downloads.getmonero.org/cli/monero-mac-x64-v0.18.4.0.tar.bz2 112 | tar -xf monero-mac-x64-v0.18.4.0.tar.bz2 113 | mv monero-x86_64-apple-darwin11-v0.18.4.0/monerod /tmp/${FOLDER}/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/node/monerod 114 | rm -r monero-x86_64-apple-darwin11-v0.18.4.0 115 | rm monero-mac-x64-v0.18.4.0.tar.bz2 116 | # download monero into directory windows 117 | wget https://downloads.getmonero.org/cli/monero-win-x64-v0.18.4.0.zip 118 | unzip monero-win-x64-v0.18.4.0.zip 119 | mv monero-x86_64-w64-mingw32-v0.18.4.0/monerod.exe /tmp/${FOLDER}/skel/windows_b/Node/monerod.exe 120 | rm -r monero-x86_64-w64-mingw32-v0.18.4.0 121 | rm monero-win-x64-v0.18.4.0.zip 122 | 123 | set +ex 124 | 125 | echo 126 | ls --color=always /tmp/${FOLDER} 127 | echo "/tmp/${FOLDER} ... OK" 128 | -------------------------------------------------------------------------------- /utils/move_binary_inside.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ## to be executed once you get zip files containing binairies from github CI and put them in /tmp/gupaxx_* 3 | 4 | [[ -d skel ]]; check "skel" 5 | [[ -f linux.zip ]]; check "linux zip" 6 | [[ -f windows.zip ]]; check "windows zip" 7 | [[ -f macos.zip ]]; check "macos zip" 8 | unzip linux.zip; unzip macos.zip; unzip windows.zip 9 | tar -xf windows.tar 10 | mv gupaxx.exe skel/windows/Gupaxx.exe 11 | mv gupaxx_b.exe skel/windows_b/Gupaxx.exe 12 | tar -xf linux.tar 13 | mv gupaxx skel/linux/gupaxx 14 | mv gupaxx_b skel/linux_b/gupaxx 15 | tar -xf macos.tar 16 | mv Gupaxx-macos-x64.app/Contents/Info.plist skel/macos-x64/Gupaxx.app/Contents/Info.plist 17 | mv Gupaxx-macos-x64.app/Contents/MacOS/gupaxx skel/macos-x64/Gupaxx.app/Contents/MacOS/gupaxx 18 | mv Gupaxx-macos-x64.app_b/Contents/Info.plist skel/macos-x64_b/Gupaxx.app/Contents/Info.plist 19 | mv Gupaxx-macos-x64.app_b/Contents/MacOS/gupaxx skel/macos-x64_b/Gupaxx.app/Contents/MacOS/gupaxx 20 | mv Gupaxx-macos-arm64.app/Contents/Info.plist skel/macos-arm64/Gupaxx.app/Contents/Info.plist 21 | mv Gupaxx-macos-arm64.app/Contents/MacOS/gupaxx skel/macos-arm64/Gupaxx.app/Contents/MacOS/gupaxx 22 | mv Gupaxx-macos-arm64.app_b/Contents/Info.plist skel/macos-arm64_b/Gupaxx.app/Contents/Info.plist 23 | mv Gupaxx-macos-arm64.app_b/Contents/MacOS/gupaxx skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/gupaxx 24 | rm -r Gupaxx-macos-x64.app 25 | rm -r Gupaxx-macos-arm64.app 26 | rm -r Gupaxx-macos-x64.app_b 27 | rm -r Gupaxx-macos-arm64.app_b 28 | rm linux.zip; rm macos.zip; rm windows.zip 29 | # windows unzip only the exe so not tar to delete. 30 | rm linux.tar; rm macos.tar; rm windows.tar 31 | -------------------------------------------------------------------------------- /utils/package.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | START_TIME=$EPOCHSECONDS 4 | 5 | title() { printf "\n\e[1;93m%s\e[0m\n" "============================ $1 ============================"; } 6 | check() { 7 | local CODE=$? 8 | if [[ $CODE = 0 ]]; then 9 | printf "${BASH_LINENO} | %s ... \e[1;92mOK\e[0m\n" "$1" 10 | else 11 | printf "${BASH_LINENO} | %s ... \e[1;91mFAIL\e[0m\n" "$1" 12 | exit $CODE 13 | fi 14 | } 15 | int() { 16 | exit 1 17 | } 18 | 19 | trap 'int' INT 20 | 21 | title "Basic checks" 22 | # Check for needed files 23 | [[ -d skel ]]; check "skel" 24 | [[ -f skel/CHANGELOG.md ]]; check "skel/CHANGELOG.md" 25 | [[ $1 = v* ]]; check "\$1 ... $1" 26 | NEW_VER="$1" 27 | cd skel; check "CD into skel" 28 | 29 | # Check that [skel] directory contains everything 30 | # and that the naming schemes are correct 31 | title "Linux folder check" 32 | [[ -f linux/gupaxx ]]; check "linux/gupaxx" 33 | [[ -f linux_b/gupaxx ]]; check "linux_b/gupaxx" 34 | [[ -f linux_b/p2pool/p2pool ]]; check "linux_b/p2pool/p2pool" 35 | [[ -f linux_b/xmrig/xmrig ]]; check "linux_b/xmrig/xmrig" 36 | [[ -f linux_b/xmrig-proxy/xmrig-proxy ]]; check "linux_b/xmrig-proxy/xmrig-proxy" 37 | [[ -f linux_b/node/monerod ]]; check "linux_b/node/monerod" 38 | title "macOS-x64 folder check" 39 | [[ -d macos-x64/Gupaxx.app ]]; check "macos-x64/Gupaxx.app" 40 | [[ -d macos-x64_b/Gupaxx.app ]]; check "macos-x64_b/Gupaxx.app" 41 | [[ -f macos-x64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool ]]; check "macos-x64_b/p2pool/p2pool" 42 | [[ -f macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig ]]; check "macos-x64_b/xmrig/xmrig" 43 | [[ -f macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy ]]; check "macos-x64_b/xmrig-proxy/xmrig-proxy" 44 | [[ -f macos-x64_b/Gupaxx.app/Contents/MacOS/node/monerod ]]; check "macos-x64_b/node/monerod" 45 | title "macOS-arm64 folder check" 46 | [[ -d macos-arm64/Gupaxx.app ]]; check "macos-arm64/Gupaxx.app" 47 | [[ -d macos-arm64_b/Gupaxx.app ]]; check "macos-arm64_b/Gupaxx.app" 48 | [[ -f macos-arm64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool ]]; check "macos-arm64_b/p2pool/p2pool" 49 | [[ -f macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig ]]; check "macos-arm64_b/xmrig/xmrig" 50 | ## no macos-arm64 xmrig-proxy released todo 51 | # [[ -f macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig-proxy/xmrig-proxy ]]; check "macos-arm64_b/xmrig-proxy/xmrig-proxy" 52 | [[ -f macos-arm64_b/Gupaxx.app/Contents/MacOS/node/monerod ]]; check "macos-arm64_b/node/monerod" 53 | title "Windows folder check" 54 | [[ -f windows/Gupaxx.exe ]]; check "windows/Gupaxx.exe" 55 | [[ -f windows_b/Gupaxx.exe ]]; check "windows_b/Gupaxx.exe" 56 | [[ -f windows_b/P2Pool/p2pool.exe ]]; check "windows_b/P2Pool/p2pool.exe" 57 | [[ -f windows_b/XMRig/xmrig.exe ]]; check "windows_b/XMRig/xmrig.exe" 58 | [[ -f windows_b/XMRig/WinRing0x64.sys ]]; check "windows_b/XMRig/WinRing0x64.sys" 59 | [[ -f windows_b/XMRig-Proxy/xmrig-proxy.exe ]]; check "windows_b/XMRig-Proxy/xmrig-proxy.exe" 60 | [[ -f windows_b/Node/monerod.exe ]]; check "windows_b/Node/monerod.exe" 61 | 62 | # Get random date for tar/zip 63 | title "RNG Date" 64 | RNG=$((EPOCHSECONDS-RANDOM*4)); check "RNG ... $RNG" 65 | DATE=$(date -d @${RNG}); check "DATE ... $DATE" 66 | 67 | # Tar Linux Bundle 68 | title "Tar Linux" 69 | # give execution permission 70 | chmod +x linux/gupaxx 71 | chmod +x linux_b/gupaxx 72 | chmod +x linux_b/p2pool/p2pool 73 | chmod +x linux_b/xmrig/xmrig 74 | chmod +x linux_b/xmrig-proxy/xmrig-proxy 75 | chmod +x linux_b/node/monerod 76 | mv linux_b "gupaxx-$NEW_VER-linux-x64-bundle"; check "linux -> gupaxx-$NEW_VER-linux-x64-bundle" 77 | tar -czpf "gupaxx-${NEW_VER}-linux-x64-bundle.tar.gz" "gupaxx-$NEW_VER-linux-x64-bundle" --owner=lm --group=lm ; check "tar linux-bundle" 78 | # Tar Linux Standalone 79 | mv linux "gupaxx-$NEW_VER-linux-x64-standalone" 80 | tar -czpf "gupaxx-${NEW_VER}-linux-x64-standalone.tar.gz" "gupaxx-$NEW_VER-linux-x64-standalone" --owner=lm --group=lm ; check "tar linux-standalone" 81 | # Remove dir 82 | rm -r "gupaxx-$NEW_VER-linux-x64-standalone"; check "rm linux dir" 83 | rm -r "gupaxx-$NEW_VER-linux-x64-bundle"; check "rm linux_b dir" 84 | 85 | # x64 86 | # Tar macOS Bundle 87 | title "Tar macOS-x64" 88 | mv macos-x64_b "gupaxx-$NEW_VER-macos-x64-bundle"; check "macos-x64_b -> gupaxx-$NEW_VER-macos-x64-bundle" 89 | tar -czpf "gupaxx-${NEW_VER}-macos-x64-bundle.tar.gz" "gupaxx-$NEW_VER-macos-x64-bundle" --owner=lm --group=lm ; check "tar macos-bundle" 90 | # Tar macOS Standalone 91 | mv macos-x64 "gupaxx-$NEW_VER-macos-x64-standalone"; check "macos-x64 -> gupaxx-$NEW_VER-macos-x64-standalone" 92 | tar -czpf "gupaxx-${NEW_VER}-macos-x64-standalone.tar.gz" "gupaxx-$NEW_VER-macos-x64-standalone" --owner=lm --group=lm ; check "tar macos-x64-standalone" 93 | # Remove dir 94 | rm -r "gupaxx-$NEW_VER-macos-x64-standalone"; check "rm macos-x64 dir" 95 | rm -r "gupaxx-$NEW_VER-macos-x64-bundle"; check "rm macos-x64_b dir" 96 | 97 | # ARM 98 | # Tar macOS Bundle 99 | title "Tar macOS-arm64" 100 | mv macos-arm64_b "gupaxx-$NEW_VER-macos-arm64-bundle"; check "macos-arm64_b -> gupaxx-$NEW_VER-macos-arm64-bundle" 101 | tar -czpf "gupaxx-${NEW_VER}-macos-arm64-bundle.tar.gz" "gupaxx-$NEW_VER-macos-arm64-bundle" --owner=lm --group=lm ; check "tar macos-bundle" 102 | # Tar macOS Standalone 103 | mv macos-arm64 "gupaxx-$NEW_VER-macos-arm64-standalone"; check "macos-arm64 -> gupaxx-$NEW_VER-macos-arm64-standalone" 104 | tar -czpf "gupaxx-${NEW_VER}-macos-arm64-standalone.tar.gz" "gupaxx-$NEW_VER-macos-arm64-standalone" --owner=lm --group=lm ; check "tar macos-arm64-standalone" 105 | # Remove dir 106 | rm -r "gupaxx-$NEW_VER-macos-arm64-standalone"; check "rm macos-arm64 dir" 107 | rm -r "gupaxx-$NEW_VER-macos-arm64-bundle"; check "rm macos-arm64_b dir" 108 | 109 | # Zip Windows Bundle 110 | title "Zip Windows" 111 | mv windows_b "gupaxx-$NEW_VER-windows-x64-bundle"; check "windows_b -> gupaxx-$NEW_VER-windows-x64-bundle" 112 | zip -qr "gupaxx-${NEW_VER}-windows-x64-bundle.zip" "gupaxx-$NEW_VER-windows-x64-bundle"; check "zip windows-bundle" 113 | # Zip Windows Standalone 114 | mv windows "gupaxx-$NEW_VER-windows-x64-standalone"; check "windows -> gupaxx-$NEW_VER-windows-x64-standalone" 115 | zip -qr "gupaxx-${NEW_VER}-windows-x64-standalone.zip" "gupaxx-$NEW_VER-windows-x64-standalone"; check "zip windows-standalone" 116 | # Remove dir 117 | rm -r "gupaxx-$NEW_VER-windows-x64-standalone"; check "rm windows dir" 118 | rm -r "gupaxx-$NEW_VER-windows-x64-bundle"; check "rm windows_b dir" 119 | 120 | # SHA256SUMS + Sign 121 | title "Hash + Sign" 122 | SHA256SUMS=$(sha256sum gupaxx* | gpg --clearsign --local-user 8EFFE4A8C0FD4B6D21C3AAB2EC6E5BB401C6362D); check "Hash + Sign" 123 | echo "${SHA256SUMS}" > SHA256SUMS; check "Create SHA256SUMS file" 124 | sha256sum -c SHA256SUMS; check "Verify SHA" 125 | gpg --verify SHA256SUMS; check "Verify GPG" 126 | 127 | # Get changelog + SHA256SUMS into clipboard 128 | title "Clipboard" 129 | clipboard() { 130 | grep -B999 -m1 "^$" CHANGELOG.md 131 | echo "## SHA256SUM & [PGP Signature](https://github.com/cyrix126/gupaxx/blob/main/pgp/cyrix126.asc)" 132 | echo '```' 133 | cat SHA256SUMS 134 | echo '```' 135 | } 136 | CHANGELOG=$(clipboard); check "Create changelog + sign" 137 | echo "$CHANGELOG" | wl-copy $clipboard 138 | check "Changelog into clipboard" 139 | 140 | # Reset timezone 141 | title "End" 142 | printf "\n%s\n" "package.sh ... Took [$((EPOCHSECONDS-START_TIME))] seconds ... OK!" 143 | -------------------------------------------------------------------------------- /utils/prepare.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # prepare new [gupaxx] version in: 4 | # 1. README.md 5 | # 2. CHANGELOG.md 6 | # 3. Cargo.toml 7 | 8 | # $1 = new_version 9 | set -ex 10 | sudo -v 11 | [[ $1 = v* ]] 12 | [[ $PWD = */gupaxx ]] 13 | 14 | # get old GUPAX_VER 15 | OLD_VER="v$(grep -m1 "version" Cargo.toml | grep -o "[0-9].[0-9].[0-9]")" 16 | OLD_VER_NUM="$(grep -m1 "version" Cargo.toml | grep -o "[0-9].[0-9].[0-9]")" 17 | 18 | # get p2pool/xmrig version 19 | P2POOL_VERSION="$(grep "P2POOL_VERSION" src/constants.rs | grep -o "\"v[0-9].*\"")" 20 | XMRIG_VERSION="$(grep "XMRIG_VERSION" src/constants.rs | grep -o "\"v[0-9].*\"")" 21 | XMRIG_PROXY_VERSION="$(grep "XMRIG_PROXY_VERSION" src/constants.rs | grep -o "\"v[0-9].*\"")" 22 | 23 | # sed change 24 | sed -i "s/$OLD_VER/$1/g" README.md 25 | sed -i 's/^version = "$OLD_VER_NUM"/$1/' Cargo.toml 26 | 27 | # changelog 28 | cat << EOM > CHANGELOG.md.new 29 | # $1 30 | ## Updates 31 | * 32 | 33 | ## Fixes 34 | * 35 | 36 | ## Bundled Versions 37 | * [\`P2Pool ${P2POOL_VERSION//\"/}\`](https://github.com/SChernykh/p2pool/releases/tag/${P2POOL_VERSION//\"/}) 38 | * [\`XMRig ${XMRIG_VERSION//\"/}\`](https://github.com/xmrig/xmrig/releases/tag/${XMRIG_VERSION//\"/}) 39 | * [\`XMRig_Proxy ${XMRIG_PROXY_VERSION//\"/}\`](https://github.com/xmrig/xmrig-proxy/releases/tag/${XMRIG_PROXY_VERSION//\"/}) 40 | 41 | 42 | --- 43 | 44 | 45 | EOM 46 | cat CHANGELOG.md >> CHANGELOG.md.new 47 | mv -f CHANGELOG.md.new CHANGELOG.md 48 | 49 | # commit 50 | git add CHANGELOG.md README.md 51 | git commit -m "prepare $1" 52 | -------------------------------------------------------------------------------- /utils/skel/linux/gupaxx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/linux/gupaxx -------------------------------------------------------------------------------- /utils/skel/linux_b/gupaxx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/linux_b/gupaxx -------------------------------------------------------------------------------- /utils/skel/linux_b/p2pool/p2pool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/linux_b/p2pool/p2pool -------------------------------------------------------------------------------- /utils/skel/linux_b/xmrig/xmrig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/linux_b/xmrig/xmrig -------------------------------------------------------------------------------- /utils/skel/macos-arm64/Gupaxx.app/Contents/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64/Gupaxx.app/Contents/Info.plist -------------------------------------------------------------------------------- /utils/skel/macos-arm64/Gupaxx.app/Contents/MacOS/gupaxx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64/Gupaxx.app/Contents/MacOS/gupaxx -------------------------------------------------------------------------------- /utils/skel/macos-arm64/Gupaxx.app/Contents/Resources/Gupaxx.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64/Gupaxx.app/Contents/Resources/Gupaxx.icns -------------------------------------------------------------------------------- /utils/skel/macos-arm64_b/Gupaxx.app/Contents/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64_b/Gupaxx.app/Contents/Info.plist -------------------------------------------------------------------------------- /utils/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/gupaxx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/gupaxx -------------------------------------------------------------------------------- /utils/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool -------------------------------------------------------------------------------- /utils/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig -------------------------------------------------------------------------------- /utils/skel/macos-arm64_b/Gupaxx.app/Contents/Resources/Gupaxx.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-arm64_b/Gupaxx.app/Contents/Resources/Gupaxx.icns -------------------------------------------------------------------------------- /utils/skel/macos-x64/Gupaxx.app/Contents/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64/Gupaxx.app/Contents/Info.plist -------------------------------------------------------------------------------- /utils/skel/macos-x64/Gupaxx.app/Contents/MacOS/gupaxx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64/Gupaxx.app/Contents/MacOS/gupaxx -------------------------------------------------------------------------------- /utils/skel/macos-x64/Gupaxx.app/Contents/Resources/Gupaxx.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64/Gupaxx.app/Contents/Resources/Gupaxx.icns -------------------------------------------------------------------------------- /utils/skel/macos-x64_b/Gupaxx.app/Contents/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64_b/Gupaxx.app/Contents/Info.plist -------------------------------------------------------------------------------- /utils/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/gupaxx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/gupaxx -------------------------------------------------------------------------------- /utils/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/p2pool/p2pool -------------------------------------------------------------------------------- /utils/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64_b/Gupaxx.app/Contents/MacOS/xmrig/xmrig -------------------------------------------------------------------------------- /utils/skel/macos-x64_b/Gupaxx.app/Contents/Resources/Gupaxx.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/macos-x64_b/Gupaxx.app/Contents/Resources/Gupaxx.icns -------------------------------------------------------------------------------- /utils/skel/windows/Gupaxx.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/windows/Gupaxx.exe -------------------------------------------------------------------------------- /utils/skel/windows_b/Gupaxx.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/windows_b/Gupaxx.exe -------------------------------------------------------------------------------- /utils/skel/windows_b/P2Pool/p2pool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/windows_b/P2Pool/p2pool.exe -------------------------------------------------------------------------------- /utils/skel/windows_b/XMRig/WinRing0x64.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/windows_b/XMRig/WinRing0x64.sys -------------------------------------------------------------------------------- /utils/skel/windows_b/XMRig/xmrig.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyrix126/gupaxx/bba7fb3b1360a66f12896eafef63e53940afd5f3/utils/skel/windows_b/XMRig/xmrig.exe --------------------------------------------------------------------------------