Add a button to the left of "Open data folder" that opens the ObsidianDragon
config folder (util::Platform::getObsidianDragonDir() — ~/.config/ObsidianDragon
etc.: settings, themes, logs), distinct from the daemon data/blockchain folder.
The two buttons render as a right-aligned group on the data-dir row, each an
explicit button-font width so the group's right edge lands exactly on the card
edge (no overflow). New i18n: settings_open_app_dir / tt_open_app_dir.
Full-node build clean; hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Single-column left every row hugging the left with a blank right half (and a
split RPC row); two-column variants left a bottom-corner gap. Rework the card as
a 2x2 grid on wide full-node windows so both halves carry real content:
TOP-LEFT NODE/Data | TOP-RIGHT RPC
BOTTOM-LEFT SECURITY | BOTTOM-RIGHT DAEMON BINARY
with the two top cells sharing a row bottom, a thin vertical + horizontal
divider, and the Daemon "Advanced" destructive buttons dropping to a full-width
4-button strip below the grid when expanded.
Gated behind one `useGrid` flag (supportsFullNodeLifecycleActions() &&
availWidth >= node-grid-breakpoint, default 900). When false — narrow windows and
lite — it falls through to the existing single full-width column, unchanged
(one-flag-revertible). Wallet Size moved to its own row (removes a mid-row gap in
both layouts). RPC / Security / Daemon rebased to per-cell X/width via aliases;
Advanced buttons factored into one lambda so the disabled predicates live in a
single place. Design produced + adversarially critiqued via a multi-agent pass.
New: components.settings-page.node-grid-breakpoint (ui.toml), "rpc_connection"
i18n string. Full-node + lite build clean; ctest green; hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build the configurable-portfolio UI on the persisted model:
- The portfolio card keeps the "all funds" summary and now renders each custom
entry below it as a compact row (label · summed DRGX · USD), live-computed via
SumPortfolioBalance over the wallet's per-address balances; the card grows to
fit the entries.
- A right-aligned "Manage…" button on the card header opens a modal editor
(material overlay dialog): list entries with Edit/Delete, "Add entry", and an
edit form = label field + quick "All shielded / All transparent / Clear"
selectors + a checklist of wallet addresses (with balances). Persists to
Settings on each mutation.
- i18n English defaults for the new portfolio_* strings.
- Test namespace fix (AddressInfo is dragonx::, not dragonx::data::).
Full-node + lite build clean; ctest green; source hygiene clean. Remaining:
the Overview-tab right-click "add/remove address to a label" integration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zero-behavior-change cleanup ahead of the console refactor:
- Delete dead API/members: addCommandResult (unused), handleSelection (declared, no
def), isAutoScrollEnabled (no callers), and scroll_to_bottom_ (written 8× but never
read) with all its writes.
- Drop the unused screenToTextPos `line_height` parameter (+ 4 call sites) and the unused
`segEnd` local — clears the two -Wunused warnings.
- Remove the toolbar filter checkboxes' dead `static bool s_prev_*` change-detectors,
whose only effect was setting the dead scroll_to_bottom_.
- Fix the console_new_lines format/arg mismatch (format takes one %d; the call passed a
spurious plural-suffix arg).
- Fix the channel/color incoherence: a "[daemon] error:" line kept red text but got a
blue daemon bar — the prefix no longer downgrades an error channel.
- Delete 11 orphaned lite_console_* i18n keys left over from the console merge (keep
lite_console_help_passthrough).
Full-node + lite build clean, ctest green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Right-clicking the output now offers "Copy <value>" when a long alphanumeric token
(txid/blockhash/address, >=16 chars) is under the cursor — resolved via screenToTextPos.
Uses right-click so it never conflicts with the left-drag text selection.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
renderOutput already draws a clickable "N new lines" jump-to-bottom indicator
(console_tab.cpp ~996), so the pill added earlier duplicated it — and reused the
"console_new_lines" format string incorrectly. Remove the pill and the stray i18n entry;
the existing indicator stands.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Highlight the matched substring (case-insensitive) in every visible line with a
translucent yellow behind the text, drawn per wrap-segment like the selection highlight.
- Show a live match count next to the filter input ("N matches", red when zero).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surface new_lines_since_scroll_ (already tracked) as a floating pill at the output
panel's bottom-right whenever the user is scrolled up with unseen output; clicking it
re-enables auto-scroll and snaps to the bottom.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The two consoles were separate implementations (the rich full-node ConsoleTab and a
minimal RenderLiteConsoleTab). Introduce ConsoleCommandExecutor to abstract the only
real differences — command execution + log source — so both variants share the one rich
terminal (the lite console now gains selection, zoom, copy, and JSON-colored output).
- console_command_executor.{h,cpp}: interface + FullNodeConsoleExecutor (dragonxd log
ingestion + RPC command execution, result queue) and LiteConsoleExecutor (lite
diagnostics ring + backend runConsoleCommand/takeConsoleResult, connection/sync status).
- ConsoleTab::render() now takes a ConsoleCommandExecutor&; log ingestion, command
submission, and command results flow through it. The RPC command-reference popup and
the daemon/errors/rpc-trace/app filter toggles are gated on the executor's capabilities;
the toolbar status dot + a status header come from the executor.
- Safety preserved: `clear`/`cls` is intercepted as a view-only clear in the shared UI so
it is NEVER forwarded (the lite backend's `clear` wipes tx history); command output is
not persisted to diagnostics.
- App owns the executor (created per variant) and routes both NavPages to console_tab_;
lite_console_tab.{h,cpp} deleted.
Needs runtime verification of BOTH consoles (full-node RPC + lite: clear, seed/export
secrecy, sync status, command results).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Fetch /coins/dragonx-2/tickers once per session (App::refreshExchanges, reusing the
price-fetch worker path) and build the exchange registry at runtime into
state.market.exchanges; parseCoinGeckoTickers groups tickers by venue. The Market tab
sources from this live list and falls back to the compiled-in registry (now including
OurBit + Nonkyc.io) when offline. Fixes the missing OurBit pair and auto-tracks future
listings.
- Fix stale selection defaults (TradeOgre / DRGX-BTC -> Nonkyc.io / DRGX/USDT) that never
matched the registry, and make the attribution generic ("Price data from CoinGecko").
- Reframe the single-asset portfolio card as a DRGX holdings dashboard: relabel to
"MY DRGX" and show the 24h change on the holdings value (colored by direction).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Data + service layer for supporting multiple mining pools:
- pool_registry: KnownPool table (pool.dragonx.is + pool.dragonx.cc) carrying
each pool's stratum host, xmrig algo, and stats-API schema; pure helpers
findKnownPoolByUrl / resolvePoolAlgo / parsePoolHashrate / chooseWeightedPool
(weighted-random by hashrate with incumbent stickiness).
- pool_stats_service: background, schema-aware hashrate fetcher (libcurl, TLS,
browser UA for Cloudflare) modelled on XmrigUpdater.
- settings: pool_select_mode (manual | auto_balance), string-serialized.
- i18n strings + unit tests (both stats schemas, algo resolution, weighted pick).
Note: pool.dragonx.cc's stratum host is us.dragonx.cc:3333 — the .cc domain is
only the Cloudflare-proxied web/API host and does not accept stratum on :3333.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a full-node daemon updater (util/DaemonUpdater + daemon_download_dialog)
reachable from Settings -> NODE & SECURITY: downloads/verifies (SHA-256 +
enforced ed25519 signature) and atomically installs the latest dragonxd from
the project Gitea, with a "Restart daemon now" step. Add a shared "Browse all
releases..." picker (release_list_view) to both the miner and daemon updaters
so users can pin older/pre-release builds. Pure no-I/O cores
(daemon_updater_core / xmrig_updater_core) are unit-tested; sign-daemon-release.sh
signs release archives offline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verified by running the app against a live node and watching a real rescan. Three
issues that only surfaced at runtime:
- Wrong RPC name: this daemon (hush/komodo) exposes the runtime rescan as
"rescan <height>", not bitcoin's "rescanblockchain". runtimeRescan() and
RPCClient::rescanBlockchain() used the bitcoin name and failed with "Method not
found" on every node. Corrected to "rescan".
- Witness/rescan progress never surfaced during a rescan: the daemon-output parser
that drives it was gated behind rpcConnected, but a heavy rescan holds cs_main so
getinfo times out and the RPC reads disconnected — silencing the parser exactly
when it's needed. The parser reads the daemon's stdout pipe (no RPC), so it now
runs whenever the daemon process is alive. It also now parses INLINE on the main
thread instead of via fast_worker_, so it can't be starved when the worker is
blocked on a getrescaninfo call (which waits on cs_main during a witness rebuild).
- Witness rebuild has TWO sub-phases with different scales — the initial-witness
pass ("Setting Initial Sapling Witness for tx <hash>, <i> of <N>") and the cache
walk ("Building Witnesses for block <h> <frac> complete, <n> remaining"). Tracking
them with one monotonic value pinned the bar at the initial pass's ~100% through
the whole cache walk. They're now tracked as distinct phases (witness_phase) with
their own monotonic progress and labels ("Setting witnesses" vs "Rebuilding
witnesses"), so neither resets/bounces and the long phase shows real movement.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The daemon's post-rescan witness rebuild ("Building Witnesses for block ...")
is a distinct, often-long phase that previously showed only as an indeterminate
"Rescanning..." with no progress. Parse the daemon's witness-build log lines and
surface a dedicated progress indicator.
- Parse "Building Witnesses for block <h> <frac> complete, <n> remaining" (and the
earlier "Setting Initial Sapling Witness for tx ..., <i> of <m>") from daemon
output, extracting a 0..1 fraction and remaining-block count.
- New SyncInfo fields building_witnesses / witness_progress / witness_remaining,
cleared at every rescan-completion site (warmup-end, getrescaninfo poll, runtime
rescan callback, daemon-log "finished").
- Status bar shows "Rebuilding witnesses NN%" (priority over the generic rescan
text); the loading overlay (shown during -rescan warmup) gets a labelled witness
progress bar with the remaining-block count.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A wallet bootstrapped from a snapshot keeps its wallet.dat but never rescans, so
its spent-state is stale and the first send tries to spend already-spent notes and
is rejected. The startup -rescan flag can't fix it either: the snapshot lacks the
pre-snapshot block history -rescan needs, so it errors. The working fix is a runtime
rescanblockchain RPC from a height the snapshot actually has.
- Add App::runtimeRescan(startHeight): runs rescanblockchain via the worker, drives
the rescanning UI state, and owns completion via the RPC callback (getrescaninfo
is unavailable on this daemon). Suppresses the per-second mining/rescan pollers
and the Core/balance/tx refreshes while the daemon holds cs_main for the scan.
- Add App::detectLowestAvailableBlockHeight(): async binary search via getblock for
the lowest height whose block data is on disk → the snapshot base, and whether the
node still has full history.
- Auto-reconcile after bootstrap: both completion sites (wizard + Settings download
dialog) mark a pending rescan; once the daemon is back up and the tip is known,
detect the base and runtimeRescan() from it (or -rescan restart on a full node).
- Settings "Rescan Blockchain" now probes first: full-history nodes get the existing
-rescan restart; bootstrapped/pruned nodes get a prompt pre-filled with the
detected base height that runs the runtime rescan.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Delete Blockchain and Repair Wallet now sit on one line, paired with the same
uniform button width as Test Connection / Rescan Blockchain (a clean 2-column grid;
all maintenance buttons share one rowBtnW sized across the four labels).
- The Daemon binary panel lays Installed and Bundled side by side across the node
column (version + size·date | version + size) instead of stacked narrow lines, with
the status line spanning underneath and Install bundled / Refresh paired below.
- Shorten the install button label to "Install bundled" so it fits the shared width;
the tooltip still explains the full action. Date shown as YYYY-MM-DD.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously the wallet re-extracted the bundled dragonxd on startup whenever the
installed binary's size differed from the bundle ("stale" overwrite), which could
replace a node a user had deliberately placed in dragonx/.
Now dragonx binaries (dragonxd/cli/tx) are auto-placed ONLY when missing — never
auto-overwritten on a size mismatch (needsParamsExtraction + extractEmbeddedResources).
Params/asmap keep their size-based refresh; a daemon dropped next to the wallet exe
still takes priority and is never touched.
Replacing the daemon is now an explicit action: Settings → "Daemon binary" reports the
installed binary's version (scanned from the file), size and modified date, compares it
to the version bundled in this build, and offers an "Install bundled daemon" button.
That stops the node, overwrites dragonxd/cli/tx with the bundled copies (waiting for the
process to release the file lock), and restarts — wallet/keys/chain data untouched.
Adds resources::{getInstalledDaemonInfo,getBundledDaemonInfo,reextractBundledDaemon}
(+ a version-string scanner) and App::reinstallBundledDaemon().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a note's stored record is corrupt or its tx isn't in the canonical chain,
z_sendmany fails to build a valid sapling spend proof even after a full -rescan,
because a plain rescan replays witnesses but keeps the existing tx/note records.
The zcashd repair for this is -zapwallettxes=2, which deletes all wallet tx/note
records and rebuilds them from the chain (keys/addresses preserved).
Adds a RepairWallet lifecycle operation that mirrors the existing -rescan plumbing
(one-shot zapOnNextStart flag on the embedded daemon; -zapwallettxes=2 implies and
supersedes -rescan), an App::repairWallet() that reuses the rescan status UI (so the
status bar + warmup-end completion detection apply), and a confirmed "Repair Wallet"
button + dialog in Settings → node maintenance (embedded daemon only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two related fixes for the post-bootstrap "send fails / rescan stuck at 99%" trap:
1) Rescan completion now keys off warmup-end. A -rescan runs entirely inside daemon
warmup (every RPC returns -28 until it finishes), so warmup completing IS the rescan
completing. The old detectors relied on getrescaninfo (which some daemons answer with
"Method not found") or a "Done rescanning"/bench log line the daemon may never print,
leaving the status bar stuck at 99% — so users killed the rescan before it finished.
When warmup ends and a rescan was confirmed active, clear the rescan state, flip to
100%, refresh history/balance, and toast completion.
2) z_sendmany failures that mean stale shielded note data (shielded-requirements-not-met,
missing sapling anchor, invalid sapling spend proof, bad-txns-sapling-*) now append a
plain-language hint telling the user to run a full rescan, instead of surfacing only the
raw daemon string.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On first run the lite welcome screen's "Restore from seed" button only showed a
hint toast and bounced the user to Settings, dismissing the welcome with no
wallet open — it never prompted for a seed. Add a real restore step to the
welcome wizard: a seed-phrase field + optional birthday height, which calls
beginRestoreWalletAsync() (same server failover as create/open), shows
"Restoring…" progress, then completes (wallet syncs) or surfaces the error to
retry. The seed buffer is wiped on success/Back and in finish().
(The Settings -> Lite -> Restore path already prompted for a seed; this fixes
the first-run welcome path.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an explicit button in Settings that opens the wallet/blockchain data
directory (getDragonXDataDir()) in the OS file manager via the existing
Platform::openFolder(). Placed in the full-node connection section (next to the
data-dir path, which was only a subtle clickable link) and in the lite section
(always available). i18n strings added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a maintenance option for the lite wallet to re-download and re-scan every
block from the lite server — useful when balances or history look wrong.
- LiteWalletController::startRescan() runs the backend `rescan` command (which
clears the wallet's synced block cache and re-syncs from its birthday) on a
detached thread, reusing the existing sync progress/refresh machinery: it
resets syncDone_ so refreshModel() shows progress again and refreshes data on
completion. No-op if no wallet is open or a scan is already running.
- scanInProgress() exposes the initial-sync-or-rescan state.
- Settings (lite, open wallet) gains a "Redownload blocks" button behind a
confirmation modal, disabled while a scan is running. i18n strings added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The console mixed RPC traces, daemon output, and the wallet's own "[app] ..."
log lines with no way to hide the latter. Add an "App" checkbox alongside the
existing Daemon/Errors/RPC toggles. Since [app] lines share COLOR_INFO with
other info text, the filter matches them by their "[app] " prefix rather than by
color. Default on; unit test + i18n added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a fuzzy mode to the explorer search: a non-numeric, non-full-hash query now
filters the list to cached blocks whose hash (or height text) contains the query
substring, live as you type. Backed by a new ExplorerBlockCache::searchBlocks()
(SQLite LIKE with escaped wildcards), memoized per query so it doesn't hit the DB
every frame. Exact queries still navigate precisely: a block height re-anchors
the list, and a full 64-char hash is resolved via RPC. Row clicks still open the
detail modal. Empty results show "No matching cached blocks".
Note: fuzzy matching covers cached (browsed/prefetched) blocks only — the daemon
has no partial-hash index — while exact height/hash lookups reach any block.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Rescan blockchain and Restart daemon buttons fired immediately on click —
both are disruptive (long offline rescan / connection drop) and easy to hit by
accident. Route them through confirmation modals, matching the existing
delete-blockchain / clear-ztx confirmations: the button now sets a confirm flag
and an overlay dialog performs the action only on explicit confirm. New i18n
strings added with English defaults.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a sort selector next to the type filter with four modes: Newest first
(default), Oldest first, Largest amount, Smallest amount. The mode folds into
the merged-list memoization cache key (so the list re-sorts only when the mode
changes) and the comparator branches on it, keeping txid as a deterministic
tiebreak. Changing the sort resets to page 1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
History streams in over many refresh cycles (the incremental shielded scan
walks every z-address), so the first batch appears long before the list is
complete — with no indication more is still coming. The existing loading banner
deliberately goes quiet once any rows are on screen.
Track whether the first full shielded scan has finished
(initial_history_scan_complete_) and, until it has, surface a progress percentage
(fraction of z-addresses scanned) in transactionRefreshProgressText() — which the
History tab already renders as its pulsing loading indicator. Goes quiet once the
first scan completes; routine per-block re-scans don't re-trigger it. Reset on a
full history invalidation (rescan / session reset) so it shows again on reload.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Auto-clear: the Copy button now routes through App::copySecretToClipboard, so a copied
private/viewing key is wiped from the clipboard after ~45s (same protection as the seed) with
a "auto-clears" notice — instead of the raw SetClipboardText that left it indefinitely.
- QR: once the key is revealed, a Show/Hide QR toggle renders the key's QR inline (via the same
GenerateQRTexture/RenderQRCode widget the Receive tab uses) for scanning into another wallet.
The QR texture is cached, regenerated on key change, and freed on hide/close/dismiss; hiding the
key also hides its QR.
- Actions row tightened to Show/Hide · Copy · QR, and the key + QR texture are now cleared on any
dismissal (Close button, scrim click, Esc), not just the Close button.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The address list supported two drag gestures that collided: dragging a row onto another
transferred funds, dragging into a gap reordered. Since rows are contiguous, a reorder-drag was
almost always over another row, so it triggered a fund transfer instead of reordering.
Disambiguate by WHERE on the target row the drag is released (user's suggestion): the top/bottom
~30% edge bands = reorder (an insertion line is shown), the centre = transfer (the row highlights).
A zero-balance row or an off-row drop always reorders. Tooltip and i18n hint updated to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two failure modes left the wallet stuck on a silent "connecting / Starting
dragonxd…" spinner with no path forward:
1. Stale external-daemon latch. EmbeddedDaemon::start() sets
external_daemon_detected_ whenever the RPC port was busy at a prior attempt
and never re-checks it, so tryConnect's no-config branch trusted that latch
and waited forever for a config the phantom would never write — even after a
stale/half-dead process freed the port. Now the port is re-evaluated LIVE
(EmbeddedDaemon::isRpcPortInUse()) each attempt: if it's genuinely busy we
keep waiting (and, after a bounded ~20s with no config, warn that whatever
owns the port isn't a usable DragonX node and how to fix it); if it's free we
fall through and start our own daemon.
2. Silent start failure. When startEmbeddedDaemon() failed (binary not found,
Sapling params missing, spawn failure) the status stayed on "Starting
dragonxd…" with the real reason only in a VERBOSE log. Now the reason
(daemon_controller_->lastError()) is surfaced once as a sticky error
notification, with a short "Couldn't start dragonxd" status.
Both counters reset on a successful connect so the messages re-arm for the next
disconnect. Lite is unaffected (tryConnect returns early for lite builds).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The lite backend's litelib_execute() is the same command interface as
silentdragonxlite-cli (balance, info, height, list, notes, addresses, sync,
syncstatus, new, send, shield, encrypt, …), so the lite Console can be a real
interactive console — like the full-node RPC console — instead of a read-only
diagnostics log.
Controller: add an async arbitrary-command runner mirroring the broadcast
pattern — runConsoleCommand() splits "<command> [args]" (the first token is the
command, the remainder is passed as the single arg string litelib_execute
expects, since it does NOT whitespace-split), runs the bridge call on a detached
thread that captures the shared bridge (never `this`), and delivers the result
to a main-thread slot drained by takeConsoleResult(). Results are NEVER routed
through LiteDiagnostics (seed/export can return secrets).
Console tab: a command input (Enter to run, Up/Down history via the shared
console_input_model helpers) over a unified scroll buffer that interleaves the
automatic diagnostics events with user command I/O, colour-coded, with the live
status header preserved. The input is disabled while a command runs.
Two backend footguns are intercepted at the UI layer before forwarding:
`clear` (the backend command WIPES wallet tx history — re-bound to clearing the
view, what the user expects) and `quit`/`exit` (would only save; the embedded
backend must stay running with the app).
Test: runConsoleCommand drives the fake backend (info -> raw response; "new zs"
-> exercises the command/arg split; blank line rejected).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Console could look empty if the wallet produced few events. Make it useful
in every state and remove a cross-platform footgun:
- Add a live status header read straight from the controller (connected /
connecting / disconnected, sync %, and the last open error) — independent of the
diagnostics event log, so the Console always shows the current connection +
wallet-open state even when the log is sparse.
- Move LiteDiagnostics::instance() into a single .cpp so there is exactly one
instance across the binary, rather than relying on the linker folding an
inline-function static across translation units (a known fragility, especially
on mingw/Windows — the most likely cause of a stuck-empty event log there).
Verified the writer and reader share one instance on Linux; builds clean for
full-node, lite, and Windows cross-compile; tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The lite variant had no visibility into why a wallet failed to open — just a
"disconnected" spinner. Add a lite-only Console tab (full-node keeps its RPC
console) that shows a live diagnostic log.
- LiteDiagnostics: a small thread-safe, bounded ring buffer (header-only). The
controller writes to it from its background threads: each failover server
attempt and result, wallet open/create/restore outcomes, sync start, and
blocked-open reasons. The App logs controller (re)builds with the preferred
server.
- lite_console_tab: a terminal-styled, read-only view of the log (newest at the
bottom, error/success lines coloured) with Clear / Copy / Auto-scroll. Reachable
even when the wallet is locked (it's diagnostics, no secrets). Registered as
NavPage::LiteConsole, gated lite-only via WalletUiSurface::LiteConsole.
A unit test drives an open-with-failover and asserts the log records the
connection attempt and the successful open. Built clean for full-node, lite, and
Windows cross-compile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Many strings are used directly as printf/ImGui format strings, and translations
are loaded from user/installer-modifiable JSON with no validation. A translated
value that drops or changes a conversion specifier would be passed to printf with
mismatched varargs (undefined behavior) on a wallet screen.
overlayTranslations() now compares each translated value's argument signature
against the English source and keeps English on mismatch. Also adds the
send_status_unconfirmed string used by the deferred-send-result path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a status panel at the top of the Network tab driven by the live WalletState:
- Connection: a colored dot + Connected / Syncing / Not connected, with the in-use server host
(or "Random server") and its latency on the right.
- Sync: "<pct>% · <walletHeight> / <chainHeight>" while syncing (with a thin progress bar),
"Synced · block N" when complete, or "No wallet open" when disconnected.
Reads app->state().sync (populated by the lite refresh: progress / wallet+chain height / complete)
and state().connected (= walletOpen). Advances with a Dummy so the bounds grow correctly.
Both variants build; suite passes; hygiene clean; lite GUI smoke OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A lite-wallet-only "Network" tab (full-node keeps the Peers tab; exactly one shows per variant)
to manage lightwalletd servers, replacing the basic selector that was in Settings.
- Card list of servers with per-server latency + status dot, DNS host + resolved IP, and an
Official/Custom pill. Official DragonX servers get a glowing outline.
- Pick a server (Sticky) by clicking its card, or toggle "use a random server" (Random mode);
selection applies immediately (App::rebuildLiteWallet(force=true) tears down + rebuilds the
controller against the new server and resyncs — its dtor detaches the uninterruptible sync
thread, so this doesn't block).
- Add custom servers; hide/unhide servers (persisted set, revealed by a "Show hidden" toggle).
- Latency/IP come from a new background probe (util/LiteServerProbe): libcurl CONNECT_ONLY does
the TCP+TLS handshake (works for gRPC lightwalletd, no HTTP response needed), recording
APPCONNECT_TIME as latency and CURLINFO_PRIMARY_IP. Auto-runs on tab open + a Refresh button.
Wiring: WalletUiSurface::LiteNetwork (gated !fullNodePagesAvailable) + NavPage::LiteNetwork in
the sidebar + app.cpp dispatch; settings gains a hidden-servers set; isOfficialLiteServer() added
to lite_connection_service. The Settings page lite-server selector + its plumbing are removed
(single source of truth = the tab).
Reuses the existing server model (LiteServerPreference, Sticky/Random, selectLiteServer) and UI
primitives (DrawGlassPanel, ThemeEffects glow, peers-tab ping-dot idiom). Unit-tested
(liteServerHost, isOfficialLiteServer) + an env-gated live probe (verified vs lite.dragonx.is:
online, latency, IP). Both variants + lite-backend build; suite passes; hygiene clean; GUI
smoke-launched without crash.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Relocate the miner-update control from a standalone full-width button into the mining-control
header row, immediately left of the benchmark button:
- The button now shows the latest available version ("Update <tag>"), with the current installed
version as text to its left ("Current: <tag>" / "none").
- A one-shot background version check (util::XmrigUpdater::startCheck) runs the first time the pool
section is shown, so the latest tag can be displayed; until it arrives the button reads
"Update miner…". Clicking opens the existing dialog; disabled (greyed, with tooltip) while the
miner is running.
- New i18n keys: xmrig_update_short, xmrig_current, xmrig_none.
Both variants build; suite passes; GUI smoke-launched without crash.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the English string literals in the miner-update dialog + the "Update miner…" mining-tab
button/tooltip with TR() keys, and register their English text in i18n.cpp's loadBuiltinEnglish()
(the in-code English fallback that non-English locales overlay). Reuses the existing cancel/close/
retry keys. Labeled values use a "%s %s" literal format with a TR'd label (no -Wformat-security
risk). Non-English locales fall back to English for the new xmrig_* keys until translations are
added to res/lang/*.json.
Both variants build; suite passes; hygiene clean.
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"