- The import-key sweep and migrate-to-seed flows set several user-visible
status/error strings (shown via sweep_status_ / seed_migration_status_ /
rescan_status) as bare English; routed them through TR() with additive keys.
- Extended scripts/check-source-hygiene.sh with two low-false-positive content
guards so this debt can't regrow: a bare-English migrate/sweep status
assignment, and a Tactile button sized with a raw (unscaled) pixel literal.
Passes clean on the tree (362 files); the dev sweep harness is allowlisted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Users who keep funds apart under different labelled addresses can now
shed notes (smaller wallet, faster scans/witness rebuilds) without the
funds being merged into a single address.
- Merge/Consolidate dialog: "Where should the funds go?" - Into one
address (unchanged) or Keep in each address. The latter lists shielded
addresses with 2+ spendable notes (label, note count, amount;
too-small ones disabled) and merges each address's notes back into
that address with z_mergetoaddress [addr] addr. Addresses with more
notes than the batch size get several batches (the daemon locks a
batch's notes before returning, so calls take disjoint notes); the
dialog suggests a second run after those confirm. Per-address
progress; ops keep running if the dialog is closed. The Consolidate
(wallet-size) preset opens in this mode; Merge keeps "Into one".
Shielded only: a transparent address can't take a merge's output.
- services/note_consolidation: groupSpendableNotes, worthConsolidating,
estimateBatches, consolidateInPlace (pure; driven by a merge-call
function), with unit tests against a stand-in following dragonxd's
rules.
- fake_dragonxd: optional note wallet (FAKE_WALLET_NOTES,
FAKE_MERGE_SECS) with z_mergetoaddress and its operations.
- i18n: 17 new keys in all 8 languages; CJK subset rebuilt.
- UI sweep: modal-consolidate target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow-up to 5246898. A transparent address only ever receives mining
rewards on DragonX (private chain), so:
- One explainer ("Receives solo-mining rewards only - the network rejects
payments to it. Rewards are shielded automatically once they mature.")
on the Overview row hover, the Receive picker hover, the Receive card
and the Validate Address result (now typed "Mining address
(transparent)").
- Receive: a selected mining address shows the explainer and a "Use a
shielded address" button (creates one if none exists) instead of the
payment request, and a "Not for payments" panel instead of a QR code.
The default selection prefers a shielded address; the "All" filter
hides empty mining addresses unless solo mining or marked as a mining
address; the filter reads All / Shielded / Mining.
- Request Payment: no transparent section, never prefilled with one.
- Overview: no "Mining - Mining" tag on a marked transparent row.
- FAQ: address answers rewritten; the send and memo answers no longer
imply you can pay a transparent address. All 8 languages.
i18n: 4 new keys, 6 FAQ answers updated in place, 2 dead keys removed,
CJK subset rebuilt. UI sweep: receive-mining-address and
modal-validate-mining targets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DragonX is a private chain (-ac_private=1): consensus rejects any
non-coinbase output to a transparent address, so a t-address can only
ever receive mining rewards, and its coins can only move to a shielded
address. The wallet treated them like ordinary recipients.
Block paying one:
- Send: a transparent recipient shows "Mining address - can't be paid"
and disables Review Send (tooltip explains; ask for a zs... address).
Typing, paste, contacts and payment URIs all go through this field.
- Overview drag-to-transfer: a mining-address row is never a transfer
target (it only reorders).
- Pool payout must be shielded (pools pay with ordinary transactions):
field tooltip, disabled Start button and the start check say why.
- isValidRecipientAddress() is now shielded-only; new isDragonXAddress()
and classifyRecipient() (Shielded / MiningOnly / Invalid).
Call them mining addresses:
- Overview: "Mining rewards" card (amber "Not yet shielded" when there
is a balance; hover explains; click opens the Shield dialog), "New
mining address" button with a tooltip, rows typed "Mining".
- QR / label dialogs, portfolio filter, contact badge ("Mining only"),
Receive notifications.
i18n: 17 new keys in all 8 languages, 12 dead keys removed, CJK subset
rebuilt. Tests: recipient classification, translation completeness.
UI sweep: new "send-mining-recipient" target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every operation that stops the node (restart, rescan, repair, delete
chain data, reinstall, bootstrap, wallet switch/adopt/restore/rebuild,
wizard restart, encrypt, decrypt) now:
- refuses up front while the node is rebuilding witnesses or has not
answered for 30s, with a "try again when it's done" warning, and sends
no stop;
- goes ahead only once the node's PROCESS has exited, not its RPC port.
A stop that doesn't finish within 120s cancels the operation (warning;
a cancelled rescan/repair disarms its flags); nothing is started over
the old node, chain data isn't deleted, wallet.dat isn't swapped.
Specifics:
- stopDaemonForWalletSwitch() no longer reports "stopped" when the app
is quitting (adopt could swap wallet.dat under a live node).
- Decrypt waited a fixed 3s after "stop" before renaming wallet.dat; it
now waits for the process to exit, and scrubs the key export if not.
- Bootstrap waits for the process before cleanChainData and stops the
node through the stop routine instead of the shared RPC client.
- StopScope::Connected lets switch/reinstall/decrypt stop an adopted or
external node through the same never-kill routine.
- A bounded stop that can't reach the node (a foreign node refusing our
credentials) gives up after 5s instead of 120s.
- macOS: isDaemonProcessRunning() checks processes (libproc), not the
RPC port.
- rpcBusyWhileAlive() also reads the busy gate: since the gate, the
first timeout usually lands on another lane and the 30s clock never
started (also weakened the quit prompt for a silent rebuild).
- Notifications render during the first-run wizard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A node rebuilding note witnesses for an hour was shown as "Starting
DragonX node... Launching dragonxd", which invited closing it.
- Overlay, a new NodeBusy banner and the status bar now say what the node
is doing (rebuilding witnesses / catching up on blocks / up but not
answering), with the elapsed time and Phase-1 block progress. A rebuild
during startup warm-up replaces the stale "-28 Activating best chain"
text, and the overlay re-applies its text once a second instead of only
on a connect attempt (30s apart while the node is busy).
- Quit prompt: elapsed time, "Keep node running & quit" is the default,
"Stop anyway & quit" becomes "Stop node & wait" (it no longer kills).
- A reinstall skipped because the node is still running now notifies.
- Status bar: don't repeat the warm-up status on the right, and don't
print "Connected" there in non-English locales.
- i18n: 31 new keys in all 8 languages; 10 dead force-quit/old-modal keys
removed; CJK subset rebuilt for the new glyphs.
- Tests: chooseDaemonStatus, NodeBusy banner priority, and every new key
present in all 8 JSONs with matching printf conversions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, stop() with no live process of its own looked up the first
"dragonxd.exe" by name and TerminateProcess'd it after the grace period:
an old node still shutting down (how an in-progress shutdown was killed),
a node on another datadir, or the user's main node. It was reached on a
lock-gated close, for an external node, after the tracked process had
exited, and from ~EmbeddedDaemon's stop(3000).
- Windows stop(): drop the by-name branch; with no tracked process it
logs and returns. findProcessByName is detection only.
- ~EmbeddedDaemon no longer calls stop(); it stops watching and releases
its handles. stop() returns whether the process exited on its own.
- SeedWalletCreator never sent its isolated node an RPC stop, so on
Windows that node was always force-killed holding the wallet.dat it
had just created, and the stack object's destructor could then kill
the main node by name. It now stops the node over the node's own RPC
on every path. A node that still had to be force-killed fails the
creation (seed wiped; nothing has been swept into it yet), so an
uncleanly closed wallet is never adopted.
- Reinstall waited only for the RPC port, which closes long before the
process exits (locked exe on Windows, held datadir lock everywhere).
It now waits for the process to exit and otherwise aborts without
touching the binaries or starting a second node.
- Linux isDaemonProcessRunning also matches /proc/<pid>/exe, so a node
whose main thread is already renamed "hush-shutoff" (still holding the
datadir lock) counts as running.
Tests: a Windows real-process test (a fake dragonxd.exe on an isolated
datadir survives stop() and the destructor), a POSIX test that the
destructor leaves the process running, and the pure identity rule.
scripts/test-windows.sh passes the stub path to the Windows suite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/test-windows.sh cross-compiles ObsidianDragonTests.exe (and the
fake_dragonxd stub) with mingw into build/windows-tests, reusing the
toolchain and dependency sources from build/windows, and runs it from
WSL through Windows interop (or wine). All tests pass on Windows.
CMake:
- Windows forced BUILD_TESTING OFF in the cache to keep curl's tests
out, which also removed ObsidianDragonTests from every Windows build.
Hide it from curl with a scoped variable instead, and restore our
cache value afterwards (curl's cmake_dependent_option FORCE-writes
it). Windows still defaults to OFF, so release builds are unchanged.
- ObsidianDragonTests now depends on copy_langs: i18n.cpp includes the
generated embedded/lang_*.h, so a tree that builds only the tests
failed (latent on Linux too).
- Link the Windows system libraries the tests need.
Tests:
- testTmpPath/fixtureDir/removeTree helpers: no hard-coded /tmp, a
runtime fixture-dir override (the compiled-in path is a Linux one),
and best-effort cleanup that can't abort the suite.
- Close the explorer cache before deleting its directory (Windows
can't delete an open file).
- Compile out the address-book and wallet-index round-trips on Windows:
getConfigDir() uses SHGetFolderPath there, which no environment
variable redirects, so they would overwrite the real
%APPDATA%\ObsidianDragon files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pinned librustzcash / transitive crates (notably traitobject 0.1.0) rely on
pre-1.70 trait coherence and fail to compile on newer rustc (E0119), so the backend
must build with 1.63. Add a rust-toolchain.toml in the vendored backend so rustup
auto-selects 1.63 when cargo runs there — no more manual RUSTUP_TOOLCHAIN=1.63.0.
The pin is scoped to the backend tree (repo-root cargo keeps the default toolchain).
Also symlink the pin into the prepared build root so --silentdragonxlitelib-dir
builds honor it too.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The --mac-release path (full-node and --lite-backend) had never run on a real
Mac and broke on several Linux-only assumptions:
- build.sh version parser used GNU sed \+ (BSD sed reads it literally), so it
aborted before compiling — switched to POSIX [[:space:]][[:space:]]*.
- build.sh libsodium universal check used GNU grep \| — switched to grep -E.
- libwebp's cpu.cmake applies -mno-sse2 to its reference DSP files; under a
universal build (-arch arm64;x86_64) that lands on the x86_64 slice, where it
disables _Float16 and breaks the SDK 26 <math.h>. Added an idempotent
FetchContent PATCH_COMMAND (cmake/patch-libwebp-simd.cmake) to neutralize it.
- The SDXL lite backend static lib needs Security + CoreFoundation frameworks on
macOS; added them to the imported dragonx_lite_backend target for APPLE.
- Added a DRAGONX_MAC_ARCHS override plus auto-detect: with --lite-backend the
app is built for the arch(es) the backend .a actually provides (its pinned
ring 0.16.11 is x86_64-only), instead of failing to link a universal app.
- build-lite-backend-artifact.sh uses bash 4+ (mapfile); added a re-exec guard
for macOS's stock bash 3.2 and added bash to setup.sh's macOS core deps.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds `sign-daemon-release.sh release <secret.key> <version>`: zips each staged
prebuilt-binaries/dragonxd-{linux,mac,win}/ into dragonx-<version>-{linux-amd64,
macos,win64}.zip, signs each (detached ed25519), and prints the SHA-256 checksum
table for the release body. Platforms with no staged daemon are skipped; warns
if the signing key doesn't match the pubkey pinned in daemon_updater.h. Keeps the
existing keygen/pubkey/sign subcommands.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Point the miner build at DragonX/drg-xmrig and rename the prebuilt staging
dir xmrig-hac -> drg-xmrig across setup.sh, build.sh, the legacy Windows
script, .gitignore and README.
setup.sh now clones/builds the miner from git.dragonx.is/DragonX/drg-xmrig
(was dragonx/xmrig-hac), and the prebuilt staging dir is renamed
prebuilt-binaries/xmrig-hac -> prebuilt-binaries/drg-xmrig across build.sh, the
legacy Windows build script, .gitignore and README.
Also aligns setup.sh's section-8 directory list with section 7 (it previously
used a bare "xmrig" token, creating a stray empty prebuilt-binaries/xmrig/),
and updates the xmrig_manager.cpp header comment.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dev-branch audit (docs/_archive/security-audit-dev-2026-07-22.md) re-found the
master issues (absent on dev) plus new ones in dev-only code. Applied here;
full-node build + test suite green; the subtle fixes were adversarially re-verified.
Ported from the master remediation (adapted to dev's code):
- bootstrap: reject zip-slip / path-traversal archive members (isSafeArchivePath)
before writing (S2-1). (dev already fail-closes on a missing checksum.)
- xmrig updater: fail closed when a signature is required but no key is pinned (F1-1).
- http_download.httpGetString: 16 MiB hard cap + MAXFILESIZE on the shared
metadata/price fetch (F1-2 / caps the updater + exchange paths at one site).
- rpc_client: explicit SSL_VERIFYPEER/VERIFYHOST (F4-1) and a 256 MiB response
cap in WriteCallback (F4-3).
- lite_connection_service: reject remote http:// lite servers, loopback only (L1-1).
Loopback is matched by a strict dotted-decimal 127.0.0.0/8 check (not a
startsWith("127.") prefix, which would wrongly accept 127.0.0.1.evil.com), with
userinfo/fragment stripping.
- lite controller: propagate encrypt/decrypt save() failure instead of reporting
success (F7-1).
- xmrig_manager: chmod(0600) the pool config before writing secrets (F5-2).
- app: clear the copied secret from the OS clipboard on shutdown (F3b-1).
- export_transactions: neutralize CSV/spreadsheet formula injection (F13-1).
- build pipeline: build-from-source lite backend + remove the self-attested
CMake signature gate (F15-1); pinned+verified appimagetool (F15-3/4);
verified Sapling params in setup.sh (F15-6); build.sh exits 0 on success.
(F14-1 empty-quoted-arg and F8-2 NUL-termination were already fixed on dev.)
Dev-only findings:
- rpc_client.callRaw: scrub the raw buffer + parsed tree (templated scrubJsonSecrets
for ordered_json) so console dumpprivkey/z_exportkey keys don't linger in freed
heap (N1-1).
- seed_wallet_creator: wipe the exported mnemonic on the failure path so a discarded
failed result never carries a live seed (W1-2).
- export_all_keys: write the plaintext key dump 0600 + atomically via
writeFileAtomically (U1-2).
- chat_database: restrict chat_messages.sqlite and its WAL/SHM sidecars to owner-only (C3-1).
Not done (need a decision, documented in the report):
- Chat header metadata (cid/z/p) rides outside the AEAD (C1/C2) — binding it is a
wire-protocol change requiring SilentDragonXLite interop review.
- Bootstrap lacks an offline-rooted signature (S2-2 residual) — needs signing infra.
- Console scrollback retains console-typed key-export output in plaintext (N1-1
residual) — inherent to an echoing console; would need output redaction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The chat "Emoji style: Color" option renders the merged emoji in color (COLR/CPAL
Twemoji) instead of the monochrome NotoEmoji subset. This needs FreeType, which the
default stb_truetype rasterizer can't do for color glyphs.
- Vendor imgui_freetype (matches the bundled 1.92 ImFontLoader API) and embed a 1.4 MB
COLRv0 Twemoji font (no libpng/harfbuzz needed).
- CMake gains an optional FreeType path: native Linux/macOS use the system FreeType via
find_package; the mingw-w64 cross-compile has none, so build.sh --win-release now
cross-builds a minimal static FreeType (scripts/build-freetype-mingw.sh) and passes it
in. Absent FreeType => graceful monochrome fallback, so no build breaks.
- Typography selects the FreeType loader + the color font (LoadColor) when color emoji is
on, else the stb loader + mono subset; toggling reloads the atlas. Both the DX11 and
OpenGL backends already support the 1.92 RGBA dynamic atlas, so color glyphs render.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge a monochrome Noto Emoji subset into the text fonts so chat messages, the
composer, contact names, and memos render emoji (😀🎉 ❤ 🔥👍 …) instead of
tofu.
- Enable IMGUI_USE_WCHAR32 (imconfig.h): emoji live above the BMP (U+1F300+), so
16-bit ImWchar literally can't address them. This widens ImWchar build-wide;
the only ImWchar uses in-tree are glyph-range arrays and one BMP private-use
codepoint, so nothing else is affected. Tests + full build pass.
- Bundle res/fonts/NotoEmoji-Subset.ttf — the OFL monochrome Noto Emoji (color
CBDT/COLR fonts can't be rasterized by ImGui's stb_truetype) pinned to wght=400
and subset to the emoji planes (1411 glyphs, 747 KB). Reproducible via
scripts/build_emoji_subset.py. Embedded via INCBIN like the CJK subset.
- Typography::loadFont merges it (MergeMode) only into the small text fonts
(Body/Subtitle/Caption/Button) — not headers, which don't need 1400 emoji.
The base font keeps precedence for U+2600–26FF, so text-style symbols stay.
Limits: ImGui does no shaping, so single-codepoint emoji render but ZWJ sequences
(family/profession) and regional-indicator flags won't compose; emoji are
monochrome (the OS emoji picker still inputs them fine, and the composer byte
counter already counts their 4-byte UTF-8 cost against the on-chain cap).
Verified headless: sizeof(ImWchar)==4 and every probed emoji is in-font and bakes
into the atlas.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add a chart-style toggle (line vs candlestick) next to the interval buttons,
shown only when the selected range has per-exchange candles (the aggregate /
Live view is line-only). It flips s_mkt.chartStyle; candles draw when OHLC
exists AND the user hasn't switched to the line. Icon reflects the current
style; tooltip says what a click switches to. Two i18n keys across 8 languages.
- Make the price chart ~50% taller: scale the height floor / desired / viewport
cap (110->165, x1.5 desired, 0.22->0.33 of available) for a roomier plot.
Verified via a forced-OHLC render: the taller chart, the toggle button, and
candles with a correct week/day x-axis.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switching exchange pairs (or first load) briefly leaves the chart with no series
while the venue's candles fetch, which showed the bare "No price history
available" empty state. When a chart fetch is in flight — App::isMarketChartLoading()
(the CoinGecko aggregate OR the per-exchange fetch) — draw a spinner + animated
"Loading price history…" in the plot area instead; the "no history" text only
shows when genuinely empty and idle. New i18n key market_chart_loading across all
8 languages. Verified via a forced-state render (spinner + label centered).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With the list now pinned at max height, a few wallets leave blank space below
the cards. Fill it — only when there's real room to spare — with a subtle
centered folder glyph + "Scan a folder to find more wallets" (OnSurfaceDisabled),
so the area reads as a gentle nudge toward the scan action rather than dead
space. Purely decorative (draw-list only, clipped to the list); the actions stay
below. New i18n key wallets_empty_hint, translated across all 8 languages.
Verified on the sweep: the hint centers in the gap and reads subtly on both dark
and light themes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two wallets-modal changes:
- The wallet list now always renders at its max height (kMaxVisibleRows) instead
of shrinking to the wallet count, so the modal is a consistent size whether you
have one wallet or many — fewer rows leave empty space, more than 7 scroll.
- Add a "Scanned folders" manager: each user-added scan folder is listed with a
control to stop scanning it (removeExtraFolder + save + re-scan so its wallets
drop out). Paths front-elide to keep the identifying leaf visible, with a
full-path tooltip. The card-height math reserves the manager's rows so nothing
clips. Two new i18n keys (wallets_scanned_folders / wallets_remove_folder),
translated across all 8 languages; CJK subset rebuilt.
Verified on the sweep at 1.0 and 1.5x DPI: max-height list on both the few- and
many-wallet surfaces, the folder manager renders and stays clear of the footer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the "Import" action — which copied an out-of-datadir wallet into the
datadir and cluttered the list with hard-to-tell-apart duplicates — with an
in-place "Open" that links the real file into the datadir under a stable
per-target name (wallet-ip-<FNV8>.dat) and switches to it. The daemon only
loads a bare filename from its own datadir, so a link is the minimal bridge:
hard link first (no privileges, same volume — covers non-admin Windows),
symlink fallback (cross-volume), then an error. Never a copy (that would fork
the wallet) and never a delete of a real file.
Also:
- show each external wallet's originating sub-directory (…/Backups/2021)
- add a per-row "open folder location" button
- widen the modal (780 → 860) for the extra button
- guard the daemon against a dangling link (external file moved / USB
unplugged) by falling back to wallet.dat rather than creating an empty one
Per-target link names (not one shared name) make switching between two
external wallets a real -wallet switch, so the rescan/cache index tracks each
correctly. Drops the now-dead wallets_import* i18n keys and back-fills the new
open/folder strings across all 8 languages + rebuilds the CJK subset.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the sort dropdown with a material::SegmentedControl — Created / Addresses
/ Txs / Size — matching the Receive tab's address-type toggle, with the asc/desc
arrow beside it. Shorten the four sort labels to fit the segments (updated across
all eight languages + the back-fill script). The direction arrow is positioned
explicitly on the segment's baseline (the control is draw-list based and doesn't
advance the layout cursor).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Back-fill the 8 new keys (created label + the four sort options and asc/desc
tooltips) into all eight languages via add_missing_translations.py (+64 keys,
additive), and rebuild res/fonts/NotoSansCJK-Subset.ttf for the new glyphs (升 昇 렬).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Back-fill the 10 new Wallets-dialog keys (metadata "keys"/"txs" columns and the
Encrypted/Seed/Legacy/Unknown badge labels + tooltips) into all eight languages
via scripts/add_missing_translations.py (additive; +80 keys, no reformatting), and
rebuild res/fonts/NotoSansCJK-Subset.ttf so the new zh/ja/ko glyphs (判 種 類 …)
render instead of tofu.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each scripts/gen_<lang>.py held a hardcoded `translations` dict that it
wrote over res/lang/<lang>.json wholesale. Those dicts had drifted ~285
keys behind the committed JSONs (keys were added directly to the JSONs
over time), so running any generator silently truncated a language file —
a live regression trap. Nothing invokes them (not build.sh, CMake, or CI),
and the JSONs are now the maintained source of truth, so the generators are
pure duplication plus a footgun.
Retire all eight. The additive, non-destructive scripts/add_missing_
translations.py remains the i18n helper, and CLAUDE.md now documents the
"edit the JSONs directly and additively" workflow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vendor the two local Rust crates that build the lite backend artifact into
third_party/silentdragonxlite/ (the qtlib C-ABI wrapper + the silentdragonxlitelib
core, with proto/res and all the lite-send fixes), and point
build-lite-backend-artifact.sh's default --backend-dir there, so the lite wallet
builds without the upstream SilentDragonXLite repo.
External build inputs are now only the Rust toolchain + git.dragonx.is + crates.io:
- the 6 librustzcash git deps point at the git.dragonx.is/DragonX/librustzcash
mirror (pinned rev acff1444), not git.hush.is;
- the Sapling params are gitignored (not committed, no Git LFS) - the build fetches
them from the git.dragonx.is/DragonX/zcash-params 'sapling-v1' release and verifies
their SHA-256 before rust-embed bakes them in (ensure_sapling_params).
For fully offline builds, cargo vendor into lib/vendor/ and add a vendored-sources
redirect (vendor/ is gitignored; the script symlinks it into the prepared dir).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tools/lite_send_smoke drives the real SDXL backend's exact GUI send path
(newaddr/status/list/tree/send/rescan) for diagnosing shielded sends.
scripts/gen-lite-checkpoints.sh generates verified mainnet checkpoints from a
synced dragonxd (getblockhash + getblockmerkletree), self-checking against a
known checkpoint before emitting.
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>
Rewrite scripts/sign-xmrig-release.sh to use OpenSSL (>= 1.1.1) instead of PyNaCl, so signing
needs no Python deps. OpenSSL's ed25519 is PureEdDSA (RFC 8032) — interop-verified against the
wallet's libsodium crypto_sign_verify_detached (script-produced .sig -> VERIFY-OK; tamper ->
VERIFY-FAIL). keygen/pubkey/sign subcommands; emits base64 raw-64-byte signatures as <file>.sig.
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>
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>
- docs/lite-wallet-implementation-plan-v2-2026-06-04.md: vertical-slice plan that
supersedes the v1 plan (now banner-marked); carries over the inherited artifact/
signing/phase-2 design docs for reference.
- scripts/check-source-hygiene.sh: pre-commit/CI guard rejecting >80-char filenames
and chained churn-token names, to stop the deleted "_plan"/"_batch" scaffolding
from regrowing.
- CLAUDE.md: repository guidance for future sessions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 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
Instead of blocking the entire UI with "Activating best chain..." until
the daemon finishes warmup, treat warmup responses as a successful
connection. The wallet now:
- Sets connected=true + warming_up=true when daemon returns RPC -28
- Shows warmup status with block progress in the loading overlay
- Polls getinfo every few seconds to detect warmup completion
- Allows Console, Peers, Settings tabs during warmup
- Shows orange status indicator with warmup message in status bar
- Skips balance/tx/address refresh until warmup completes
- Triggers full data refresh once daemon is ready
Also: fix curl handle/header leak on reconnect, fill in empty
externalDetected error branch, bump version to v1.2.0 in build scripts.
- Set CMAKE_OSX_DEPLOYMENT_TARGET and CMAKE_OSX_ARCHITECTURES before
project() so they propagate to all FetchContent dependencies (SDL3, etc.)
- build.sh: native mac release builds universal binary, detects and
rebuilds single-arch libsodium, verifies with lipo, exports
MACOSX_DEPLOYMENT_TARGET; dev build uses correct build/mac directory
- fetch-libsodium.sh: build arm64 and x86_64 separately then merge with
lipo on native macOS; fix sha256sum unavailable on macOS (use shasum)
- 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
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)