Migrate the three PIN dialogs in App::renderPinDialogs (Set PIN / Change PIN /
Remove PIN) onto the settings-modal reference design — presentation-only, plus a
deliberate secret-hygiene addition:
- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth 420/420/400,
idSuffix pinsetup/pinchange/pinremove}) replacing the legacy positional overload
- drop each dialog's redundant post-description Separator
- every rendered-chrome string -> TR(): titles/buttons reuse settings_set_pin /
settings_change_pin / settings_remove_pin, the Set-PIN confirm reuses
wiz_pin_confirm; 7 new pin_* keys added to the English source + all 8
res/lang/*.json additively; ImGui::Text -> TextUnformatted for labels
- HARDENING: wipe the passphrase/PIN input buffers on BlurFloat dismiss
(X/Esc/outside-click) for all three dialogs. Set PIN's wallet-passphrase buffer
(pin_passphrase_buf_) was previously left resident on dismiss; it is now zeroed.
The vault/RPC path is byte-for-byte unchanged: the submit-time memsets, the
walletpassphrase/walletlock RPCs, vault_->store/changePin/retrieve/removeVault,
SecureVault::secureZero, and setPinEnabled/save are untouched. The pin_status_ and
toast strings inside the worker callbacks are left in English (a separate
service-layer i18n pass), to avoid editing the secret path.
Verified: build + ctest + hygiene clean; i18n complete in 8 langs with no format
specifiers; a 3-lens adversarial review (secret-hygiene / presentation-only /
ui-i18n) returned zero findings, with the presentation lens proving the vault path
diffs byte-for-byte identical against HEAD.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migrate App::renderDecryptWalletDialog ("Remove Wallet Encryption") onto the
settings-modal reference design — presentation-only for a fund-critical dialog,
plus one deliberate secret-hygiene addition:
- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth 480,
idSuffix "decrypt"}) replacing the legacy positional overload
- Phase 0 amber TextWrapped warning -> material::DialogWarningHeader(decrypt_warning)
- drop the redundant Phase 0 Separator
- every rendered-chrome string across all 4 phases (PassphraseEntry / Working /
Success / Error) -> TR() (14 new decrypt_*/try_again keys added to the English
source + all 8 res/lang/*.json additively; the button reuses
settings_remove_encryption, the label reuses change_pass_current, cancel/close reused)
- HARDENING: wipe decrypt_pass_buf_ on BlurFloat dismiss (X/Esc/outside-click),
mirroring the change-passphrase fix; canClose() gates pOpen so this cannot fire
during the Working phase
The fund-moving async worker pyramid (unlock -> export -> stop -> backup ->
restart -> re-import, vault/PIN cleanup) is byte-for-byte unchanged; the three
in-worker toast notifications and the "Incorrect passphrase" status are left in
English to avoid touching the fund path (a separate service-layer i18n pass).
Verified: build + ctest + hygiene clean; i18n complete in 8 langs with no format
specifiers and the wallet.dat.encrypted.bak literal preserved; a 3-lens adversarial
review (secret-hygiene / fund-path-untouched / ui-i18n) returned zero findings, with
the fund-path lens proving the async pyramid diffs IDENTICAL against HEAD.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Change Passphrase dialog only zeroed change_old/new/confirm_pass_buf_ on
the success path (inside changePassphrase); dismissing via X / Esc / outside-
click left the typed passphrases resident in the input buffers. Add a same-
frame teardown mirroring the encrypt dialog's X-button cleanup: when the
BlurFloat overlay clears show_change_passphrase_ mid-frame, memset the three
buffers. The failure path still keeps them intact for retry, and the async
change already copies the passphrases into worker-owned strings, so wiping the
UI buffers cannot disturb an in-flight change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migrate the Encrypt Wallet (3-phase) and Change Passphrase dialogs in
renderEncryptWalletDialog onto the settings-modal reference design —
presentation-only, no change to the crypto/state-machine/secret-wipe logic:
- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth, idSuffix})
replacing the legacy positional overload (same visual style, dp-scaled width)
- amber TextWrapped warning -> material::DialogWarningHeader(wiz_encrypt_warning)
- drop redundant Separator dividers
- every hardcoded English string -> TR() (13 new enc_*/change_pass_* keys added to
the English source + all 8 res/lang/*.json additively; CJK subset rebuilt)
The strength-meter math, 3-phase transitions, "cannot close while encrypting"
guard, memset/SecureVault buffer wipes, and the encrypt/changePassphrase/vault
async paths are byte-for-byte unchanged. Verified: build + ctest + hygiene clean,
wiz_strength %s format-string safe across all languages, and a 3-lens adversarial
review (secret-hygiene / presentation-only / ui-i18n) returned zero findings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hand-drawn absolute geometry (dl->AddText offsets, SetNextWindowSize,
explicit ImVec2 button widths, SameLine strides) is immune to the app's
DPI machinery (font-atlas rebuild + ScaleAllSizes), so several overlays
rendered native-size (tiny) on HiDPI / Windows 150% displays. Multiply the
raw logical-px geometry by Layout::dpiScale():
- seed_display: content-aware, DPI-scaled seed-grid column stride (a raw
130px stride let words collide with the next column's index at 150%)
- lock screen: card/logo/input/unlock-button + all vertical offsets
- migrate-to-seed + seed-backup dialogs: button widths
- send-confirm: confirm/cancel button widths
- about dialog: value-column x, edition inset, link/close button widths
- chat conversation list: row height + padding (fixes the preview clip)
Also freeze demo state during the full UI sweep: guard App::update() on
capture_mode_ so a running daemon's refresh can't clobber the injected
demo state (it blanked Send/Receive/History with a CDB error mid-sweep).
Verified on Windows 4K@150% and locally at font_scale=1.5 (same
dpiScale()==1.5 code path). Adds a DPI-scaling convention note to CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The existing sweep only captures tabs. This adds a "Full UI sweep" (DEBUG
OPTIONS) that also drives every normally-hidden surface into view and captures
it under every skin, so all the UI a normal sweep misses can be reviewed at
once.
- The capture unit becomes a "surface" (SweepTarget: a base tab, optionally
with a setup()/teardown() that forces a modal / multi-step flow / state
overlay on top). Tabs are surfaces with a null setup. The existing tab sweep
is folded into the same state machine (one code path). All sweep logic +
the catalog + demo data move into a new src/app_sweep.cpp.
- Runs OFFLINE against injected demo data (installDemoWalletData snapshots then
restores the real state_ — WalletState isn't copy-assignable, so a targeted
field snapshot is used) and fires NO live op: capture_mode_ guards the
seed-backup RPC, auto-lock, and the migration pumps; the async-firing dialogs
are neutralized by pre-setting their state (fetch_started_ + demo phrase;
seed_migration_step_ set directly). Verified: 0 RPC/secret/send calls.
- Catalog (v1): tabs + the bool-flag modals (import/export key, backup,
seed-backup, about, settings) + the 6 migrate-to-seed steps + 4 wizard steps
+ lock/warmup/not-ready overlays + the send-confirm popup (via a new
ui::SweepShowSendConfirm debug hook, since its state is send_tab-static).
- Overlay/blur surfaces settle 8 frames (blur backdrop freeze); setup re-runs
each frame (OpenPopup popups must re-fire). updateScreenshotSweep now runs
before the first-run-wizard early-return in render() so the sweep advances
while a wizard step is shown. main.cpp drops vsync during a sweep so it isn't
throttled to the compositor's unfocused rate. Output:
<config>/screenshots-full/<surface>/<skin>.png + index.md.
- DRAGONX_FULL_SWEEP=1 runs the sweep headlessly then quits (CI / verification).
Verified end-to-end under WSLg: 288 PNGs (9 skins x 32 surfaces), all 1200x720,
[Sweep] done, clean quit, no daemon. Byte-identity across runs holds only for
static skins on data-free surfaces — the rest vary by animated theme effects /
live price / relative timestamps (expected for a review tool, not a bug).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
checkAutoLock() called App::lockWallet(), which early-returns without an
rpc_ handle — so lite wallets, which have no daemon, never idle-locked and
stayed unlocked indefinitely. Route lite through lockLiteWallet() instead,
which locks the backend and tears down the chat session so no decrypted
store / unlocked DB key survives the idle lock. Full-node builds keep
lite_wallet_ null, so the branch is a no-op there and behaviour is unchanged.
The idle timer (last_interaction_, reset on ImGui input at the top of
App::update()) is already build-agnostic, so no other wiring is needed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tighten input handling across several dialogs so bad/edge input can't
produce a doomed request or a confusing display:
- app_security: passphrase strength meter now factors character-class
diversity — an all-one-class string downgrades one tier so "aaaaaaaa"
no longer scores as high as a mixed one.
- block_info: clamp the height field to the chain tip and hide/deny
"Next" at the tip (was only yielding a raw RPC error).
- address_label: trim surrounding whitespace before saving; a
whitespace-only label clears it (mirrors clearing the icon).
- send_tab: re-clamp the amount when a Max send has its fee bumped in the
confirm popup (kept the total within budget) and NUL-terminate the
strncpy'd address/memo when a payment URI overwrites the form.
- settings_page: reject an empty/whitespace-only lite wallet path before
dispatching an unusable open/restore request.
- peers_tab: guard ExtractIP against an empty address.
- transaction_details: disable "View in explorer" when the configured
explorer URL is empty/whitespace so we never open a garbage link.
- app: seed-backup "Skip" now requires a second, deliberate click with a
fund-loss warning before it dismisses.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
UI-standardization audit: ~66 call sites still used raw ImGui::Button /
ImGui::SmallButton instead of the app's canonical TactileButton, so those
buttons missed the standard glass fill + rim + tactile overlay (and the
light-theme flat treatment). Convert 59 plain action-button sites across the
wizard, security dialogs, settings, market/console/explorer tabs, and the
address dialogs to material::TactileButton / TactileSmallButton (drop-in:
same signature/return, args preserved verbatim). Danger buttons keep their
PushStyleColor wrappers — Tactile renders through them then overlays.
Deliberately left raw (not plain buttons): InvisibleButton hit-targets, the
frameless transparent-bg collapsible-header toggles (RPC/Debug), the icon-only
pagination chevrons, and the receive All/Z/T segmented filter — a glass rim
would clash with those intentionally borderless/segmented looks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The PIN/passphrase input uses default ImGui framing, which is subtle on the glass
card — when focused there was no clear indication the field is active. Draw an
accent (Primary) ring around the input while it's focused/being edited so it's
obvious the field is ready for typing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regression from the full-window lock overlay: the full-window ##LockInputBlocker
InvisibleButton (drawn first) shadowed the lock card's widgets (PIN/passphrase
input, Unlock button, mode toggle) that overlap it, so clicks never reached them
(dead) and the app's global "hand cursor on clickable hover" fired for the whole
screen. Mark the blocker SetNextItemAllowOverlap() so the later widgets take
hit-test priority — the exact use case in ImGui's docs (invisible button covering
an area where subsequent items are added). The portfolio avoids this by putting
its content in a child window; the lock screen draws direct, so it needs the flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address two GUI-review points:
1. Light themes no longer get a dark modal backdrop: DrawFullWindowBlurBackdrop
now tints toward the theme app background (WithAlpha(Background(), ...)) for
both the opaque base and the frost, so a light theme gets a light frosted
backdrop and a dark theme a dark one. Fixes all blur overlays, not just the
lock screen.
2. The lock screen now blurs the WHOLE window (sidebar included), not just the
content area: renderLockScreen opens a full-viewport borderless overlay window
(##LockOverlay, same pattern as the modal overlays / portfolio, opened from
within ##ContentArea) with an input blocker, instead of drawing in the content
child. Removed the theme-independent dark scrim (contradicted point 1).
Auth logic untouched. Needs GUI re-verify of all auth paths + that the whole
screen (incl. sidebar) obscures on lock and unlock returns cleanly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GUI review (light theme) showed the blurred backdrop is light there, washing out
the (now glass) card. Add a theme-independent dark scrim over the blur so the lock
screen reads as a clearly-dimmed "locked" state and the card stands out in both
light and dark themes — standard lock-screen dimming. Alpha tunable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GUI review showed the lock card was a flat light SurfaceVariant panel that stood
out against the dark glass cards of the other modals. Draw it with the same
GlassPanelSpec (rounding 16, fill 35, border 50) that BeginOverlayDialog uses, so
it renders as the same opaque-dark glass card on the blur backdrop. Visual only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The security DIALOGS (encrypt/decrypt/change-passphrase/remove-encryption, PIN
set/change/remove) already adopted the blur backdrop via the Phase 2 flip (they
use BeginOverlayDialog). This finishes Phase 4 by giving the bespoke lock screen
the same live-blur backdrop over the wallet content — restyle only, auth logic
(PIN/passphrase input, attempt counting, lockout timer) untouched.
The lock screen previously defaulted to backdrop-alpha=0 (wallet fully visible
behind the card); the blur now obscures it — a privacy improvement as well as a
consistency one. Capture-once on lock-open + resize (frame-gap detected via the
shared BlurCaptureStateFor), MarkBlurOverlayDrawn drives the theme-effect
suppression + acrylic re-capture on unlock. The opaque base in
DrawFullWindowBlurBackdrop guarantees the wallet is obscured even if blur is
unavailable (low-spec / acrylic-off / capture failure) — no content leak. UV
mapping is screen-space, so the content-area sub-rect blurs the correct region.
Needs GUI verification of every auth path (PIN/passphrase unlock, lockout).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract App::restartDaemonAfterEncryption(taskName, announceRestartStatus)
from the two verbatim copies in encryptWalletWithPassphrase and
processDeferredEncryption. The two differed only in the async task name and
whether they set connection_status_ = "restarting_after_encryption" first
(the immediate encrypt path does; the deferred path does not) — both
parameterized. The 20x100ms settle loop, cancel/shutdown guards, and
stop/startEmbeddedDaemon sequence are preserved exactly.
Deliberately NOT done: the audit's "decrypt 5-deep callback pyramid" finding.
On inspection the nesting is load-bearing, not gratuitous — WalletSecurityWorkflow
is unsynchronized main-thread-only state, and each step's worker->MainCb bounce
is what keeps its mutations on the main thread (the UI reads snapshot()/importActive()
every frame); collapsing the pipeline onto one worker/async task would race the
UI, and the final restart+import stage requires an AsyncTaskManager::Token that
worker_->post can't supply. Left byte-for-byte intact.
Full-node + Lite build clean; ctest 1/1 (incl. WalletSecurity controller tests);
hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mechanical, behavior-preserving consolidations from the audit. Rendering
extractions were kept byte-equivalent; sites that couldn't be made identical
were left as-is (noted below).
Shared helpers:
- util::truncateMiddle(s,maxLen) / (s,front,back) in text_format.h — replaces
6 file-local middle-ellipsis truncators + several inline substr sites
(send/receive/transactions/balance_recent_tx/explorer + app.cpp + 3 dialogs).
Carries the maxLen<=3 guard, fixing the latent unsigned-underflow copies.
- material::LoadingDots() in draw_helpers.h — one animated-ellipsis source for
8 copy-pasted spinner sites (identical GetTime()*3 phase preserved).
- Reuse the existing FormatHashrate() in explorer_tab + peers_tab (dropped two
inline hashrate ladders).
- material::DrawButtonGlassOverlay() — the glass-fill/rim/tactile overlay block
shared by TactileButton / TactileSmallButton / schema TactileButton.
- material::CollapsibleHeader() — the invisible-button + label + chevron idiom
(3 of 5 settings_page sites; RPC/Debug headers skipped — non-identical).
- material::GlassCardScope (RAII) — the ChannelsSplit/Indent/DrawGlassPanel/
ChannelsMerge card scaffold (5 settings_page cards; About/send/receive skipped
— different padding / logo interleaving).
- material::DialogWarningHeader()/DialogConfirmFooter() — warning header +
50/50 Cancel/danger footer across the confirm dialogs; button height moved
from a hardcoded 40px into ui.toml (components.overlay-dialog.confirm-btn-height).
- File-local enterLowSpec()/exitLowSpec() collapse the 3 low-spec snapshot copies.
i18n: wrapped hardcoded English in the shared balance render paths and the
whole Lite lifecycle/security section in TR(), with English defaults added to
loadBuiltinEnglish() (res/lang/*.json left for the translation tooling — TR
falls back to English, so output is unchanged).
Full-node + Lite build clean; ctest 1/1; hygiene clean. These are rendering
changes — the Settings page (cards/headers/confirm dialogs), all tactile
buttons, and the Lite settings section warrant a screenshot check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Four correctness fixes surfaced by the codebase audit:
1. Lite address-book path bug: AddressBook::getDefaultPath() hardcoded
"ObsidianDragon" while Settings::getDefaultPath() uses DRAGONX_APP_NAME,
so the Lite build wrote addressbook.json into the full-node app's config
dir instead of ObsidianDragonLite/. Now uses DRAGONX_APP_NAME on all
platforms (no macOS path change — the getConfigDir consolidation, which
would move the macOS dir, is deferred).
2. PIN-unlock lockout bypass: the PIN path duplicated unlockWallet's
success/lockout logic and its RPC-error branch bumped the attempt counter
but skipped the escalating-lockout math entirely — a PIN user hitting an
RPC error escaped the lockout curve. Extracted App::applyUnlockSuccess()
and App::applyUnlockFailure() and routed all unlock paths (passphrase,
PIN vault-fail, PIN RPC-fail) through them, so the lockout curve now
applies uniformly. (noVault stays a mode-switch, not a failed attempt.)
3. Witness/rescan reset drift: the ~11-line rescan+witness completion reset
was copy-pasted at four sites (app.cpp x2, app_network.cpp x2); adding a
witness field and missing a copy would leave stale progress. Folded into
App::resetWitnessRescanProgress(). Left the distinct phase-transition
reset in App::update() untouched (it sets witness_phase, not 0).
4. Truncation underflow: send_tab/receive_tab's size_t TruncateAddress
copies computed (maxLen - 3) without guarding maxLen <= 3, which wraps
and throws std::out_of_range on short inputs. Added the guard.
Full-node + Lite build clean; ctest 1/1; source-hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Preserve the previously-uncommitted lite wallet implementation and related dev WIP
under version control:
- src/wallet/ lite services: client bridge, bridge runtime, connection, lifecycle,
sync, gateway, result parsers, state mapper, artifact contract/resolver, refresh
services, UI adapters, wallet_backend/capabilities. (Includes two small M1 fixes:
lifecycle walletReady now parses the response; default chain name -> "main".)
- src/chat/ chat protocol; tests/fixtures/ (lite + hushchat); tools/hushchat_fixture_check.cpp;
scripts/build-lite-backend-artifact.sh.
- Pre-existing modified app_network/security/wizard, network_refresh_service, sidebar,
mining_tab, bootstrap dialog, and version headers captured as-is.
Co-Authored-By: Claude Opus 4.8 (1M context) <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.
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
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
RPC client:
- Add call() overload with per-call timeout parameter
- z_exportwallet uses 300s, z_importwallet uses 1200s timeout
Decrypt wallet (app_security.cpp, app.cpp):
- Show per-step and overall elapsed timers during decrypt flow
- Reduce dialog to 5 steps; close before key import begins
- Run z_importwallet on detached background thread
- Add pulsing "Importing keys..." status bar indicator
- Report success/failure via notifications instead of dialog
RPC caching (app_network.cpp, app.h):
- Cache z_viewtransaction results in viewtx_cache_ across refresh cycles
- Skip RPC calls for already-cached txids (biggest perf win)
- Build confirmed_tx_cache_ for deeply-confirmed transactions
- Clear all caches on disconnect
- Remove unused refreshTransactions() dead code
Peers (app_network.cpp, peers_tab.cpp):
- Route refreshPeerInfo() through fast_worker_ to avoid head-of-line blocking
- Replace footer "Refresh Peers" button with ICON_MD_REFRESH in toggle header
- Refresh button triggers both peer list and full blockchain data refresh
Mining (mining_tab.cpp):
- Allow pool mining toggle when blockchain is not synced
- Pool mining only needs xmrig, not local daemon sync
- Fix z_importwallet to use full path instead of filename only
- Add rescanBlockchain() method that restarts daemon with -rescan flag
- Track rescan progress via daemon output parsing and getrescaninfo RPC
- Display rescan progress in status bar with animated indicator when starting
- Improve dark theme card contrast: lighter surface-variant, tinted borders, stronger rim-light
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)