- Pin the ed25519 public key in xmrig_updater.h, activating signature verification in soft mode
(kXmrigRequireSignature=false): a release's ".sig" asset is verified when present, but an
unsigned release still installs on TLS + SHA-256. Verified live against the current release
(v6.25.2, which ships no .sig yet) — still installs.
- gitignore *.ed25519.key / *.ed25519.pub.b64 so a signing secret key can never be committed.
- Add a unit test that the pinned key decodes to a valid 32-byte ed25519 key (a malformed paste
fails the build, not silently disabling verification).
Bug fix (found via a flaky test): verifyXmrigSignature trimmed trailing whitespace BEFORE the
raw-64-byte check, so a raw signature whose last byte equals '\n'/'\r'/space/tab (~1.6% of
signatures) was corrupted and rejected. Now base64 is tried first (safe to trim) and the raw
path uses the exact untrimmed bytes. Added a deterministic regression test that forces a
whitespace-terminated raw signature. Suite is stable (0 failures in 10 runs; was ~3/8).
Also de-brittled the live integration test: it no longer pins a release-specific binary hash
(reaching Done already means the worker verified the binary against the release's own checksum).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the supply-chain gap the review flagged: today the archive and its SHA-256 share one
trust root (the release body), so a compromised/edited release can ship an arbitrary binary
that still "verifies". This adds authenticity via a detached ed25519 signature checked against
a public key PINNED IN THE BINARY (not fetched), using libsodium's crypto_sign_verify_detached.
Opt-in / soft rollout:
- kXmrigSignaturePublicKeyBase64 in xmrig_updater.h is EMPTY by default -> signatures are not
checked and behavior is unchanged (TLS + SHA-256 only). Paste the base64 public key to enable.
- Once a key is pinned, an install verifies a "<archive>.sig" asset (base64/raw 64-byte ed25519
signature over the archive bytes) when present; kXmrigRequireSignature=true additionally
refuses installs that publish no signature.
- The check runs after the SHA-256 check, over the same already-read archive bytes; refuses on
a missing key-but-required, unreachable .sig, or invalid signature.
- verifyXmrigSignature + selectXmrigSignatureAsset are pure (libsodium only) and unit-tested:
valid base64 + raw-64-byte signatures verify; tampered data, wrong key, and malformed/empty
inputs all fail closed. Cross-tool interop verified (Python stdlib base64 == sodium base64).
- scripts/sign-xmrig-release.sh: keygen / sign / pubkey helper (PyNaCl = same libsodium ed25519)
to produce the .sig assets and the public key to pin.
No behavior change until a key is pinned. Both variants build; suite passes; live worker
re-verified (signatures off by default).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses confirmed findings from the multi-lens review of the updater:
- Cancelable + live progress (was: download uncancelable, progress stuck at 0%, closing
the dialog mid-download blocked the UI thread on the worker join). Wire a libcurl
CURLOPT_XFERINFOFUNCTION that publishes byte counts and returns abort when cancel() is
requested; add a Cancel button. The dialog's destructor now aborts the transfer promptly,
so closing mid-download no longer freezes the UI.
- Graceful "unavailable" instead of a red error on platforms with no published build
(macOS / ARM): new terminal State::Unavailable rendered neutrally, not as a failure.
- Install-time running guard (TOCTOU): App::isPoolMinerRunning() re-checked in the dialog
before each install, so a dialog opened before mining started can't replace a live binary.
- Size caps: CURLOPT_MAXFILESIZE on the download and a per-archive-member ceiling before
decomphressing into memory, to bound an attacker-controlled archive.
- Distinguish a local read failure of the downloaded archive from a checksum mismatch
(was reported misleadingly as "possible tampering").
- Reword the dialog's verification note to "checked against the release's published SHA-256
checksum" (integrity, not authenticity — see the signing note below).
Not fixed here (needs your input): WinRing0x64.sys has no per-file hash published, but it is
covered by the verified archive checksum (it is inside the verified zip); and the release is
not cryptographically signed — checksums and binary share one trust root. Adding a pinned-key
ed25519/minisign signature is the real supply-chain hardening and needs an offline signing key
+ a release-process change.
Both variants build; suite passes; live worker re-verified end-to-end on linux-x64.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds util/XmrigUpdater: a background-thread service (mirrors util/Bootstrap) that pulls
the latest DRG-XMRig release from the project's Gitea, verifies it, and installs the miner
binary into the daemon directory. Service layer only; the mining-tab UI hook comes next.
Flow: GET /api/v1/repos/DragonX/drg-xmrig/releases/latest -> pick the asset matching this
platform (…-linux-x64.zip / …-win-x64.zip; no macOS build -> graceful "unavailable") ->
download (libcurl, TLS verified) -> verify the archive SHA-256 -> extract with miniz,
flattening the versioned subdir the archive nests the binary in -> verify the extracted
binary's SHA-256 in memory before writing it -> atomic install (+chmod +x on POSIX). On
Windows also extracts WinRing0x64.sys; config.json/README.md are skipped.
Security (download-and-execute): TLS is verified, and BOTH the archive and the inner binary
are checked against the SHA-256 checksums published in the release body (parsed as
"<hex> <name>" lines) — install is refused on a missing or mismatched checksum.
Split into a pure core (xmrig_updater_core.cpp: release parse, asset/platform match, checksum
parse, SHA-256) and the curl/miniz worker (xmrig_updater.cpp). The core is unit-tested against
a real captured release fixture (tests/fixtures/xmrig/release_latest.json); an env-gated
(DRAGONX_TEST_NETWORK=1) integration test exercises the worker live and was verified end-to-end
on linux-x64 (inner binary SHA-256 matches the published value). Both variants build; suite passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the user confirms a send on a locked encrypted lite wallet, show an unlock
modal (passphrase -> unlockWallet) instead of letting the backend reject it with
"Wallet is locked". After unlocking, the user re-confirms the send (the form is
preserved). Balances remain viewable while locked; only spending needs unlock.
- send_tab: the Confirm-and-send button routes to App::requestLiteUnlock() when
getWalletState().isLocked(), else sends as before.
- App::renderLiteUnlockPrompt(): centered modal, passphrase (Enter submits),
Unlock/Cancel; the passphrase buffer is sodium-zeroed after every path.
Full-node unaffected (gated on liteWallet()/isLocked()). Builds clean, launches
clean, tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the bare "land on main UI with a No-wallet overlay" first-run with a
lite welcome modal, shown when no wallet file exists yet (lite_wallet_ present,
not open, walletExists() false):
- "Create new wallet" — one-click createWallet({}); on success, notifies the user
to back up their recovery phrase and navigates to Settings (Backup & keys),
where the seed can be revealed/copied via the existing backup UI.
- "Restore from seed" — navigates to Settings (Lite wallet request → Restore).
- "Later" — dismiss for the session.
Routes to the already-built + verified create/restore/backup flows rather than
re-implementing seed display in the modal (no new secret-handling surface).
Dismissed once an action is chosen; never shown again once a wallet exists.
Full-node is unaffected (renderLiteFirstRunPrompt() returns early when
lite_wallet_ is null). English i18n built-ins added.
Verified: fresh-HOME lite launch shows the prompt, clean run + shutdown, no
crash/RPC noise; tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In lite builds there is no daemon, and isConnected() now tracks the lite wallet,
so the full-node "not connected / waiting for daemon" wording was misleading when
no wallet is open. Add two strings (lite_no_wallet, lite_no_wallet_short; English
built-ins, so other languages fall back until translated) and use them in lite:
- receive/send address preview + receive empty-state overlay + send "can't send"
tooltip + transactions empty state -> "No wallet open [— create or open one in
Settings]" instead of daemon wording.
- Status bar: the red indicator shows "No wallet open" (not "Disconnected") in
lite; the P2P peer count is skipped (lite has no peers); and the redundant
full-node connection-detail line is suppressed (connection_status_ set to
"Connected"/"" from the lite wallet state).
Full-node wording unchanged (all gated on isLiteBuild()). Build + run clean
(no RPC noise), tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an encrypted SQLite transaction history cache with cached tip metadata and
per-address shielded scan progress so startup and full refreshes avoid
re-scanning every z-address while still invalidating on wallet/address/rescan
changes.
Improve wallet history loading by paging transparent transactions, preserving
cached shielded and sent rows, keeping recent/unconfirmed activity visible, and
classifying mining-address receives. Show z_sendmany opid sends immediately in
History and Overview, pin pending rows through refreshes, and apply optimistic
address/balance debits until opids resolve.
Add timestamped RPC console tracing by source/method without logging params or
results, reduce redundant refresh/RPC calls, and cache Explorer recent block
summaries in SQLite.
Expand focused tests for transaction cache encryption, scan-progress
persistence/invalidation, history preservation, operation-status parsing,
pending send visibility, and Explorer/RPC refresh behavior.
- Add expanded address icon picker with search, bottom-aligned actions, and improved modal sizing
- Embed a pickaxe icon font subset and wire it into typography/address icon rendering
- Track view-only shielded addresses and prevent sends from non-spendable z-addresses
- Improve address transfer dialog sizing, max amount handling, and text clipping
- Tune main header layout values in ui.toml
- Update README, codebase overview, and third-party license documentation
- Add text scaling for section labels (TOOLS, ADVANCED) in sidebar
- Separate explorer_section key from explorer nav label to fix ALL CAPS
- Shorten long sidebar translations: es/pt settings, pt overview, ru tools/advanced
- Fix explorer translations from ALL CAPS to proper case in all languages
- New BootstrapDownloadDialog accessible from Settings page
- Stops daemon before download, prevents auto-restart during bootstrap
- Confirm/Downloading/Done/Failed states with progress display
- Mirror support (bootstrap2.dragonx.is)
- Add bootstrap_downloading_ flag to prevent tryConnect() auto-reconnect
- Right-align Download Bootstrap + Setup Wizard buttons in settings
- Add 100 missing i18n keys to all 8 language files (de/es/fr/ja/ko/pt/ru/zh)
- Includes bootstrap, explorer, mining benchmark, transfer, delete blockchain,
force quit, address label, and settings section translations
- Update add_missing_translations.py with new translation batch
- Rewrite RenderSharedAddressList with two-pass layout architecture
- Add drag-to-transfer: drag address onto another to open transfer dialog
- Add AddressLabelDialog with custom label text and 20-icon picker
- Add AddressTransferDialog with amount input, fee, and balance preview
- Add AddressMeta persistence (label, icon, sortOrder) in settings.json
- Gold favorite border inset 2dp from container edge
- Show hide button on all addresses, not just zero-balance
- Smaller star/hide buttons to clear favorite border
- Semi-transparent dragged row with context-aware tooltip
- Copy-to-clipboard deferred to mouse-up (no copy on drag)
- Themed colors via resolveColor() with CSS variable fallbacks
- Keyboard nav (Up/Down/J/K, Enter to copy, F2 to edit label)
- Add i18n keys for all new UI strings
- Fix blk/s calculation that was inflated ~10x due to resetting the
time baseline every frame instead of only when blocks advanced
- Add decay when no new blocks arrive for 10s so rate doesn't stay stale
- Add 7 missing translation keys (timeout_off/1min/5min/15min/30min/1hour,
slider_off) to all 8 language files so settings dropdowns translate
- Show language names in native script (中文, Русский, 日本語, 한국어)
instead of initial burst performance. Previously the benchmark used a
fixed 20s warmup + 10s peak measurement, which reported inflated
results on thermally constrained hardware (e.g. 179 H/s vs actual
sustained 117 H/s on a MacBook Pro).
- Adaptive warmup with stability detection: mine for at least 90s,
then compare rolling 10s hashrate windows. Require 3 consecutive
windows within 5% before declaring thermal equilibrium (cap 300s)
- Average-based measurement: record mean hashrate over 30s instead
of peak, reflecting real sustained throughput
- Start candidates at half the system cores — lower thread counts
are rarely optimal and waste time warming up
- Add CoolingDown phase: 5s idle pause between tests so each starts
from a similar thermal baseline
- Adaptive time estimates: use observed warmup durations from
completed tests to predict remaining time
- UI shows Stabilizing when waiting for thermal equilibrium past
the minimum warmup, Cooling during idle pauses"
Security (P0):
- Fix sidebar remaining interactive behind lock screen
- Extend auto-lock idle detection to include active widget interactions
- Distinguish missing PIN vault from wrong PIN; auto-switch to passphrase
Blocking UX (P1):
- Add 15s timeout for encryption state check to prevent indefinite loading
- Show restart reason in loading overlay after wallet encryption
- Add Force Quit button on shutdown screen after 10s
- Warn user if embedded daemon fails to start during wizard completion
Polish (P2):
- Use configured explorer URL in Receive tab instead of hardcoded URL
- Increase request memo buffer from 256 to 512 bytes to match Send tab
- Extend notification duration to 5s for critical operations (tx sent,
wallet encrypted, key import, backup, export)
- Add Reduce Motion accessibility setting (disables page fade + balance lerp)
- Show estimated remaining time during mining thread benchmark
- Add staleness indicator to market price data (warning after 5 min)
New i18n keys: incorrect_pin, incorrect_passphrase, pin_not_set,
restarting_after_encryption, force_quit, reduce_motion, tt_reduce_motion,
ago, wizard_daemon_start_failed
- Add pool mining thread benchmark: cycles through thread counts with
20s warmup + 10s measurement to find optimal setting for CPU
- Add GPU-aware idle detection: GPU utilization >= 10% (video, games)
treats system as active; toggle in mining tab header (default: on)
Supports AMD sysfs, NVIDIA nvidia-smi, Intel freq ratio; -1 on macOS
- Fix idle thread scaling: use getRequestedThreads() for immediate
thread count instead of xmrig API threads_active which lags on restart
- Apply active thread count on initial mining start when user is active
- Skip idle mining adjustments while benchmark is running
- Disable thread grid drag-to-select during benchmark
- Add idle_gpu_aware setting with JSON persistence (default: true)
- Add 7 i18n English strings for benchmark and GPU-aware tooltips
Build & setup:
- Fix setup.sh and build.sh for macOS (bundle daemon, xmrig, sapling params, asmap.dat into .app)
- Fix CMakeLists.txt libsodium linking for macOS
- Fix incbin.h to use __DATA,__const section on macOS
- Remove vendored libsodium-1.0.18 source tree (use fetch script instead)
- Remove prebuilt-binaries/xmrig (replaced by xmrig-hac)
- Add .DS_Store to .gitignore
Rendering & UI:
- Use GLSL #version 150 and OpenGL 3.2 Core Profile on macOS
- Force dpiScale=1.0 on macOS to fix Retina double-scaling
- Set default window/UI opacity to 100% on Mac/Linux
- Add scroll fade shader guard for macOS GL compatibility
- Add ImGui error recovery around render loop and mining tab
Daemon & bootstrap:
- Fix getDragonXDataDir() to return ~/Library/Application Support/Hush/DRAGONX/ on macOS
- Fix isPortInUse() with connect() fallback (no /proc/net/tcp on macOS)
- Increase daemon watchdog timeout from 3s to 15s
- Add daemon status indicator (colored dot + label) in wizard bootstrap phases
Mining tab:
- Fix EmbeddedDaemon::getMemoryUsageMB() crash on macOS (was using Linux /proc)
- Fix XmrigManager::getMemoryUsageMB() to use ps on macOS instead of /proc
- Restructure RenderMiningTab with wrapper pattern for exception safety
- Fix default pool URL to include port (pool.dragonx.is:3433)
- Replace all hardcoded English strings with TR() translation keys across
every tab, dialog, and component (~20 UI files)
- Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with
complete translations (~37k lines added)
- Improve i18n loader with exe-relative path fallback and English base
fallback for missing keys
- Add pool-side hashrate polling via pool stats API in xmrig_manager
- Introduce Layout::beginFrame() per-frame caching and refresh balance
layout config only on schema generation change
- Offload daemon output parsing to worker thread
- Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
Mine-when-idle:
- Auto-start/stop mining based on system idle time detection
- Platform::getSystemIdleSeconds() via XScreenSaver (Linux) / GetLastInputInfo (Win)
- Settings: mine_when_idle toggle + configurable delay (30s–10m)
- Settings page UI with checkbox and delay combo
Console tab:
- Shell-like argument parsing with quote and JSON bracket support
- Pass JSON objects/arrays directly as RPC params
- Fix selection indices when lines are evicted from buffer
Connection & status bar:
- Reduce RPC connect timeout to 1s for localhost fast-fail
- Fast retry timer on daemon startup and external daemon detection
- Show pool mining hashrate in status bar; sidebar badge reflects pool state
UI polish:
- Add logo to About card in settings; expose logo dimensions on App
- Header title offset-y support; adjust content-area margins
- Fix banned peers row cursor position (rawRowPosB.x)
Branding:
- Update copyright to "DragonX Developers" in RC and About section
- Replace logo/icon assets with updated versions
Misc:
- setup.sh: checkout dragonx branch before pulling
- Remove stale prebuilt-binaries/xmrig/.gitkeep
Windows identity:
- Add VERSIONINFO resource (.rc) with ObsidianDragon file description
- Embed application manifest for DPI awareness and shell identity
- Patch libwinpthread/libpthread to remove competing VERSIONINFO
- Set AppUserModelID and HWND property store to override Task Manager cache
- Link patched pthread libs to eliminate "POSIX WinThreads" description
Address creation (+New button):
- Move z_getnewaddress/getnewaddress off UI thread to async worker
- Inject new address into state immediately for instant UI selection
- Trigger background refresh for balance updates
Mining tab:
- Add pool mining dropdown with saved URLs/workers and bookmarks
- Add solo mining log panel from daemon output with chart/log toggle
- Fix toggle button cursor (render after InputTextMultiline)
- Auto-restart miner on pool config change
- Migrate default pool URL to include stratum port
Transactions:
- Sort pending (0-conf) transactions to top of history
- Fall back to timereceived when timestamp is missing
Shutdown:
- Replace blocking sleep_for calls with 100ms polling loops
- Check shutting_down_ flag throughout daemon restart/bootstrap flows
- Reduce daemon stop timeout from 30s to 10s
Other:
- Fix market chart fill artifact (single concave polygon vs per-segment quads)
- Add bootstrap checksum verification state display
- Rename daemon client identifier to ObsidianDragon
Diagnostics & logging:
- add verbose logging system (VERBOSE_LOGF) with toggle in Settings
- forward app-level log messages to Console tab for in-UI visibility
- add detailed connection attempt logging (attempt #, daemon state,
config paths, auth failures, port owner identification)
- detect HTTP 401 auth failures and show actionable error messages
- identify port owner process (PID + name) on both Linux and Windows
- demote noisy acrylic/shader traces from DEBUG_LOGF to VERBOSE_LOGF
- persist verbose_logging preference in settings.json
- link iphlpapi on Windows for GetExtendedTcpTable
Security & encryption:
- update local encryption state immediately after encryptwallet RPC
so Settings reflects the change before daemon restarts
- show notifications for encrypt success/failure and PIN skip
- use dedicated RPC client for z_importwallet during decrypt flow
to avoid blocking main rpc_ curl_mutex (which starved peer/tx refresh)
- force full state refresh (addresses, transactions, peers) after
successful wallet import
Network tab:
- redesign peers refresh button as glass-panel with icon + label,
matching the mining button style
- add spinning arc animation while peer data is loading
(peer_refresh_in_progress_ atomic flag set/cleared in refreshPeerInfo)
- prevent double-click spam during refresh
- add refresh-button size to ui.toml
Other:
- use fast_rpc_ for rescan polling to avoid blocking on main rpc_
- enable DRAGONX_DEBUG in all build configs (was debug-only)
- setup.sh: pull latest xmrig-hac when repo already exists
Full-node GUI wallet for DragonX cryptocurrency.
Built with Dear ImGui, SDL3, and OpenGL3/DX11.
Features:
- Send/receive shielded and transparent transactions
- Autoshield with merged transaction display
- Built-in CPU mining (xmrig)
- Peer management and network monitoring
- Wallet encryption with PIN lock
- QR code generation for receive addresses
- Transaction history with pagination
- Console for direct RPC commands
- Cross-platform (Linux, Windows)