Skip to content

Qt: Dark theme, dashboard overhaul, security audit & performance#11

Open
kirklandsig wants to merge 23 commits intoRadiumCore:devfrom
kirklandsig:feature/dark-theme-dashboard
Open

Qt: Dark theme, dashboard overhaul, security audit & performance#11
kirklandsig wants to merge 23 commits intoRadiumCore:devfrom
kirklandsig:feature/dark-theme-dashboard

Conversation

@kirklandsig
Copy link

@kirklandsig kirklandsig commented Feb 26, 2026

v1.1.0 — Dark Theme, Dashboard Overhaul, Security Audit & Performance

Complete UI modernization of the Validity wallet. Dark theme, redesigned dashboard, new features, comprehensive security audit, and startup performance fixes for wallets with 36K+ transactions.


Visual Overhaul

  • Dark theme matching validitytech.com aesthetic, with light theme option
  • Card-based dashboard with drag-and-drop reordering, 2-column grid layout
  • Sidebar navigation replacing the old toolbar
  • Staking chart — 30-day daily staking history visualization
  • Complete Validity branding — all "Bitcoin" references replaced across UI strings and form files
  • Recolored toolbar/menu icons for dark theme consistency
  • Splash screen with full developer chain (Bitcoin Core → Blackcoin → Blackcoin More → Radium Core → Validity)

New Features

  • Backup Wizard — guided wallet backup flow
  • Network Peer Map — world map visualization of connected peers with IPv6 support
  • System tray enhancements — improved notifications and tray icon behavior
  • Theme switching — light/dark toggle in Options

Security Audit Fixes

  • CExtKey buffer overflow (key.h): Fixed potential overflow in extended key serialization
  • UnlockStaking fall-through (askpassphrasedialog.cpp): Fixed missing break in passphrase dialog switch
  • ODR violation: Removed direct #include "rpc/blockchain.cpp" that caused undefined behavior
  • Thread safety: Added cs_wallet lock for all mapWallet, IsStaking(), and GetStakeWeight() calls from the UI thread — fixes data races with the staking thread
  • Uninitialized variable: lastStaking bool used before initialization (undefined behavior on first BlockCountChanged)
  • Bounds check: Size validation before accessing staking report array indices 30-34
  • Integer overflow: Saturation guard for daily reward calculation (CAmount from double)
  • Passphrase handling: Replaced std::string temporary with QByteArray + immediate zeroing
  • Dashboard grid: Safe destructor cleanup (direct delete vs deleteLater)
  • Deferred timer: QPointer guard prevents use-after-free if widget destroyed during timer window

Performance — Startup Fix for Large Wallets

The wallet was freezing ("Not Responding") on startup for wallets with 36K+ staking transactions. The root cause: TransactionTableModel::refreshWallet() iterated every wallet transaction on the UI thread under LOCK2(cs_main, cs_wallet), blocking for 10+ seconds.

Fix (multi-stage):

  1. Background thread loading: Transaction model construction moved to std::thread so the UI never blocks on the 36K-transaction scan
  2. Chunked locking: Background thread processes transactions in 200-tx batches with 10ms yields between, giving the UI thread windows to acquire locks for balance/staking operations
  3. Non-blocking UI locks: BlockCountChanged and deferredStatsLoad use TRY_LOCK at function scope (recursive_mutex allows nested re-entry) and gracefully reschedule on contention instead of blocking
  4. Deferred model binding: Overview page recent-transactions list and transaction history view connect to the model after the dashboard renders, not during construction
  5. Deferred staking stats: First staking stats calculation deferred by 2 seconds so the dashboard renders instantly with balances

Startup flow:

  • Instant: Dashboard renders with balances
  • 0–4s: Background thread processes all wallet transactions in chunks
  • UI stays responsive throughout — click around, check peers, etc.
  • After loading: Transaction history and staking stats populate seamlessly

Performance — General

  • QSS stylesheet deduplication and caching
  • Staking chart data caching
  • Deferred recolorToolbarIcons() to after window visible (eliminates 70K+ pixel iteration blocking splash)
  • Cached original icons to prevent quality degradation on theme switches

Bug Fixes

  • Peer map IPv4-mapped IPv6: Fixed [::ffff:x.x.x.x] addresses placed at (0,0), overlapping center node
  • Pure IPv6 peers: Now spread visually on the map instead of clustering
  • Splitter grid layout: Fixed card sizing issues in dashboard

Files Changed (21 files)

overviewpage.cpp/.h, walletview.cpp/.h, transactiontablemodel.cpp/.h, bitcoingui.cpp/.h, askpassphrasedialog.cpp, carddragdrop.cpp, geoip.cpp, rpcconsole.cpp, splashscreen.cpp, key.h, optionsdialog.cpp, networkstyle.cpp, platformstyle.cpp, thememanager.cpp, backupwizard.cpp, peermapwidget.cpp

Build

Cross-compiled for Windows (x86_64-w64-mingw32) on Ubuntu/WSL2. No consensus or blockchain changes — UI only.

Test Plan

  • Launch wallet with synced 36K+ tx chain — verify dashboard renders instantly, no freeze
  • Verify dark cosmic theme loads matching validitytech.com
  • Verify sidebar navigation works (Overview, Send, Receive, Transactions)
  • Open Options > Display — verify theme picker switches Dark/Light
  • Check splash screen shows branded design with full dev chain
  • Hover over tray icon — verify staking status tooltip
  • Open peer map — verify IPv6 peers display correctly
  • Run Backup Wizard from File menu
  • Encrypt/decrypt/unlock wallet — verify passphrase dialog works
  • Check all dialogs for color consistency (no white backgrounds, no black text)
  • Wait for transaction history to populate after startup (~4s)

Full diff: main...kirklandsig:Validity:feature/dark-theme-dashboard
Release binary: https://github.com/kirklandsig/Validity/releases/tag/v1.1.0-security-audit

🤖 Generated with Claude Code

JJ12880 and others added 3 commits September 23, 2025 10:50
Adds a complete theming system with dark/light mode support and redesigns
the overview page with a modern card-based layout plus a 30-day staking
rewards bar chart.

NO CONSENSUS CHANGES - purely UI/client-side modifications.

Theme System:
- New ThemeManager class loads QSS stylesheets at runtime
- Dark theme (default) branded with Validity green (#43b581)
- Light theme preserves familiar classic look
- Theme preference persisted via QSettings across sessions
- Comprehensive dark.qss covering all Qt widget types (350+ lines)

Dashboard Redesign:
- Overview page restructured into 4 distinct cards:
  - Balance Card (top-left): available, pending, immature, stake, total
  - Staking Chart Card (top-right): 30-day bar chart + summary stats
  - Network Card (bottom-left): supply, staking %, weight, generation
  - Recent Transactions Card (bottom-right): last 5 transactions
- Cards use rounded corners and subtle borders for visual separation

Staking Rewards Chart:
- New StakingChartWidget (custom QPainter QWidget)
- Displays 30 daily bars showing staking rewards over time
- Validity green bars with rounded top corners
- Hover tooltips showing exact date and reward amount
- Y-axis with auto-scaled labels, X-axis with date labels
- Zero new backend work: reuses existing PrepareRangeForStakeReport() data

Code Quality Fixes:
- Renamed local round() to roundTo2() to avoid std::round conflict
- Fixed malformed iconset XML in overviewpage.ui
- Removed using namespace std at file scope
- Added Qt 5/6 compatibility for QDateTime::fromTime_t deprecation
- Removed hardcoded inline progress bar stylesheets (theme handles them)
- Fixed progress bar maximum value mismatches between .ui and .cpp

New files:
- src/qt/thememanager.h/.cpp
- src/qt/stakingchartwidget.h/.cpp
- src/qt/res/themes/dark.qss
- src/qt/res/themes/light.qss
- docs/plans/2026-02-25-dark-theme-dashboard-design.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major visual overhaul to match validitytech.com aesthetic:
- Rewrite dark.qss with cosmic gradient palette, pill-shaped buttons,
  thin scrollbars, and modern typography
- Convert horizontal toolbar to vertical sidebar navigation
- Redesign splash screen with dark cosmic gradient and branded text
- Add theme picker (Dark/Light) to Options dialog Display tab
- Set QPalette alongside QSS for PlatformStyle compatibility
- Update all hardcoded colors across 10+ files for theme consistency
- Fix inline stylesheets in .ui forms (modal overlay, overview, send)
- Update guiconstants.h color constants for dark theme
- Enhance system tray tooltip with staking status details

Security fixes from full codebase audit:
- Fix CExtKey::Unserialize buffer overflow (missing length validation)
- Fix UnlockStaking case fall-through in passphrase dialog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kirklandsig
Copy link
Author

kirklandsig commented Feb 26, 2026

image

@kirklandsig kirklandsig reopened this Feb 26, 2026
kirklandsig and others added 20 commits February 26, 2026 09:31
- Staking summary: tighter spacing with pipe separators between stat pairs
- Layout swap: Transactions full-width middle row, Network compact bottom
- NUM_ITEMS 5->7 for expanded transaction list
- Removed 860 lines of hardcoded palette XML from sendcoinsentry.ui
- Extended dark.qss (+275 lines): payment requests, coin control, RPC console,
  sign/verify, calendar, scroll areas, date pickers, dialog polish
- Security: division-by-zero guards in overviewpage.cpp (3 locations)
- Security: pwalletMain null check before staking status
- Fee warning color #aa0000 -> #e05555 for dark theme readability

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backup Wizard:
- New guided multi-step backup dialog (QDialog + QStackedWidget)
- 4 pages: Intro, Choose Location, Progress, Complete
- Builds on existing walletModel->backupWallet() - zero backend changes
- Accessible via File > Backup Wizard menu item
- New files: backupwizard.h/.cpp

Network Peer Map:
- Custom QPainter world map widget showing connected peers
- Simplified continent outlines with peer dots color-coded by latency
- Green (<100ms), yellow (<300ms), red (>300ms) ping indicators
- Hover tooltips showing peer IP, version, ping, bytes in/out
- Lightweight IP geolocation using first-octet heuristic mapping
- Connection lines from center node to each peer
- Inbound peer indicator (white ring)
- Added as new "Peer Map" tab in Debug/RPC Console
- New files: peermapwidget.h/.cpp, geoip.h/.cpp

Build system updated: Makefile.qt.include, dark.qss styling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Expanded tray icon menu with Overview, Transactions, Backup Wizard,
  and wallet lock/unlock quick actions
- Enhanced incoming transaction notifications to distinguish staking
  rewards with dedicated "Staking Reward!" title and reward-focused message
- Richer tray tooltip showing staking weight, network weight, and
  connection count alongside reward estimate
- Fixed branding: "Bitcoin network" -> "Validity network" in connection
  tooltip, default message title now uses PACKAGE_NAME
- Shortened staking status messages for cleaner display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added 526 lines to dark.qss covering send/receive forms, fee section,
  coin control stats, address book, transaction history, coin control
  dialog, options dialog, and global focus/disabled/tooltip states
- Fee section: radio button highlight, slider styling, label hierarchy
- Send page: icon button hover effects, validation states, add/clear
  button styling, monospace coin control stats
- Receive page: form label hierarchy, request table hover effects,
  show/remove button styling, QR dialog URI display
- Address book: new/copy/delete/export button styling with hover states
- Transaction view: filter bar inputs, table hover/select effects,
  context menu styling, date range widget
- Coin control: tree widget alternating rows, checkbox styling,
  select all button, item hover/select effects
- Global: disabled states, focus indicators, tooltip styling
- Fixed "Invalid Bitcoin address" -> "Invalid Validity address"
- Fixed Windows startup shortcut names Bitcoin -> Validity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three comprehensive reports based on JJ's request to explore
upgrading Validity to latest BlackCoin or Bitcoin Core:

1. Bitcoin Core 30.0 port feasibility (12-24 months, extreme risk)
2. BlackCoin More 26.x rebase feasibility (RECOMMENDED, 1-3 months)
3. PoS consensus code inventory (~1,670 lines across 12 files)

Key finding: BlackCoin More 26.x is the clear best path — same PoS
kernel, JJ already contributes to it, proven upgrade path exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaced all remaining user-facing "Bitcoin" references with "Validity":
- Address book: "Bitcoin addresses" -> "Validity addresses" (send/receive)
- Send page: "Send coins to a Bitcoin address" -> Validity
- Sign/Verify: "Bitcoin addresses" -> "Validity addresses"
- Edit address: "not a valid Bitcoin address" -> Validity
- Address input placeholder: "Enter a Bitcoin address" -> Validity
- Payment server: IPC name "BitcoinQt" -> "ValidityQt"
- Payment server: "Bitcoin address" error -> Validity
- Fatal error: "Bitcoin can no longer continue" -> Validity
- Help action: removed redundant "Bitcoin" from command-line options text

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updated all user-facing tooltip and description text in .ui files:
- optionsdialog: UPnP tooltip, SOCKS5 proxy descriptions (3 strings)
- overviewpage: sync warning tooltips (2 strings)
- intro: data directory description
- receivecoinsdialog: payment request message notes (2 strings)
- sendcoinsentry: address tooltip, URI reference, network note (3 strings)
- signverifymessagedialog: all sign/verify address references (4 strings)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Complete rewrite of light.qss from 51 to 700+ lines matching dark theme
  coverage across all pages (cards, sidebar, tabs, tables, dialogs, etc.)
- New CardDragDropManager (carddragdrop.h/.cpp) for dashboard card reordering
  via event filter, with drop indicator and QSettings persistence
- Integrate drag-drop into OverviewPage for transactionsCard and networkCard
- Update HANDOFF.md to reflect all-phases-complete status

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- All 4 dashboard cards now draggable (balance, staking, transactions, network)
  via flattened VBoxLayout instead of nested HBoxLayout
- Cards are resizable by dragging bottom edge (persisted to QSettings)
- Fix dark theme sidebar icons: recolorToolbarIcons() colorizes toolbar
  button icons using QPalette::WindowText, auto-updates on theme change
- Include pre-built validity-qt.exe binary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace flat 4-card vertical stack with QSplitter grid:
  top row (balance | staking chart side-by-side), transactions, network
- All splitter sections resizable with native drag handles
- Splitter sizes persisted to QSettings across restarts
- Fix ALL action icons (menus + toolbar) by colorizing with
  QPalette::WindowText - auto-updates on theme change
- Add QSplitter handle styling to both dark and light themes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove ~15 duplicate/redundant QSS selectors in dark.qss and light.qss
  that caused double widget-tree walks during style resolution
- Simplify ancestor-prefixed selectors (QDialog#X QLabel#Y → QLabel#Y)
  where the ID alone is unique, avoiding unnecessary tree traversal
- Add QPixmap paint cache to StakingChartWidget — only rebuilds when
  data, widget size, or hover state changes; paintEvent just blits cache
- Enable uniform item sizes on transaction list for faster scrolling
- Merge conflicting CoinControlDialog QTreeWidget blocks into single
  class-based selector with combined properties

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The staking stats, supply, and network weight computations scan the
entire wallet transaction map and UTXO set while holding cs_main,
blocking the UI for seconds on fully synced wallets. This change:

- Defers the first heavy computation by 2 seconds via QTimer so the
  dashboard cards, chart, and transaction list render immediately
- Caches wallet tx count — skips GetsStakeSubTotal rescan if no new
  transactions have arrived since the last computation
- Caches UTXO supply by block height — avoids redundant GetSupply()
  calls when the chain tip hasn't changed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace fixed QSplitter layout with DashboardGridManager — a full
drag-and-drop system supporting 2-column card arrangements:

- Cards can be dragged between left/right columns and reordered
  within a column (grab any non-interactive area to drag)
- Floating thumbnail preview follows the cursor during drag
- Green indicator bar shows the exact drop position
- Both column widths and card heights are resizable via splitter handles
- Layout is persisted to QSettings (card positions + splitter sizes)
- Default layout: balance, staking, network on left; transactions on right
- Escape key cancels an in-progress drag
- Interactive widgets (buttons, inputs, scroll areas) are excluded
  from drag initiation so normal card interactions still work

Also adds checkpoint validation to the stats caching:
- Network weight cache invalidates if drift exceeds 20%
- Staking report recalculates when staking status changes
- Copyright headers updated to include all contributors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Security fixes from comprehensive triple-agent audit:
- CRITICAL: Remove #include "rpc/blockchain.cpp" (ODR violation,
  namespace pollution); use proper extern declarations and headers
- CRITICAL: Add cs_wallet lock for mapWallet.size(), IsStaking(),
  and GetStakeWeight() calls from UI thread (data race)
- CRITICAL: Initialize lastStaking bool (was undefined behavior)
- CRITICAL: Add bounds check for aRange[30-34] staking report access
- Fix integer overflow in daily reward calculation (saturation guard)
- Remove roundTo2() from CAmount contexts (was no-op, confusing)
- Remove using namespace boost (namespace pollution)

Performance fix:
- Defer recolorToolbarIcons() to after window is visible via
  QTimer::singleShot(0) — was blocking splash-to-dashboard
- Cache original icons to prevent degradation on theme switches

Additional security hardening:
- Passphrase: use QByteArray with immediate zeroing instead of
  leaking through std::string temporary on ordinary heap
- DashboardGridManager: use delete (not deleteLater) in destructor
  to prevent memory leak on application shutdown
- QTimer::singleShot: use QPointer guard for deferred stats load

Peer map bug fix:
- GeoIP: handle IPv4-mapped IPv6 addresses ([::ffff:x.x.x.x]:port)
  instead of returning (0,0) which overlaps center node
- Pure IPv6 peers now spread visually using address hash
- Force initial updatePeerMap() call in setClientModel()

Splash screen:
- Show full copyright chain (Bitcoin Core, Blackcoin, Blackcoin More,
  Radium Core, Validity) matching the loading screen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only create OverviewPage and SendCoinsDialog at startup. Defer
TransactionView (heaviest — loads full tx table model),
ReceiveCoinsDialog, and AddressBookPage until first navigation.
Eliminates ~60% of widget construction from the startup path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace broken lazy page construction (caused "Not Responding" freeze)
with deferred model loading: all widgets created eagerly but the heavy
transactionView->setModel() is deferred via QTimer::singleShot(0)
so the dashboard renders in the first event loop pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The real bottleneck was getTransactionTableModel() being called in
OverviewPage::setWalletModel() and WalletView::setWalletModel(),
triggering refreshWallet() which iterates ALL wallet transactions
with crypto ops (IsMine, ExtractDestination) under LOCK2.

Fix: defer transaction filter setup (500ms) and transaction view
model + signal connection (750ms) so dashboard renders with balances
first, transactions populate after.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: refreshWallet() iterates ALL 36,104 wallet transactions
calling decomposeTransaction() (crypto ops) on each, blocking the
UI thread for 10-30+ seconds. No amount of deferring helps — the
work itself must move off the UI thread.

Fix: TransactionTableModel now spawns a std::thread to load
wallet transactions in the background. The UI renders immediately
with an empty transaction list, and when the background load
completes, onBackgroundRefreshComplete() swaps in the data via
beginResetModel()/endResetModel(). Incremental updates and
confirmation updates are skipped during background loading.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The background thread was holding LOCK2(cs_main, cs_wallet) for the
entire 36K transaction scan, starving the UI thread. Also, TRY_LOCK
was scoped incorrectly (released before blocking LOCK re-acquired).

Fix: background thread processes in 200-tx batches with 10ms yields.
UI functions use TRY_LOCK at function scope (recursive_mutex allows
nested LOCK calls) and reschedule on contention instead of blocking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kirklandsig kirklandsig changed the title Qt: Dark theme + modern dashboard with staking chart Qt: Dark theme, dashboard overhaul, security audit & performance Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants