fix(util): use the POSIX daemon binary name, scan versions off-thread

The installed-daemon lookup built the binary path from the Windows
resource key "dragonxd.exe", which never exists on Linux or macOS, so
the installed full node was never found there. The version scan also
read the whole binary, now around 200 MB, into memory on the UI thread.

- A new DAEMON_BINARY_NAME gives the daemon's file name on this platform
  ("dragonxd" on POSIX, "dragonxd.exe" on Windows); the old resource
  constant stays only as the embedded-table key. The installed daemon is
  now found on POSIX.
- The version-stamp scan streams the file in 4 MiB chunks with bounded
  memory, carrying bytes across each boundary so a stamp straddling it is
  neither cut short nor missed, and stops at the first match. The result
  is cached by path, size and mtime, so only a changed binary is
  rescanned.
- Settings stats the binary on the UI thread but runs the streamed scan
  on a worker, guarded so a new scan is not started while one is still
  running (the future blocks in its destructor), and the version fills
  in when it lands.

Tests: the POSIX path and binary name, a version stamp in a small file,
a stamp straddling the 4 MiB chunk boundary, and a file with no stamp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 21:42:23 -05:00
co-authored by Claude Opus 4.8
parent 21dc874988
commit abc82136c4
4 changed files with 117 additions and 17 deletions
+44
View File
@@ -2951,6 +2951,49 @@ void testSecureVaultWellFormed()
}
#endif
#ifndef _WIN32
void testInstalledDaemonInfoPosix()
{
const char* oldHome = std::getenv("HOME");
const std::string savedHome = oldHome ? oldHome : "";
const char* oldXdg = std::getenv("XDG_CONFIG_HOME");
const bool hadXdg = oldXdg != nullptr;
const std::string savedXdg = hadXdg ? oldXdg : "";
const fs::path tmp = makeTempDir();
setenv("HOME", tmp.string().c_str(), 1);
setenv("XDG_CONFIG_HOME", (tmp / ".config").string().c_str(), 1);
{
const fs::path dir = fs::path(dragonx::resources::getDaemonDirectory());
std::error_code ec;
fs::create_directories(dir, ec);
EXPECT_TRUE(dir.string().rfind(tmp.string(), 0) == 0); // never the real config dir
auto info0 = dragonx::resources::getInstalledDaemonInfo(false);
EXPECT_FALSE(info0.exists); // nothing installed yet
{ // a stamp in a small file
std::ofstream o(dir / "dragonxd", std::ios::binary);
o << "garbage v9 x v1.3.0-0123abcdef trailing";
}
auto info1 = dragonx::resources::getInstalledDaemonInfo(true);
EXPECT_TRUE(info1.exists);
EXPECT_EQ(info1.version, std::string("v1.3.0-0123abcdef"));
{ // a stamp straddling the 4 MiB chunk boundary
std::string blob(4u * 1024 * 1024 - 7, 'x');
blob += "v1.4.2-deadbeef12"; blob += std::string(100, 'y');
std::ofstream o(dir / "dragonxd", std::ios::binary | std::ios::trunc); o << blob;
}
EXPECT_EQ(dragonx::resources::getInstalledDaemonInfo(true).version, std::string("v1.4.2-deadbeef12"));
{ // no stamp => ""
std::ofstream o(dir / "dragonxd", std::ios::binary | std::ios::trunc); o << std::string(5000, 'z');
}
EXPECT_TRUE(dragonx::resources::getInstalledDaemonInfo(true).version.empty());
EXPECT_EQ(std::string(dragonx::resources::DAEMON_BINARY_NAME), std::string("dragonxd"));
}
if (!savedHome.empty()) setenv("HOME", savedHome.c_str(), 1); else unsetenv("HOME");
if (hadXdg) setenv("XDG_CONFIG_HOME", savedXdg.c_str(), 1); else unsetenv("XDG_CONFIG_HOME");
removeTree(tmp);
}
#endif
void testWalletSecurityController()
{
using dragonx::services::WalletSecurityController;
@@ -17652,6 +17695,7 @@ int main()
testPinVaultPolicy();
#ifndef _WIN32
testSecureVaultWellFormed();
testInstalledDaemonInfoPosix();
#endif
testWalletSecurityController();
testWalletSecurityWorkflow();