From ae66b5469490597ba649ddbdaee629593bf61db4 Mon Sep 17 00:00:00 2001 From: DanS Date: Mon, 12 Oct 2026 13:22:00 -0500 Subject: [PATCH] fix(miner): keep an updated miner, don't re-extract the bundled one Builds that embed xmrig (Windows release) re-extracted the bundled miner whenever its size differed from the file on disk. An "Update miner..." install always differs, so the next start silently put the bundled build back while settings still showed the updated version. - The bundled miner is now placed only when it is missing, like the node binaries (kXmrigRefreshOnSizeMismatch = false in the provision table). Params and asmap still follow the bundled size. - Behaviour change: a wallet upgrade no longer refreshes a miner the user never updated; "Update miner..." is the way to get a newer build. - The uncalled needsXmrigExtraction() is removed. Co-Authored-By: Claude Opus 5.5 --- src/resources/embedded_resources.cpp | 26 +++++--------------------- src/resources/embedded_resources.h | 6 +++--- tests/test_phase4.cpp | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/resources/embedded_resources.cpp b/src/resources/embedded_resources.cpp index 3b88f63..87ea337 100644 --- a/src/resources/embedded_resources.cpp +++ b/src/resources/embedded_resources.cpp @@ -243,15 +243,15 @@ std::mutex& provisionMutex() { static std::mutex m; return m; } struct ProvisionEntry { const char* name; - bool refreshOnSizeMismatch; // re-extract when the bundled size differs (params, asmap, xmrig) + bool refreshOnSizeMismatch; // re-extract when the bundled size differs (params, asmap) bool executable; bool fullNodeOnly; // not written by Lite builds (the lite backend bakes in its own params) bool startupTrigger; // makes needsParamsExtraction() true }; -// The same files and rules as the old hand-written extraction: params/asmap/xmrig follow the bundled size; -// the node binaries and the rebuild helper are placed only when MISSING (the user may run a specific dragonxd; -// replacing it is the explicit Settings action). asmap/cli/tx/rebuild are written along, never a trigger. +// Params and asmap follow the bundled size; the node binaries, the rebuild helper and the miner are placed only +// when MISSING (user-updatable via the updaters: the user may run a specific dragonxd or an "Update miner..." +// build; replacing the node is the explicit Settings action). asmap/cli/tx/rebuild are written along, never a trigger. const ProvisionEntry kProvisionEntries[] = { { RESOURCE_SAPLING_SPEND, true, false, true, true }, { RESOURCE_SAPLING_OUTPUT, true, false, true, true }, @@ -262,7 +262,7 @@ const ProvisionEntry kProvisionEntries[] = { { RESOURCE_DRAGONX_TX, false, true, true, false }, #endif #ifdef HAS_EMBEDDED_XMRIG - { RESOURCE_XMRIG, true, true, false, true }, + { RESOURCE_XMRIG, kXmrigRefreshOnSizeMismatch, true, false, true }, #endif #ifdef HAS_EMBEDDED_WALLET_REBUILD { RESOURCE_DRAGONX_WALLET_REBUILD, false, true, true, false }, @@ -790,22 +790,6 @@ std::string getDaemonPath() return ""; } -bool needsXmrigExtraction() -{ -#ifdef HAS_EMBEDDED_XMRIG - std::string daemonDir = getDaemonDirectory(); -#ifdef _WIN32 - std::string xmrigPath = daemonDir + "\\xmrig.exe"; -#else - std::string xmrigPath = daemonDir + "/xmrig"; -#endif - const auto* res = getEmbeddedResource(RESOURCE_XMRIG); - return resourceNeedsUpdate(res, xmrigPath); -#else - return false; -#endif -} - bool hasXmrigAvailable() { #ifdef HAS_EMBEDDED_XMRIG diff --git a/src/resources/embedded_resources.h b/src/resources/embedded_resources.h index ab7bce5..c7d041b 100644 --- a/src/resources/embedded_resources.h +++ b/src/resources/embedded_resources.h @@ -41,6 +41,9 @@ bool extractEmbeddedResources(ProvisionProgress* progress = nullptr, const Cance // Does `destPath` need (re)writing? Missing -> yes. Present: only when `refreshOnSizeMismatch` and the size // differs. embeddedSize == 0 -> never. (Pure apart from the stat; exposed for tests.) bool resourceNeedsWrite(std::uint64_t embeddedSize, bool refreshOnSizeMismatch, const std::string& destPath); +// The bundled miner is placed only when MISSING: a present xmrig is never replaced by the bundled copy, even when +// its size differs, so an "Update miner..." install survives a wallet restart or upgrade. +inline constexpr bool kXmrigRefreshOnSizeMismatch = false; // the in-app updater owns the miner once placed // Write `data` to `destPath` via `destPath + kPartialFileSuffix`, flush it to disk, then move it into place // (POSIX rename; Windows MoveFileEx with retry and move-aside of a running .exe to ".old"). POSIX: chmod 0755 // first when `executable`. Returns false (temp removed, original kept) on any failure or on cancel. @@ -152,9 +155,6 @@ bool hasDaemonAvailable(); // Get daemon executable path (extracts if needed) std::string getDaemonPath(); -// Check if xmrig needs to be extracted -bool needsXmrigExtraction(); - // Check if xmrig is available (embedded or in app directory) bool hasXmrigAvailable(); diff --git a/tests/test_phase4.cpp b/tests/test_phase4.cpp index 20e89f8..2d8588d 100644 --- a/tests/test_phase4.cpp +++ b/tests/test_phase4.cpp @@ -3559,6 +3559,22 @@ void testResourceNeedsWrite() removeTree(dir); } +// C120: the bundled miner is placed only when missing, so an "Update miner..." install of a different size is +// never overwritten by the bundled copy on the next start. +void testXmrigProvisionPolicy() +{ + using dragonx::resources::kXmrigRefreshOnSizeMismatch; + using dragonx::resources::resourceNeedsWrite; + EXPECT_TRUE(!kXmrigRefreshOnSizeMismatch); + const fs::path dir = makeTempDir(); + const std::string f = (dir / "xmrig.exe").string(); + const std::string missing = (dir / "missing-xmrig.exe").string(); + { std::ofstream(f, std::ios::binary) << "12345"; } + EXPECT_TRUE(!resourceNeedsWrite(10, kXmrigRefreshOnSizeMismatch, f)); // present, different size: kept + EXPECT_TRUE(resourceNeedsWrite(10, kXmrigRefreshOnSizeMismatch, missing)); // missing: placed + removeTree(dir); +} + // ── C222: bootstrap preflight (util/bootstrap.h) ── // Writes a zip with miniz. level 0 stores members uncompressed, so a test can corrupt member data in place. static bool writeBootstrapTestZip(const fs::path& zipPath, @@ -16982,6 +16998,7 @@ int main() testVerifySaplingParams(); testStartupProvisionState(); testResourceNeedsWrite(); + testXmrigProvisionPolicy(); testBootstrapSpaceSufficient(); testBootstrapPlanArchive(); #ifndef _WIN32