// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 // // XmrigUpdater — fetch + verify + install the latest DRG-XMRig miner from the DragonX Gitea. // // Flow (mirrors util/Bootstrap): query the Gitea releases API for the latest release, pick the // asset matching this platform (…-linux-x64.zip / …-win-x64.zip), download it, verify its // published SHA-256, then extract the miner binary (flattening the versioned subdir the archive // nests it in) into the target directory and verify the extracted binary's SHA-256 before it is // made executable. All network/disk work runs on a background thread; progress is polled // thread-safely from the UI thread. // // Security: download-and-execute, so verification is mandatory — TLS is verified (libcurl // defaults), the host is the project's own Gitea over HTTPS, and BOTH the archive and the inner // binary are checked against the SHA-256 checksums published in the release body. The checksums // live in the release body markdown (no SHA256SUMS asset), parsed as " " lines. #pragma once #include #include #include #include #include #include #include namespace dragonx { namespace util { struct XmrigReleaseAsset { std::string name; std::string downloadUrl; long long size = 0; }; struct XmrigRelease { bool ok = false; std::string tag; // e.g. "v1.0.0" std::string body; // release notes markdown (holds the checksum blocks) std::vector assets; std::string error; }; // ── Release signature (opt-in supply-chain hardening) ──────────────────────── // // Pinned ed25519 public key (base64, 32 bytes) used to verify a detached signature over the // downloaded archive. EMPTY by default = signature verification DISABLED (TLS + the release's // published SHA-256 are the only gates, unchanged behavior). To enable: paste the base64 public // key here and have the release publish a ".sig" asset containing the base64 (or // raw 64-byte) ed25519 signature of the archive bytes (see scripts/sign-xmrig-release.sh). Once a // key is set, an install verifies the signature when the .sig asset is present; set // kXmrigRequireSignature=true to additionally refuse installs that publish no signature. inline constexpr const char* kXmrigSignaturePublicKeyBase64 = ""; inline constexpr bool kXmrigRequireSignature = false; // ── Pure helpers (no I/O; unit-tested) ─────────────────────────────────────── // Parse the Gitea GET /releases/latest JSON into an XmrigRelease (ok=false + error on failure). XmrigRelease parseXmrigRelease(const std::string& json); // The asset-name token for the host platform: "linux-x64", "win-x64", "macos-x64", // "macos-arm64", or "" if unknown/unsupported. std::string currentXmrigPlatformToken(); // Index of the asset whose name matches the platform token (e.g. ends with "-linux-x64.zip"), // or -1 if none (e.g. no macOS build is published). int selectXmrigAsset(const XmrigRelease& release, const std::string& platformToken); // Parse " …" lines from the release body into { name -> lowercase-hex }. // Keys are the first whitespace-delimited token after the hash, so this captures both the archive // checksums (keyed by zip filename) and the inner-binary checksums (keyed by "xmrig"/"xmrig.exe"). std::map parseXmrigChecksums(const std::string& body); // Lowercase-hex SHA-256 of a buffer (libsodium). Empty string on failure. std::string sha256Hex(const void* data, std::size_t len); // The binary file basenames to extract for a platform: {"xmrig"} on POSIX, // {"xmrig.exe", "WinRing0x64.sys"} on Windows. First entry is always the miner binary. std::vector xmrigExtractBasenames(const std::string& platformToken); // Index of the detached-signature asset for a given archive (name ".sig" or // ".minisig"), or -1 if none is published. int selectXmrigSignatureAsset(const XmrigRelease& release, const std::string& archiveName); // Verify a detached ed25519 signature over `data`. `signatureFileContent` is the .sig asset body // (base64 of, or raw, a 64-byte ed25519 signature; whitespace tolerated). `pubKeyBase64` is the // base64 32-byte public key. Returns true only on a cryptographically valid signature. bool verifyXmrigSignature(const std::string& data, const std::string& signatureFileContent, const std::string& pubKeyBase64); // ── Background worker ──────────────────────────────────────────────────────── class XmrigUpdater { public: enum class State { Idle, Checking, UpToDate, UpdateAvailable, Unavailable, // no miner build is published for this platform (terminal, not an error) Downloading, Verifying, Extracting, Done, Failed }; struct Progress { State state = State::Idle; double downloaded_bytes = 0; double total_bytes = 0; float percent = 0.0f; std::string status_text; std::string error; // non-empty on Failed std::string latest_tag; // tag reported by the API (once checked) std::string installed_tag; // caller-supplied current install (for update detection) bool update_available = false; }; // Gitea releases API for the DRG-XMRig fork. static constexpr const char* kApiUrl = "https://git.dragonx.is/api/v1/repos/DragonX/drg-xmrig/releases/latest"; XmrigUpdater() = default; ~XmrigUpdater(); XmrigUpdater(const XmrigUpdater&) = delete; XmrigUpdater& operator=(const XmrigUpdater&) = delete; // Query the latest release on a background thread. `installedTag` (may be empty/unknown) is // compared to the API tag to set Progress.update_available. End state: UpToDate / UpdateAvailable // / Failed. void startCheck(const std::string& installedTag); // Download → verify archive → extract (flatten) → verify binary → install into `targetDir` on a // background thread. Re-fetches the release so it is self-contained. End state: Done / Failed. // On Done, getProgress().latest_tag is the version that should be persisted as the installed tag. void startInstall(const std::string& targetDir); void cancel(); Progress getProgress() const; bool isDone() const; // true once the worker reached a terminal state (Done/Failed/Unavailable) // Internal: called by the libcurl progress callback. Publishes live download bytes and returns // false to ask curl to abort (when cancel() was requested). Public only so the C callback in the // .cpp can reach it without leaking curl types into this header. bool onDownloadProgress(double downloadedBytes, double totalBytes); private: void runCheck(std::string installedTag); void runInstall(std::string targetDir); void setProgress(State state, const std::string& text, double done = 0, double total = 0); bool downloadToFile(const std::string& url, const std::string& destPath); std::string httpGet(const std::string& url); mutable std::mutex mutex_; Progress progress_; std::atomic cancel_requested_{false}; std::atomic worker_running_{false}; std::thread worker_; }; } // namespace util } // namespace dragonx